{
  "openapi": "3.1.0",
  "info": {
    "title": "Warmlane Public API",
    "version": "1.0.0",
    "description": "Server-to-server API for pushing leads in, launching campaigns, reading reply threads, and managing suppressions.  Stable forever at /v1/ - additive-only changes; breaks go to /v2/.\n\nKeys are minted in Settings → API Keys (CLIENT_ADMIN only).  Plaintext is shown once; we store the SHA-256 hash.",
    "contact": {
      "name": "Warmlane Support",
      "email": "support@warmlane.io"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://api.warmlane.io",
      "description": "Production"
    },
    {
      "url": "http://localhost:3002",
      "description": "Local dev"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    },
    {
      "BearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "diagnostics",
      "description": "Smoke endpoints - no business data."
    },
    {
      "name": "subscribers",
      "description": "Push leads in / list / remove."
    },
    {
      "name": "campaigns",
      "description": "Launch a campaign + read its funnel."
    },
    {
      "name": "suppressions",
      "description": "Manage opt-outs."
    },
    {
      "name": "conversations",
      "description": "Read reply threads + push manual replies."
    },
    {
      "name": "webhooks",
      "description": "Outbound webhook subscriptions (Stripe-style signed delivery)."
    }
  ],
  "paths": {
    "/v1/ping": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Smoke test (no auth)",
        "description": "Returns 200 with the request id round-tripped in the body.  Use this to verify reachability before exercising any authed endpoint.",
        "operationId": "ping",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PingResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/whoami": {
      "get": {
        "tags": [
          "diagnostics"
        ],
        "summary": "Echo the authenticated caller",
        "description": "Stripe-style /account: confirms which key authenticated and which scopes it carries.  Useful before exercising real endpoints.",
        "operationId": "whoami",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoamiResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/subscribers": {
      "post": {
        "tags": [
          "subscribers"
        ],
        "summary": "Push a lead into a list (upsert on email)",
        "description": "Idempotent on (clientId, email).  Pass `Idempotency-Key` header for 24h replay across retries.  Cross-tenant listId returns 404.",
        "operationId": "createSubscriber",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriberCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing subscriber updated (upsert)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberResponse"
                }
              }
            }
          },
          "201": {
            "description": "New subscriber created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "subscribers"
        ],
        "summary": "List subscribers in a list (cursor paginated)",
        "operationId": "listSubscribers",
        "parameters": [
          {
            "$ref": "#/components/parameters/ListIdRequired"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tag",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Substring match on email / name / phone"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/subscribers/{email}": {
      "delete": {
        "tags": [
          "subscribers"
        ],
        "summary": "Remove a subscriber from a list",
        "description": "Does NOT add to suppression list - use POST /v1/suppressions for that.",
        "operationId": "deleteSubscriber",
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "$ref": "#/components/parameters/ListIdRequired"
          }
        ],
        "responses": {
          "200": {
            "description": "Deleted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriberDeleteResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/campaigns/{id}/launch": {
      "post": {
        "tags": [
          "campaigns"
        ],
        "summary": "Launch a campaign (or schedule it)",
        "description": "Empty body → transition to Sending immediately.  Body `{scheduledAt: ISO8601}` → transition to Scheduled.  Same gates the web UI uses: tenant verification + non-empty physical address (CAN-SPAM).  Already-sending = idempotent no-op.  Sent (terminal) = 409.",
        "operationId": "launchCampaign",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CampaignLaunchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Launched (or already in target state)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignLaunchResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/campaigns/{id}/stats": {
      "get": {
        "tags": [
          "campaigns"
        ],
        "summary": "Real-time funnel stats (ETag-cacheable)",
        "description": "Returns Cache-Control: private, max-age=5 + a strong ETag.  Polling clients should send If-None-Match for 304 short-circuit.",
        "operationId": "campaignStats",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string"
                }
              },
              "Cache-Control": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignStatsResponse"
                }
              }
            }
          },
          "304": {
            "description": "Not Modified - ETag matched If-None-Match"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/suppressions": {
      "post": {
        "tags": [
          "suppressions"
        ],
        "summary": "Suppress an email",
        "description": "201 on first suppression, 200 if already suppressed.  Reasons restricted to {unsubscribe, manual} on this surface; bounce/complaint are reserved for internal webhook handlers.",
        "operationId": "createSuppression",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SuppressionCreateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already suppressed (reason updated)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppressionResponse"
                }
              }
            }
          },
          "201": {
            "description": "Newly suppressed",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppressionResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "suppressions"
        ],
        "summary": "List suppressions (cursor paginated)",
        "operationId": "listSuppressions",
        "parameters": [
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "name": "reason",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "unsubscribe",
                "manual"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SuppressionPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/conversations/{id}": {
      "get": {
        "tags": [
          "conversations"
        ],
        "summary": "Read a reply thread + meeting state",
        "operationId": "getConversation",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConversationEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/webhook_endpoints": {
      "post": {
        "tags": [
          "webhooks"
        ],
        "summary": "Register a webhook endpoint",
        "description": "Returns the signing secret ONCE.  Verify the X-Warmlane-Signature header using HMAC-SHA256 over `${t}.${rawBody}` with this secret.  Replay window: reject events where |now - t| > 5min.",
        "operationId": "createWebhookEndpoint",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointCreateRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registered (secret returned ONCE)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointCreateResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "get": {
        "tags": [
          "webhooks"
        ],
        "summary": "List registered endpoints (without secrets)",
        "operationId": "listWebhookEndpoints",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointPage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/webhook_endpoints/{id}": {
      "delete": {
        "tags": [
          "webhooks"
        ],
        "summary": "Revoke an endpoint - future deliveries stop",
        "operationId": "revokeWebhookEndpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Revoked",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointRevokeResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/webhook_endpoints/{id}/test": {
      "post": {
        "tags": [
          "webhooks"
        ],
        "summary": "Fire a synthetic test event at the registered URL",
        "description": "Dispatches a `webhook.test` event (NOT one of the 6 production events) using the real signing path.  Use it to verify your handler before going live.",
        "operationId": "testWebhookEndpoint",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Test delivery attempted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTestResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/conversations/{id}/send": {
      "post": {
        "tags": [
          "conversations"
        ],
        "summary": "Push a manual reply onto the thread",
        "description": "Whether the message actually transmits depends on the platform's REPLY_MODE (default 'log').  Response includes transmissionMode so the integrator sees what happened.",
        "operationId": "sendConversationMessage",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKey"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MessageSendRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Message appended",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MessageSendResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/ValidationFailed"
          },
          "401": {
            "$ref": "#/components/responses/InvalidApiKey"
          },
          "403": {
            "$ref": "#/components/responses/ScopeOrPlanError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Your client API key (qm_live_…). Mint it in Settings → API Keys. This is the ONLY accepted credential - there is no username/password or OAuth on /v1/."
      },
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "qm_live_<32 chars>",
        "description": "The SAME client API key, passed as 'Authorization: Bearer qm_live_…' instead of the X-API-Key header. Not a JWT - /v1/ only accepts API keys."
      }
    },
    "parameters": {
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "description": "8–128 chars, [A-Za-z0-9._-].  Replays within 24h return the cached response verbatim + Idempotency-Replay: true header.",
        "schema": {
          "type": "string",
          "minLength": 8,
          "maxLength": 128,
          "pattern": "^[A-Za-z0-9._-]+$"
        }
      },
      "ListIdRequired": {
        "name": "listId",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string"
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "description": "Opaque pagination cursor returned in the previous response's nextCursor.",
        "schema": {
          "type": "string"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      }
    },
    "responses": {
      "InvalidApiKey": {
        "description": "Missing / malformed / revoked / expired key",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "ScopeOrPlanError": {
        "description": "Forbidden - key lacks scope, plan disables API, etc.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "ValidationFailed": {
        "description": "Request body invalid",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found (cross-tenant lookups also 404)",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "Conflict": {
        "description": "State conflict",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds"
          },
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorEnvelope"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "required": [
          "error",
          "requestId"
        ],
        "additionalProperties": false,
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": false,
            "properties": {
              "code": {
                "type": "string",
                "description": "snake_case machine-readable code"
              },
              "message": {
                "type": "string"
              },
              "details": {
                "type": [
                  "object",
                  "null"
                ]
              }
            }
          },
          "requestId": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PingResponse": {
        "type": "object",
        "required": [
          "ok",
          "version",
          "timestamp",
          "requestId"
        ],
        "additionalProperties": false,
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "version": {
            "type": "string",
            "enum": [
              "v1"
            ]
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "requestId": {
            "type": "string"
          }
        }
      },
      "WhoamiResponse": {
        "type": "object",
        "required": [
          "clientId",
          "keyId",
          "scopes",
          "planId",
          "requestId"
        ],
        "additionalProperties": false,
        "properties": {
          "clientId": {
            "type": "string"
          },
          "keyId": {
            "type": "string"
          },
          "keyPrefix": {
            "type": [
              "string",
              "null"
            ]
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "write",
                "manage"
              ]
            }
          },
          "planId": {
            "type": "string"
          },
          "requestId": {
            "type": "string"
          }
        }
      },
      "ConsentRecord": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "source": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "version": {
            "type": "string"
          }
        }
      },
      "Subscriber": {
        "type": "object",
        "required": [
          "listId",
          "email"
        ],
        "additionalProperties": false,
        "properties": {
          "listId": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string"
          },
          "addedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "consent": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/ConsentRecord"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "SubscriberCreateRequest": {
        "type": "object",
        "required": [
          "listId",
          "email"
        ],
        "additionalProperties": true,
        "properties": {
          "listId": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "consent": {
            "type": "object"
          }
        }
      },
      "SubscriberResponse": {
        "type": "object",
        "required": [
          "subscriber",
          "consentBackfilled"
        ],
        "additionalProperties": false,
        "properties": {
          "subscriber": {
            "$ref": "#/components/schemas/Subscriber"
          },
          "consentBackfilled": {
            "type": "boolean"
          }
        }
      },
      "SubscriberDeleteResponse": {
        "type": "object",
        "required": [
          "deleted",
          "listId",
          "email"
        ],
        "additionalProperties": false,
        "properties": {
          "deleted": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "listId": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          }
        }
      },
      "SubscriberPage": {
        "type": "object",
        "required": [
          "items",
          "hasMore",
          "nextCursor"
        ],
        "additionalProperties": false,
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscriber"
            }
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "CampaignLaunchRequest": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "scheduledAt": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 future timestamp.  Omit for immediate launch."
          }
        }
      },
      "Campaign": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "Draft",
              "Scheduled",
              "Sending",
              "Paused",
              "Sent"
            ]
          },
          "listId": {
            "type": [
              "string",
              "null"
            ]
          },
          "scheduledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sendingStartedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "sentAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "CampaignLaunchResponse": {
        "type": "object",
        "required": [
          "campaign",
          "launched",
          "alreadyInState"
        ],
        "additionalProperties": false,
        "properties": {
          "campaign": {
            "$ref": "#/components/schemas/Campaign"
          },
          "launched": {
            "type": "boolean"
          },
          "alreadyInState": {
            "type": "boolean"
          }
        }
      },
      "CampaignStats": {
        "type": "object",
        "required": [
          "seeded",
          "sent",
          "opens",
          "clicks",
          "replies",
          "bookings",
          "unsubscribes"
        ],
        "additionalProperties": false,
        "properties": {
          "seeded": {
            "type": "integer",
            "minimum": 0
          },
          "sent": {
            "type": "integer",
            "minimum": 0
          },
          "opens": {
            "type": "integer",
            "minimum": 0
          },
          "clicks": {
            "type": "integer",
            "minimum": 0
          },
          "replies": {
            "type": "integer",
            "minimum": 0
          },
          "bookings": {
            "type": "integer",
            "minimum": 0
          },
          "unsubscribes": {
            "type": "integer",
            "minimum": 0
          },
          "partial": {
            "type": "boolean",
            "description": "true when the send partition exceeded the server scan cap - counts are a lower bound."
          }
        }
      },
      "CampaignStatsResponse": {
        "type": "object",
        "required": [
          "campaignId",
          "status",
          "updatedAt",
          "stats"
        ],
        "additionalProperties": false,
        "properties": {
          "campaignId": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "Draft",
              "Scheduled",
              "Sending",
              "Paused",
              "Sent"
            ]
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "stats": {
            "$ref": "#/components/schemas/CampaignStats"
          }
        }
      },
      "SuppressionCreateRequest": {
        "type": "object",
        "required": [
          "email"
        ],
        "additionalProperties": false,
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "reason": {
            "type": "string",
            "enum": [
              "unsubscribe",
              "manual"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "email"
            ],
            "description": "v1 only accepts 'email'.  'sms' and 'all' deferred to v2."
          }
        }
      },
      "Suppression": {
        "type": "object",
        "required": [
          "email",
          "reason",
          "channel"
        ],
        "additionalProperties": false,
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "reason": {
            "type": "string"
          },
          "channel": {
            "type": "string"
          },
          "suppressedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "SuppressionResponse": {
        "type": "object",
        "required": [
          "suppression",
          "alreadySuppressed"
        ],
        "additionalProperties": false,
        "properties": {
          "suppression": {
            "$ref": "#/components/schemas/Suppression"
          },
          "alreadySuppressed": {
            "type": "boolean"
          }
        }
      },
      "SuppressionPage": {
        "type": "object",
        "required": [
          "items",
          "hasMore",
          "nextCursor"
        ],
        "additionalProperties": false,
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Suppression"
            }
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "id",
          "direction",
          "channel",
          "body"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "channel": {
            "type": "string",
            "enum": [
              "EMAIL",
              "SMS",
              "WHATSAPP"
            ]
          },
          "body": {
            "type": "string"
          },
          "from": {
            "type": [
              "string",
              "null"
            ]
          },
          "to": {
            "type": [
              "string",
              "null"
            ]
          },
          "timestamp": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "mode": {
            "type": [
              "string",
              "null"
            ]
          },
          "via": {
            "type": [
              "string",
              "null"
            ]
          },
          "viaKeyId": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "Meeting": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": [
              "string",
              "null"
            ]
          },
          "date": {
            "type": [
              "string",
              "null"
            ]
          },
          "time": {
            "type": [
              "string",
              "null"
            ]
          },
          "dealValue": {
            "type": [
              "number",
              "null"
            ]
          },
          "notes": {
            "type": [
              "string",
              "null"
            ]
          },
          "statusUpdatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "Conversation": {
        "type": "object",
        "required": [
          "id",
          "status"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "leadName": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "phone": {
            "type": [
              "string",
              "null"
            ]
          },
          "channel": {
            "type": "string"
          },
          "dispatchChannel": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "campaignId": {
            "type": [
              "string",
              "null"
            ]
          },
          "currentStep": {
            "type": "integer",
            "minimum": 0
          },
          "lastAiAction": {
            "type": [
              "string",
              "null"
            ]
          },
          "meeting": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/Meeting"
              },
              {
                "type": "null"
              }
            ]
          },
          "messageCount": {
            "type": "integer",
            "minimum": 0
          },
          "messages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Message"
            }
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updatedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "ConversationEnvelope": {
        "type": "object",
        "required": [
          "conversation"
        ],
        "additionalProperties": false,
        "properties": {
          "conversation": {
            "$ref": "#/components/schemas/Conversation"
          }
        }
      },
      "MessageSendRequest": {
        "type": "object",
        "required": [
          "body"
        ],
        "additionalProperties": false,
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 16384
          },
          "channel": {
            "type": "string",
            "enum": [
              "EMAIL",
              "SMS",
              "WHATSAPP"
            ]
          },
          "scheduledAt": {
            "type": "string",
            "format": "date-time",
            "description": "Deferred to v2 - engine does not queue future-dated replies at v1."
          }
        }
      },
      "MessageSendResponse": {
        "type": "object",
        "required": [
          "message",
          "conversation",
          "transmissionMode"
        ],
        "additionalProperties": false,
        "properties": {
          "message": {
            "$ref": "#/components/schemas/Message"
          },
          "conversation": {
            "$ref": "#/components/schemas/Conversation"
          },
          "transmissionMode": {
            "type": "string",
            "enum": [
              "live",
              "log"
            ]
          }
        }
      },
      "WebhookEndpointCreateRequest": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "additionalProperties": false,
        "properties": {
          "url": {
            "type": "string",
            "description": "HTTPS only.  Private/loopback IPs rejected (SSRF defence)."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "subscriber.created",
                "subscriber.unsubscribed",
                "campaign.completed",
                "conversation.replied",
                "booking.created",
                "invoice.paid"
              ]
            }
          }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "status"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "revoked"
            ]
          },
          "secretPrefix": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastDeliveryStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointCreateResponse": {
        "type": "object",
        "required": [
          "id",
          "url",
          "events",
          "status",
          "secret"
        ],
        "additionalProperties": false,
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string"
          },
          "secret": {
            "type": "string",
            "description": "Plaintext signing secret.  SHOWN ONCE - store it; we keep only the hash."
          },
          "secretPrefix": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastDeliveryStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WebhookEndpointPage": {
        "type": "object",
        "required": [
          "items",
          "hasMore",
          "nextCursor"
        ],
        "additionalProperties": false,
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebhookEndpoint"
            }
          },
          "hasMore": {
            "type": "boolean"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "WebhookEndpointRevokeResponse": {
        "type": "object",
        "required": [
          "ok",
          "id",
          "status"
        ],
        "additionalProperties": false,
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              true
            ]
          },
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "revoked"
            ]
          },
          "secretPrefix": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "createdBy": {
            "type": [
              "string",
              "null"
            ]
          },
          "lastDeliveryAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "lastDeliveryStatus": {
            "type": [
              "string",
              "null"
            ]
          },
          "consecutiveFailures": {
            "type": "integer",
            "minimum": 0
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "WebhookTestResponse": {
        "type": "object",
        "required": [
          "endpointId",
          "deliveryId",
          "attempt",
          "ok"
        ],
        "additionalProperties": false,
        "properties": {
          "endpointId": {
            "type": "string"
          },
          "deliveryId": {
            "type": "string"
          },
          "attempt": {
            "type": "integer",
            "minimum": 1
          },
          "ok": {
            "type": "boolean"
          },
          "statusCode": {
            "type": [
              "integer",
              "null"
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "willRetry": {
            "type": "boolean"
          },
          "nextAttemptAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      }
    }
  }
}