{
  "openapi": "3.0.0",
  "info": {
    "title": "mailfloss API",
    "version": "1.0.0",
    "description": "mailfloss email verification API — version 1.\n\n## Authentication\nEvery request must include your API key as a Bearer token in the `Authorization` header:\n\n```\nAuthorization: Bearer YOUR_API_KEY\n```\n\nThis is the only accepted authentication scheme for v1. The legacy `?api_key=` query parameter is **not** accepted on v1 endpoints.\n\n## Conventions\nList endpoints return a `{ data, pagination }` envelope. Errors return a stable machine-readable `code` (branch on it, not the message). Timestamps are ISO 8601 UTC.",
    "contact": {
      "name": "mailfloss support",
      "url": "https://mailfloss.com",
      "email": "support@mailfloss.com"
    }
  },
  "servers": [
    {
      "url": "https://api.mailfloss.com",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "Verification",
      "description": "Single-email verification endpoints"
    },
    {
      "name": "Batch",
      "description": "Asynchronous batch verification jobs"
    },
    {
      "name": "Account",
      "description": "Account info, credits, API key management"
    },
    {
      "name": "Team",
      "description": "Organization members / seats"
    },
    {
      "name": "Reports",
      "description": "Verification usage analytics"
    },
    {
      "name": "Integrations",
      "description": "ESP connections — connect, list, manage, and configure email-platform integrations (incl. per-connection keyword rules)"
    },
    {
      "name": "Webhooks",
      "description": "Webhook subscriptions — event notifications POSTed to your endpoints"
    },
    {
      "name": "Privacy",
      "description": "Data privacy / erasure API"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKeyHeader": {
        "type": "http",
        "scheme": "bearer",
        "description": "Send your API key as a Bearer token: `Authorization: Bearer <token>`. This is the only accepted authentication scheme for the v1 API."
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable machine-readable error code. See docs/api/v1_contract.md for the frozen enum."
              },
              "message": {
                "type": "string",
                "description": "Human-readable error message. Do NOT pattern-match — use code instead."
              },
              "type": {
                "type": "string",
                "description": "Coarse category derived from HTTP status (authentication_error, billing_error, invalid_request_error, rate_limit_error, not_found_error, conflict_error, api_error)."
              },
              "request_id": {
                "type": "string",
                "nullable": true,
                "description": "Echoes the X-Request-Id response header."
              }
            },
            "required": [
              "code",
              "message",
              "type",
              "request_id"
            ]
          }
        },
        "required": [
          "error"
        ]
      }
    },
    "parameters": {}
  },
  "paths": {
    "/v1/verify": {
      "get": {
        "summary": "Verify a single email address",
        "description": "Performs email verification on a single email address and returns a deliverability verdict. Consumes one credit from the org account on success. Returns the raw engine verdict: batch results for the same address may differ, because batch verdicts have the org's floss-settings overrides applied — see the v1 contract (\"Live vs batch verdicts\").",
        "tags": [
          "Verification"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The email address to verify."
            },
            "required": false,
            "description": "The email address to verify.",
            "name": "email",
            "in": "query"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "string"
                }
              ],
              "description": "Optional per-request timeout, in seconds."
            },
            "required": false,
            "description": "Optional per-request timeout, in seconds.",
            "name": "timeout",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Verification verdict",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "type": "string"
                    },
                    "domain": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "passed",
                        "undeliverable",
                        "risky",
                        "unknown"
                      ],
                      "description": "Verification verdict. `passed` = safe to send; `undeliverable` = hard fail (no mailbox / invalid); `risky` = role, disposable, bounced, or complained (deliverable but flagged); `unknown` = could not be determined — includes accept-all/catch-all domains and transient SMTP failures. Source: server/api/checker.js. This is the raw engine verdict, with no org floss-settings overrides applied — batch-verify results for the same address may differ."
                    },
                    "reason": {
                      "type": "string"
                    },
                    "passed": {
                      "type": "boolean"
                    },
                    "role": {
                      "type": "boolean"
                    },
                    "disposable": {
                      "type": "boolean"
                    },
                    "free": {
                      "type": "boolean"
                    },
                    "relay": {
                      "type": "boolean",
                      "description": "True when the address belongs to a known email relay/forwarding/masking service (e.g. Apple Hide My Email, DuckDuckGo, Firefox Relay, SimpleLogin, addy.io). Annotation only — does not change the verdict."
                    },
                    "gibberish": {
                      "type": "boolean",
                      "description": "True when the local part looks random/gibberish (e.g. xk39fjqz). Conservative signup-fraud signal; annotation only — does not change the verdict."
                    },
                    "suggestion": {
                      "type": "string"
                    },
                    "meta": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "email",
                    "status",
                    "reason",
                    "passed"
                  ]
                },
                "example": {
                  "email": "jane@example.com",
                  "domain": "example.com",
                  "status": "passed",
                  "reason": "valid",
                  "passed": true,
                  "role": false,
                  "disposable": false,
                  "free": false,
                  "relay": false,
                  "gibberish": false,
                  "suggestion": "",
                  "meta": ""
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits to complete the request (billing_error). Future: may carry an x402 payment-instructions sibling.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "insufficient_credits",
                    "message": "Your account does not have enough credits to complete this request.",
                    "type": "billing_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/batch-verify": {
      "post": {
        "summary": "Submit a batch of emails for async verification",
        "description": "Queues a verification job for the supplied email list. Returns immediately with a job ID; results retrievable via /v1/batch-verify/{id}/results once the job completes.",
        "tags": [
          "Batch"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress)."
            },
            "required": false,
            "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress).",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emails": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "webhook_url": {
                    "type": "string",
                    "description": "Optional callback URL. When the batch job finishes, mailfloss sends a POST to this URL with the completed job (its `id` plus the verdict summary), so you can react without polling GET /v1/jobs/{id}. Omit it to poll instead. (The legacy `webhookUrl` spelling is still accepted but undocumented — prefer `webhook_url`.)"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id"
                  ]
                },
                "example": {
                  "id": "job_a1b2c3d4e5f6g7h8"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "402": {
            "description": "Insufficient credits to complete the request (billing_error). Future: may carry an x402 payment-instructions sibling.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "insufficient_credits",
                    "message": "Your account does not have enough credits to complete this request.",
                    "type": "billing_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reuse while an earlier request with the same key is still in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Idempotency-Key was previously used with a different request body (fingerprint mismatch).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_key_reused",
                    "message": "This Idempotency-Key was already used with a different request body.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/batch-verify/{id}/status": {
      "get": {
        "summary": "Get batch job status",
        "description": "Poll endpoint for batch job progress. Returns the job's verdict counters (`numImported`, `emailsProcessed`, `numPassed`, `numFailed`, …) plus a `status` string. Counters are absent until processing begins; there is no single percent-complete field — derive progress from `emailsProcessed / numImported`.",
        "tags": [
          "Batch"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Job status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Echo of the job id from the path."
                    },
                    "processed": {
                      "type": "boolean",
                      "description": "True once the job has finished processing."
                    },
                    "numImported": {
                      "type": "number",
                      "description": "Emails imported into the job."
                    },
                    "emailsProcessed": {
                      "type": "number",
                      "description": "Emails verified so far."
                    },
                    "numPassed": {
                      "type": "number",
                      "description": "Verified-good count."
                    },
                    "numFailed": {
                      "type": "number",
                      "description": "Everything not passed (undeliverable + risky + unknown)."
                    },
                    "numUndeliverable": {
                      "type": "number",
                      "description": "Verified-bad count (subset of numFailed)."
                    },
                    "numUnknown": {
                      "type": "number",
                      "description": "Indeterminate count (subset of numFailed)."
                    },
                    "numRisky": {
                      "type": "number",
                      "description": "Risky count (subset of numFailed)."
                    },
                    "status": {
                      "type": "string",
                      "description": "Job state. `starting` (internal importing) and `processing` (internal pending) are renamed; other internal Jobs.status values pass through unchanged (e.g. `completed`, `cancel`, `force_stop`). Not a closed enum."
                    }
                  },
                  "required": [
                    "id"
                  ]
                },
                "example": {
                  "id": "job_a1b2c3d4e5f6g7h8",
                  "processed": false,
                  "numImported": 1000,
                  "emailsProcessed": 650,
                  "numPassed": 540,
                  "numFailed": 110,
                  "numUndeliverable": 80,
                  "numUnknown": 10,
                  "numRisky": 20,
                  "status": "processing"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/batch-verify/{id}/results": {
      "get": {
        "summary": "Get batch job results (paginated)",
        "description": "Returns the verification results for a completed job, paginated via the per_page + next query parameters. Row verdicts are post-org-override: statuses the org's floss settings are configured to ignore are rewritten to passed/valid before storage, so they may differ from GET /v1/verify for the same address — see the v1 contract (\"Live vs batch verdicts\").",
        "tags": [
          "Batch"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "number"
                },
                {
                  "type": "string"
                }
              ],
              "description": "Results per page, capped at 1000."
            },
            "required": false,
            "description": "Results per page, capped at 1000.",
            "name": "per_page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Pagination cursor; omit on first call."
            },
            "required": false,
            "description": "Pagination cursor; omit on first call.",
            "name": "next",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "email": {
                            "type": "string"
                          },
                          "domain": {
                            "type": "string"
                          },
                          "processed": {
                            "type": "boolean"
                          },
                          "passed": {
                            "type": "boolean"
                          },
                          "reason": {
                            "type": "string",
                            "description": "Batch verdict reason (post org-override — may differ from live /verify)."
                          },
                          "status": {
                            "type": "string",
                            "description": "Batch verdict status (post org-override — may differ from live /verify)."
                          },
                          "completed_at": {
                            "type": "string",
                            "description": "Last-touch time of the row (the rawEmails `updated_at`; later integration actions move it forward)."
                          },
                          "index": {
                            "type": "number",
                            "description": "Submission order. Present on /results; omitted by the deprecated endpoint."
                          },
                          "suggestion": {
                            "type": "string"
                          },
                          "fixed": {
                            "type": "boolean"
                          },
                          "deleted": {
                            "type": "boolean"
                          },
                          "unsubscribed": {
                            "type": "boolean"
                          },
                          "updated": {
                            "type": "boolean",
                            "description": "Integration write-back (rawEmails `integrationUpdated`)."
                          },
                          "tagged": {
                            "type": "boolean",
                            "description": "Integration tag write-back (rawEmails `integrationTagged`)."
                          }
                        },
                        "required": [
                          "email"
                        ]
                      }
                    },
                    "next": {
                      "type": "string",
                      "description": "Opaque pagination cursor. Pass as `next` to fetch the following page. Absent on the last page."
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "email": "jane@example.com",
                      "domain": "example.com",
                      "processed": true,
                      "passed": true,
                      "reason": "valid",
                      "status": "passed",
                      "completed_at": "2026-06-19T10:18:22.000Z",
                      "index": 0
                    },
                    {
                      "email": "noreply@example.com",
                      "domain": "example.com",
                      "processed": true,
                      "passed": false,
                      "reason": "nonexistent",
                      "status": "undeliverable",
                      "completed_at": "2026-06-19T10:18:23.000Z",
                      "index": 1,
                      "suggestion": ""
                    }
                  ],
                  "next": "U2FsdGVkX1-8k3n0pQ7rWtZ2xY"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/batch-verify/{id}/cancel": {
      "post": {
        "summary": "Cancel a batch verification job",
        "description": "Cancels a pending or in-progress batch job. No-op if job has already completed.",
        "tags": [
          "Batch"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress)."
            },
            "required": false,
            "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress).",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "responses": {
          "200": {
            "description": "Cancel acknowledgement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success",
                        "error"
                      ],
                      "description": "`success` when the job was cancelled and unprocessed emails re-credited; `error` if the cancel write did not apply."
                    }
                  },
                  "required": [
                    "status"
                  ]
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reuse while an earlier request with the same key is still in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Idempotency-Key was previously used with a different request body (fingerprint mismatch).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_key_reused",
                    "message": "This Idempotency-Key was already used with a different request body.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/account/timeline": {
      "get": {
        "summary": "Account activity timeline",
        "description": "A compact, newest-first feed of what happened on the account inside a rolling window (`days`, default 30, max 90; `limit` events, default 60, max 200): verification jobs with their outcome AND why they started (`trigger` — manual / autofloss / instafloss / decay_protection / api / cron / staff / system — recorded at job start for jobs created after September 2026, inferred from the job's flags before that: see `trigger_explicit`), settings and connection changes with exact before/after where recorded, and account-state events (support-chat sessions, escalations, ESP connects/disconnects, plan / add-on / auto-refill changes, logins, holds). Built to answer \"was this job manual or scheduled?\" and \"why did my HubSpot run process this many emails?\" without guessing. Scoped to the key's account. Ids and internal admin paths only — never emails, IP addresses, user agents, staff identities, tokens or payment data; settings values under credential-looking keys read \"redacted\". Requires `read:jobs`.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Window length in days, integer in [1, 90]; default 30."
            },
            "required": false,
            "description": "Window length in days, integer in [1, 90]; default 30.",
            "name": "days",
            "in": "query"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Maximum events returned, integer in [1, 200]; default 60. `summary.truncated` reports overflow."
            },
            "required": false,
            "description": "Maximum events returned, integer in [1, 200]; default 60. `summary.truncated` reports overflow.",
            "name": "limit",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Account activity timeline",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "generated_at": {
                      "type": "string"
                    },
                    "window": {
                      "type": "object",
                      "properties": {
                        "since": {
                          "type": "string"
                        },
                        "until": {
                          "type": "string"
                        },
                        "days": {
                          "type": "integer"
                        }
                      },
                      "required": [
                        "since",
                        "until",
                        "days"
                      ]
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "jobs": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "jobs_by_trigger": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "integer",
                            "minimum": 0
                          }
                        },
                        "settings_changes": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "account_events": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "truncated": {
                          "type": "boolean",
                          "description": "true when more events fell in the window than `limit` allowed."
                        }
                      },
                      "required": [
                        "jobs",
                        "jobs_by_trigger",
                        "settings_changes",
                        "account_events",
                        "truncated"
                      ]
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "oneOf": [
                          {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "enum": [
                                  "job"
                                ]
                              },
                              "at": {
                                "type": "string",
                                "description": "ISO 8601 — when the job started."
                              },
                              "job": {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "description": "Job id — the same id GET /v1/jobs/{id} takes."
                                  },
                                  "status": {
                                    "type": "string",
                                    "enum": [
                                      "queued",
                                      "processing",
                                      "completed",
                                      "cancelled"
                                    ],
                                    "description": "Job lifecycle state. queued = accepted, not yet processing; processing = in flight; completed = finished; cancelled = stopped before completion (by the customer, or by the system reaping a stalled job)."
                                  },
                                  "source": {
                                    "type": "string",
                                    "enum": [
                                      "api",
                                      "csv",
                                      "zapier",
                                      "widget",
                                      "input",
                                      "integration"
                                    ],
                                    "description": "How the job was submitted. `api` = this REST API; `csv` = an in-app file upload; `input` = emails pasted into the in-app Copy & paste box; `widget` = the embeddable verification widget; `zapier` = the Zapier connector; `integration` = an ESP auto-cleanse (the ESP slug is in `source_integration`)."
                                  },
                                  "source_integration": {
                                    "type": "string",
                                    "nullable": true
                                  },
                                  "started_at": {
                                    "type": "string",
                                    "nullable": true
                                  },
                                  "finished_at": {
                                    "type": "string",
                                    "nullable": true
                                  },
                                  "results": {
                                    "type": "object",
                                    "nullable": true,
                                    "properties": {
                                      "total": {
                                        "type": "integer",
                                        "minimum": 0
                                      },
                                      "passed": {
                                        "type": "integer",
                                        "minimum": 0
                                      },
                                      "failed": {
                                        "type": "integer",
                                        "minimum": 0
                                      }
                                    },
                                    "required": [
                                      "total",
                                      "passed",
                                      "failed"
                                    ],
                                    "description": "Verdict headline — null until the job completes."
                                  }
                                },
                                "required": [
                                  "id",
                                  "status",
                                  "source",
                                  "source_integration",
                                  "started_at",
                                  "finished_at",
                                  "results"
                                ]
                              },
                              "trigger": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "manual",
                                  "autofloss",
                                  "instafloss",
                                  "decay_protection",
                                  "api",
                                  "cron",
                                  "staff",
                                  "system"
                                ],
                                "description": "What started the job. manual = a person in the dashboard; autofloss = the daily schedule; instafloss = real-time; decay_protection = the monthly re-verification; api = this REST API; cron / system = other server-initiated work; staff = mailfloss support acting on the account."
                              },
                              "trigger_explicit": {
                                "type": "boolean",
                                "description": "true when the trigger was RECORDED at job start (jobs created after MAI-938 shipped); false when it was inferred from the job's flags (older jobs) — and null trigger means it could not be inferred at all."
                              },
                              "source_action": {
                                "type": "string",
                                "nullable": true,
                                "description": "Recorded start action, e.g. dashboard.hubspot.import_all, cron.decay_protect_hubspot, api.import_since."
                              },
                              "actor": {
                                "type": "string",
                                "enum": [
                                  "customer",
                                  "staff",
                                  "api",
                                  "system",
                                  "cron",
                                  "unknown"
                                ],
                                "description": "Who acted. staff = mailfloss support (impersonation is never hidden); unknown = an older row that predates actor attribution."
                              },
                              "admin_path": {
                                "type": "string",
                                "description": "Path on the internal admin app for staff follow-up."
                              }
                            },
                            "required": [
                              "type",
                              "at",
                              "job",
                              "trigger",
                              "trigger_explicit",
                              "source_action",
                              "actor",
                              "admin_path"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "enum": [
                                  "settings_changed"
                                ]
                              },
                              "at": {
                                "type": "string"
                              },
                              "connection": {
                                "type": "object",
                                "properties": {
                                  "id": {
                                    "type": "string",
                                    "description": "Connection id — same as GET /v1/integrations/{type} connections[].id."
                                  },
                                  "type": {
                                    "type": "string",
                                    "nullable": true,
                                    "description": "Public integration slug, or null for a non-ESP connection."
                                  }
                                },
                                "required": [
                                  "id",
                                  "type"
                                ]
                              },
                              "actor": {
                                "type": "string",
                                "enum": [
                                  "customer",
                                  "staff",
                                  "api",
                                  "system",
                                  "cron",
                                  "unknown"
                                ],
                                "description": "Who acted. staff = mailfloss support (impersonation is never hidden); unknown = an older row that predates actor attribution."
                              },
                              "source": {
                                "type": "string",
                                "nullable": true,
                                "description": "Which writer recorded it (updateFlossSettings, toggleFlossSettings, api:v1:settings, …)."
                              },
                              "changes": {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "object",
                                  "properties": {
                                    "from": {
                                      "nullable": true
                                    },
                                    "to": {
                                      "nullable": true
                                    }
                                  }
                                },
                                "description": "Exact before/after per changed setting when the write recorded a diff. Values under a credential-looking key are replaced by \"redacted\"."
                              },
                              "changes_explicit": {
                                "type": "boolean",
                                "description": "false when only the written field names are known (older rows)."
                              },
                              "fields": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                },
                                "description": "Setting names touched by the write."
                              },
                              "admin_path": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "type",
                              "at",
                              "connection",
                              "actor",
                              "source",
                              "changes",
                              "changes_explicit",
                              "fields",
                              "admin_path"
                            ]
                          },
                          {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string",
                                "enum": [
                                  "account_event"
                                ]
                              },
                              "at": {
                                "type": "string"
                              },
                              "kind": {
                                "type": "string",
                                "description": "Event kind, e.g. support_session_opened, escalation_filed, esp_connected, plan_change, account_login, account_paused."
                              },
                              "actor": {
                                "type": "string",
                                "enum": [
                                  "customer",
                                  "staff",
                                  "api",
                                  "system",
                                  "cron",
                                  "unknown"
                                ],
                                "description": "Who acted. staff = mailfloss support (impersonation is never hidden); unknown = an older row that predates actor attribution."
                              },
                              "source": {
                                "type": "string",
                                "nullable": true
                              },
                              "before": {
                                "nullable": true,
                                "description": "Prior state for plan / add-on / auto-refill changes; null otherwise."
                              },
                              "after": {
                                "nullable": true
                              },
                              "meta": {
                                "type": "object",
                                "additionalProperties": {
                                  "nullable": true
                                },
                                "description": "A small whitelisted subset (e.g. login new_ip / new_user_agent booleans, the ESP name)."
                              }
                            },
                            "required": [
                              "type",
                              "at",
                              "kind",
                              "actor",
                              "source",
                              "meta"
                            ]
                          }
                        ]
                      },
                      "description": "Newest first."
                    }
                  },
                  "required": [
                    "generated_at",
                    "window",
                    "summary",
                    "events"
                  ]
                },
                "example": {
                  "generated_at": "2026-09-03T12:00:00.000Z",
                  "window": {
                    "since": "2026-08-04T12:00:00.000Z",
                    "until": "2026-09-03T12:00:00.000Z",
                    "days": 30
                  },
                  "summary": {
                    "jobs": 2,
                    "jobs_by_trigger": {
                      "decay_protection": 1,
                      "manual": 1
                    },
                    "settings_changes": 1,
                    "account_events": 1,
                    "truncated": false
                  },
                  "events": [
                    {
                      "type": "job",
                      "at": "2026-09-02T03:00:12.000Z",
                      "job": {
                        "id": "job_a1b2c3d4e5f6g7h8",
                        "status": "completed",
                        "source": "integration",
                        "source_integration": "hubspot",
                        "started_at": "2026-09-02T03:00:12.000Z",
                        "finished_at": "2026-09-02T05:41:30.000Z",
                        "results": {
                          "total": 116911,
                          "passed": 100499,
                          "failed": 16412
                        }
                      },
                      "trigger": "decay_protection",
                      "trigger_explicit": true,
                      "source_action": "cron.decay_protect_hubspot",
                      "actor": "cron",
                      "admin_path": "/job/job_a1b2c3d4e5f6g7h8"
                    },
                    {
                      "type": "settings_changed",
                      "at": "2026-09-01T14:22:05.000Z",
                      "connection": {
                        "id": "conn_3f9a2b7c1d",
                        "type": "hubspot"
                      },
                      "actor": "customer",
                      "source": "updateFlossSettings",
                      "changes": {
                        "decayProtectHubSpot": {
                          "from": false,
                          "to": true
                        }
                      },
                      "changes_explicit": true,
                      "fields": [
                        "decayProtectHubSpot"
                      ],
                      "admin_path": "/floss-setting/conn_3f9a2b7c1d"
                    },
                    {
                      "type": "account_event",
                      "at": "2026-09-01T14:20:00.000Z",
                      "kind": "support_session_opened",
                      "actor": "customer",
                      "source": "support_session_mint",
                      "before": null,
                      "after": null,
                      "meta": {}
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/jobs": {
      "get": {
        "summary": "List verification jobs",
        "description": "Paginated history of the org's verification jobs, newest first. Includes jobs from every source — API submissions, CSV uploads, in-app pasted (`input`) imports, and ESP auto-cleanses — each tagged with a `source` (and `source_integration` for ESP jobs). Filter with `?source=` and/or `?status=`. Each job carries a `results` verdict summary (`passed`/`undeliverable`/`risky`/`unknown`), but ONLY once `status` is `completed` — counts accrue during processing and are `null` until the job finishes (cancelled jobs have partial counts, also `null`). Always paginated with the universal cursor envelope (default per_page 25, max 100); the cursor is opaque — clients MUST NOT parse it. Use `GET /v1/jobs/{id}` for the full per-reason breakdown.",
        "tags": [
          "Batch"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Integer in [1, 100]; default 25. Out of range → 400."
            },
            "required": false,
            "description": "Integer in [1, 100]; default 25. Out of range → 400.",
            "name": "per_page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque cursor from a previous response's `pagination.next_cursor`. Unparseable → 400."
            },
            "required": false,
            "description": "Opaque cursor from a previous response's `pagination.next_cursor`. Unparseable → 400.",
            "name": "cursor",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "api",
                "csv",
                "zapier",
                "widget",
                "input",
                "integration"
              ],
              "description": "Filter by submission source."
            },
            "required": false,
            "description": "Filter by submission source.",
            "name": "source",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "queued",
                "processing",
                "completed",
                "cancelled"
              ],
              "description": "Filter by lifecycle state."
            },
            "required": false,
            "description": "Filter by lifecycle state.",
            "name": "status",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated job history",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Stable job identifier. Address detail / results / cancel by it."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "queued",
                              "processing",
                              "completed",
                              "cancelled"
                            ],
                            "description": "Job lifecycle state. queued = accepted, not yet processing; processing = in flight; completed = finished; cancelled = stopped before completion (by the customer, or by the system reaping a stalled job)."
                          },
                          "source": {
                            "type": "string",
                            "enum": [
                              "api",
                              "csv",
                              "zapier",
                              "widget",
                              "input",
                              "integration"
                            ],
                            "description": "How the job was submitted. `api` = this REST API; `csv` = an in-app file upload; `input` = emails pasted into the in-app Copy & paste box; `widget` = the embeddable verification widget; `zapier` = the Zapier connector; `integration` = an ESP auto-cleanse (the ESP slug is in `source_integration`)."
                          },
                          "source_integration": {
                            "type": "string",
                            "nullable": true,
                            "description": "ESP slug when `source` is `integration`; otherwise null."
                          },
                          "created_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC submission time."
                          },
                          "finished_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC completion time when known, else null. Rarely, a `completed` job repaired by a maintenance sweep has no recorded finish time and reports null here."
                          },
                          "results": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "total": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Emails in the job — the same total the dashboard displays (emailsProcessed; legacy jobs that predate that counter fall back to the imported count)."
                              },
                              "processed": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Emails verified (emailsProcessed)."
                              },
                              "passed": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Verified-good — safe to send."
                              },
                              "failed": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Everything NOT passed — the dashboard's \"Failed\" total (undeliverable + risky + unknown). This is the headline \"how many invalid/bad were removed\" number; the per-reason `reasons` breakdown itemizes it."
                              },
                              "undeliverable": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Verified-bad — should be removed. A SUBSET of `failed`."
                              },
                              "risky": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Deliverable but risky (spam-trap/role/disposable/…). A SUBSET of `failed`."
                              },
                              "unknown": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Could not be determined (accept-all / unverifiable). A SUBSET of `failed`."
                              }
                            },
                            "required": [
                              "total",
                              "processed",
                              "passed",
                              "failed",
                              "undeliverable",
                              "risky",
                              "unknown"
                            ],
                            "description": "Verdict counts — null unless `status` is `completed` (counts are not trustworthy mid-flight; cancelled jobs have partial counts so also null)."
                          }
                        },
                        "required": [
                          "id",
                          "status",
                          "source",
                          "source_integration",
                          "created_at",
                          "finished_at",
                          "results"
                        ]
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Opaque cursor for the next page. `null` when has_more is false. Clients MUST NOT parse this string; the server may change the encoding."
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether more pages remain after this one."
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "job_a1b2c3d4e5f6g7h8",
                      "status": "completed",
                      "source": "api",
                      "source_integration": null,
                      "created_at": "2026-06-19T10:15:00Z",
                      "finished_at": "2026-06-19T10:18:30Z",
                      "results": {
                        "total": 1000,
                        "processed": 1000,
                        "passed": 850,
                        "failed": 150,
                        "undeliverable": 100,
                        "risky": 40,
                        "unknown": 10
                      }
                    },
                    {
                      "id": "job_9z8y7x6w5v4u3t2s",
                      "status": "processing",
                      "source": "csv",
                      "source_integration": null,
                      "created_at": "2026-06-20T13:45:00Z",
                      "finished_at": null,
                      "results": null
                    }
                  ],
                  "pagination": {
                    "next_cursor": "eyJfaWQiOiI2NjI3YWYxMmM0NWUxYWIwMDAxZWMyMzQ1In0",
                    "has_more": true
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/jobs/{id}": {
      "get": {
        "summary": "Get a verification job",
        "description": "Returns one verification job by id, with the full `results` breakdown including per-reason counts (`reasons`: spam / role_based / disposable / nonexistent / …). As with the list, `results` is `null` until the job's `status` is `completed`. A job id that does not exist or belongs to another org → 404 not_found. (For paginated per-email results use `GET /v1/batch-verify/{id}/results`; to cancel, `POST /v1/batch-verify/{id}/cancel`.)",
        "tags": [
          "Batch"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Job detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Stable job identifier. Address detail / results / cancel by it."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "queued",
                        "processing",
                        "completed",
                        "cancelled"
                      ],
                      "description": "Job lifecycle state. queued = accepted, not yet processing; processing = in flight; completed = finished; cancelled = stopped before completion (by the customer, or by the system reaping a stalled job)."
                    },
                    "source": {
                      "type": "string",
                      "enum": [
                        "api",
                        "csv",
                        "zapier",
                        "widget",
                        "input",
                        "integration"
                      ],
                      "description": "How the job was submitted. `api` = this REST API; `csv` = an in-app file upload; `input` = emails pasted into the in-app Copy & paste box; `widget` = the embeddable verification widget; `zapier` = the Zapier connector; `integration` = an ESP auto-cleanse (the ESP slug is in `source_integration`)."
                    },
                    "source_integration": {
                      "type": "string",
                      "nullable": true,
                      "description": "ESP slug when `source` is `integration`; otherwise null."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC submission time."
                    },
                    "finished_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC completion time when known, else null. Rarely, a `completed` job repaired by a maintenance sweep has no recorded finish time and reports null here."
                    },
                    "results": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "total": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Emails in the job — the same total the dashboard displays (emailsProcessed; legacy jobs that predate that counter fall back to the imported count)."
                        },
                        "processed": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Emails verified (emailsProcessed)."
                        },
                        "passed": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Verified-good — safe to send."
                        },
                        "failed": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Everything NOT passed — the dashboard's \"Failed\" total (undeliverable + risky + unknown). This is the headline \"how many invalid/bad were removed\" number; the per-reason `reasons` breakdown itemizes it."
                        },
                        "undeliverable": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Verified-bad — should be removed. A SUBSET of `failed`."
                        },
                        "risky": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Deliverable but risky (spam-trap/role/disposable/…). A SUBSET of `failed`."
                        },
                        "unknown": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Could not be determined (accept-all / unverifiable). A SUBSET of `failed`."
                        },
                        "reasons": {
                          "type": "object",
                          "properties": {
                            "invalid": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "nonexistent": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "deactivated": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "inbox_full": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "illegitimate": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "spam": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "role_based": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "banned_words": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "disposable": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "failed_exchange": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "accept_all": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "unverified": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "complained": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "bounced": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "blacklisted": {
                              "type": "integer",
                              "minimum": 0
                            },
                            "available": {
                              "type": "integer",
                              "minimum": 0
                            }
                          },
                          "required": [
                            "invalid",
                            "nonexistent",
                            "deactivated",
                            "inbox_full",
                            "illegitimate",
                            "spam",
                            "role_based",
                            "banned_words",
                            "disposable",
                            "failed_exchange",
                            "accept_all",
                            "unverified",
                            "complained",
                            "bounced",
                            "blacklisted",
                            "available"
                          ],
                          "description": "Per-reason counts (DETAIL only). Every key present; 0 when none."
                        }
                      },
                      "required": [
                        "total",
                        "processed",
                        "passed",
                        "failed",
                        "undeliverable",
                        "risky",
                        "unknown",
                        "reasons"
                      ],
                      "description": "Verdict counts incl. `reasons` — null unless `status` is `completed`."
                    }
                  },
                  "required": [
                    "id",
                    "status",
                    "source",
                    "source_integration",
                    "created_at",
                    "finished_at",
                    "results"
                  ]
                },
                "example": {
                  "id": "job_a1b2c3d4e5f6g7h8",
                  "status": "completed",
                  "source": "integration",
                  "source_integration": "mailchimp",
                  "created_at": "2026-06-19T08:00:00Z",
                  "finished_at": "2026-06-19T08:05:45Z",
                  "results": {
                    "total": 1000,
                    "processed": 1000,
                    "passed": 850,
                    "failed": 150,
                    "undeliverable": 100,
                    "risky": 40,
                    "unknown": 10,
                    "reasons": {
                      "invalid": 12,
                      "nonexistent": 60,
                      "deactivated": 18,
                      "inbox_full": 6,
                      "illegitimate": 3,
                      "spam": 9,
                      "role_based": 14,
                      "banned_words": 2,
                      "disposable": 8,
                      "failed_exchange": 1,
                      "accept_all": 26,
                      "unverified": 4,
                      "complained": 2,
                      "bounced": 5,
                      "blacklisted": 0,
                      "available": 850
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/users": {
      "get": {
        "summary": "List team members",
        "description": "Returns the org's team roster — the account owner plus any invited seats — each with a `role` (`administrator`/`contributor`), an `is_primary` flag (the owner), and a `status` (`active`/`invited`/`paused`). `created_at` is the member's account-creation time — `null` for `invited` seats (no account yet), and rarely for a member whose login email no longer matches their seat. Most orgs have a single member (the owner). Always paginated with the universal cursor envelope (default per_page 25, max 100); the cursor is opaque — clients MUST NOT parse it. Members are sorted by email; the owner is identified by `is_primary`, not list position. `role` reflects the seat assignment the dashboard's Team tab displays — display truth, not an enforced-capability guarantee (see the v1 contract).",
        "tags": [
          "Team"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Integer in [1, 100]; default 25. Out of range → 400."
            },
            "required": false,
            "description": "Integer in [1, 100]; default 25. Out of range → 400.",
            "name": "per_page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque cursor from a previous response's `pagination.next_cursor`. Unparseable → 400."
            },
            "required": false,
            "description": "Opaque cursor from a previous response's `pagination.next_cursor`. Unparseable → 400.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated team roster",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Opaque, stable member handle (`usr_<...>`). Address a member by it in GET /v1/users/{user_id}. Distinct from `email`; clients MUST NOT parse it.",
                            "example": "usr_amFuZUBhY21lLmNvbQ"
                          },
                          "email": {
                            "type": "string",
                            "description": "Member email address."
                          },
                          "name": {
                            "type": "string",
                            "nullable": true,
                            "description": "Display name, or null when unset."
                          },
                          "role": {
                            "type": "string",
                            "enum": [
                              "administrator",
                              "contributor"
                            ],
                            "description": "Role label for the seat, as shown in the dashboard's Team tab (the account owner is always `administrator`). Display truth: the label describes the intended permission level but is not an enforced-capability guarantee — see the v1 contract. Orthogonal to `is_primary`."
                          },
                          "is_primary": {
                            "type": "boolean",
                            "description": "True for the account owner (exactly one per org). Independent of `role` — answers \"who owns the account / billing\"."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "invited",
                              "paused"
                            ],
                            "description": "Member status. `active` = has an account and access; `invited` = invite sent, not yet accepted (no account yet); `paused` = seat disabled."
                          },
                          "created_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC account-creation time. `null` for `invited` seats — they have no account yet. Rarely also `null` for an `active`/`paused` member whose login email no longer matches their seat (e.g. they changed it after accepting) — the account exists but its creation time cannot be resolved. Do NOT infer status from this field; use `status`."
                          }
                        },
                        "required": [
                          "id",
                          "email",
                          "name",
                          "role",
                          "is_primary",
                          "status",
                          "created_at"
                        ]
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Opaque cursor for the next page. `null` when has_more is false. Clients MUST NOT parse this string; the server may change the encoding."
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether more pages remain after this one."
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "usr_amFuZUBhY21lLmNvbQ",
                      "email": "jane@acme.com",
                      "name": "Jane Smith",
                      "role": "administrator",
                      "is_primary": true,
                      "status": "active",
                      "created_at": "2026-01-15T08:45:00Z"
                    },
                    {
                      "id": "usr_am9obkBhY21lLmNvbQ",
                      "email": "john@acme.com",
                      "name": "John Analyst",
                      "role": "contributor",
                      "is_primary": false,
                      "status": "invited",
                      "created_at": null
                    }
                  ],
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/me": {
      "get": {
        "summary": "Get the account owner as a team member",
        "description": "Returns the account this API key belongs to, AS A TEAM SEAT (the same member object as GET /v1/users/{user_id}). Keys are org-scoped, so `me` is always the account OWNER (`is_primary: true`, role `administrator`) — regardless of which team member created the key. For the holder's account identity and security posture (email verification, two-factor, country, signup date), use GET /v1/account instead.",
        "tags": [
          "Team"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Team member (the account owner)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Opaque, stable member handle (`usr_<...>`). Address a member by it in GET /v1/users/{user_id}. Distinct from `email`; clients MUST NOT parse it.",
                      "example": "usr_amFuZUBhY21lLmNvbQ"
                    },
                    "email": {
                      "type": "string",
                      "description": "Member email address."
                    },
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Display name, or null when unset."
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "administrator",
                        "contributor"
                      ],
                      "description": "Role label for the seat, as shown in the dashboard's Team tab (the account owner is always `administrator`). Display truth: the label describes the intended permission level but is not an enforced-capability guarantee — see the v1 contract. Orthogonal to `is_primary`."
                    },
                    "is_primary": {
                      "type": "boolean",
                      "description": "True for the account owner (exactly one per org). Independent of `role` — answers \"who owns the account / billing\"."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "invited",
                        "paused"
                      ],
                      "description": "Member status. `active` = has an account and access; `invited` = invite sent, not yet accepted (no account yet); `paused` = seat disabled."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC account-creation time. `null` for `invited` seats — they have no account yet. Rarely also `null` for an `active`/`paused` member whose login email no longer matches their seat (e.g. they changed it after accepting) — the account exists but its creation time cannot be resolved. Do NOT infer status from this field; use `status`."
                    }
                  },
                  "required": [
                    "id",
                    "email",
                    "name",
                    "role",
                    "is_primary",
                    "status",
                    "created_at"
                  ]
                },
                "example": {
                  "id": "usr_amFuZUBhY21lLmNvbQ",
                  "email": "jane@acme.com",
                  "name": "Jane Smith",
                  "role": "administrator",
                  "is_primary": true,
                  "status": "active",
                  "created_at": "2026-01-15T08:45:00Z"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/users/{user_id}": {
      "get": {
        "summary": "Get a team member",
        "description": "Returns one team member by their opaque `id` (the `usr_<...>` handle from the list). A handle that does not resolve to a member of the authenticated org → 404 not_found. The literal `me` is also accepted and returns the authenticated account holder AS A TEAM SEAT (role + seat info). For the holder's account identity and security posture (email verification, two-factor, country, signup date), use GET /v1/account instead.",
        "tags": [
          "Team"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Opaque member handle (`usr_<...>`) from GET /v1/users, or the literal `me`."
            },
            "required": true,
            "description": "Opaque member handle (`usr_<...>`) from GET /v1/users, or the literal `me`.",
            "name": "user_id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Team member",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Opaque, stable member handle (`usr_<...>`). Address a member by it in GET /v1/users/{user_id}. Distinct from `email`; clients MUST NOT parse it.",
                      "example": "usr_amFuZUBhY21lLmNvbQ"
                    },
                    "email": {
                      "type": "string",
                      "description": "Member email address."
                    },
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Display name, or null when unset."
                    },
                    "role": {
                      "type": "string",
                      "enum": [
                        "administrator",
                        "contributor"
                      ],
                      "description": "Role label for the seat, as shown in the dashboard's Team tab (the account owner is always `administrator`). Display truth: the label describes the intended permission level but is not an enforced-capability guarantee — see the v1 contract. Orthogonal to `is_primary`."
                    },
                    "is_primary": {
                      "type": "boolean",
                      "description": "True for the account owner (exactly one per org). Independent of `role` — answers \"who owns the account / billing\"."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "invited",
                        "paused"
                      ],
                      "description": "Member status. `active` = has an account and access; `invited` = invite sent, not yet accepted (no account yet); `paused` = seat disabled."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC account-creation time. `null` for `invited` seats — they have no account yet. Rarely also `null` for an `active`/`paused` member whose login email no longer matches their seat (e.g. they changed it after accepting) — the account exists but its creation time cannot be resolved. Do NOT infer status from this field; use `status`."
                    }
                  },
                  "required": [
                    "id",
                    "email",
                    "name",
                    "role",
                    "is_primary",
                    "status",
                    "created_at"
                  ]
                },
                "example": {
                  "id": "usr_amFuZUBhY21lLmNvbQ",
                  "email": "jane@acme.com",
                  "name": "Jane Smith",
                  "role": "administrator",
                  "is_primary": true,
                  "status": "active",
                  "created_at": "2026-01-15T08:45:00Z"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/reports/usage": {
      "get": {
        "summary": "Get verification usage analytics",
        "description": "Org-level verification usage for one UTC calendar period — a top-level `total`, a `statuses` breakdown (`passed`/`undeliverable`/`risky`/`unknown`, per https://docs.mailfloss.com/status-definitions) and an org-wide `reasons` breakdown (same vocabulary as GET /v1/jobs/{id}). Select with `?period=` (default `this_month`). `period=lifetime` returns the all-time `total` only — `statuses` and `reasons` are `null` (only the grand total is stored). An org with no activity returns a zeroed report (200), not 404. `total` is authoritative; the `statuses` counts may not sum exactly to it.",
        "tags": [
          "Reports"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "this_week",
                "this_month",
                "this_year",
                "lifetime"
              ],
              "description": "Calendar period; default this_month. Unknown value → 400."
            },
            "required": false,
            "description": "Calendar period; default this_month. Unknown value → 400.",
            "name": "period",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Scope the report to a single ESP connection (the public connection id = the flossSettings `_id` from GET /v1/integrations/{type}). Omit for an org-wide report. Per-connection data exists only for this_week / this_month / this_year — combining it with `period=lifetime` returns 400 (lifetime is org-only)."
            },
            "required": false,
            "description": "Scope the report to a single ESP connection (the public connection id = the flossSettings `_id` from GET /v1/integrations/{type}). Omit for an org-wide report. Per-connection data exists only for this_week / this_month / this_year — combining it with `period=lifetime` returns 400 (lifetime is org-only).",
            "name": "connection_id",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Usage report",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "period": {
                      "type": "string",
                      "enum": [
                        "this_week",
                        "this_month",
                        "this_year",
                        "lifetime"
                      ],
                      "description": "Calendar period (UTC). this_week = current Sun–Sat; this_month = current calendar month; this_year = current calendar year; lifetime = all-time total (total only — no breakdown). Default this_month."
                    },
                    "connection_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "The ESP connection this report is scoped to when `?connection_id=` was supplied; `null` for an org-wide report. Per-connection data exists only for this_week / this_month / this_year (lifetime is org-only)."
                    },
                    "range": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "start": {
                          "type": "string",
                          "description": "Period start, YYYY-MM-DD (UTC)."
                        },
                        "end": {
                          "type": "string",
                          "description": "Period end, YYYY-MM-DD (UTC)."
                        }
                      },
                      "required": [
                        "start",
                        "end"
                      ],
                      "description": "UTC calendar boundaries; null for lifetime."
                    },
                    "total": {
                      "type": "integer",
                      "minimum": 0,
                      "description": "Emails verified in the period (authoritative grand total)."
                    },
                    "statuses": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "passed": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Verified-good — safe to send."
                        },
                        "failed": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Everything NOT passed for the period — the dashboard's \"Failed\" total (undeliverable + risky + unknown). The headline \"how many invalid/bad were removed this period\" number; `reasons` itemizes it."
                        },
                        "undeliverable": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Verified-bad — should be removed. A SUBSET of `failed`."
                        },
                        "risky": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Deliverable but risky (spam-trap / role / disposable / …). A SUBSET of `failed`."
                        },
                        "unknown": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Could not be determined (accept-all / unverifiable). A SUBSET of `failed`."
                        }
                      },
                      "required": [
                        "passed",
                        "failed",
                        "undeliverable",
                        "risky",
                        "unknown"
                      ],
                      "description": "Per-status counts for the period. null for lifetime."
                    },
                    "reasons": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "invalid": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "nonexistent": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "deactivated": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "inbox_full": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "illegitimate": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "spam": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "role_based": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "banned_words": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "disposable": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "failed_exchange": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "accept_all": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "unverified": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "complained": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "bounced": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "blacklisted": {
                          "type": "integer",
                          "minimum": 0
                        },
                        "available": {
                          "type": "integer",
                          "minimum": 0
                        }
                      },
                      "required": [
                        "invalid",
                        "nonexistent",
                        "deactivated",
                        "inbox_full",
                        "illegitimate",
                        "spam",
                        "role_based",
                        "banned_words",
                        "disposable",
                        "failed_exchange",
                        "accept_all",
                        "unverified",
                        "complained",
                        "bounced",
                        "blacklisted",
                        "available"
                      ],
                      "description": "Per-reason counts. Every key present; 0 when none. null for lifetime."
                    }
                  },
                  "required": [
                    "period",
                    "connection_id",
                    "range",
                    "total",
                    "statuses",
                    "reasons"
                  ]
                },
                "example": {
                  "period": "this_month",
                  "connection_id": null,
                  "range": {
                    "start": "2026-06-01",
                    "end": "2026-06-30"
                  },
                  "total": 45000,
                  "statuses": {
                    "passed": 40000,
                    "failed": 5000,
                    "undeliverable": 3500,
                    "risky": 1200,
                    "unknown": 300
                  },
                  "reasons": {
                    "invalid": 500,
                    "nonexistent": 1200,
                    "deactivated": 800,
                    "inbox_full": 400,
                    "illegitimate": 200,
                    "spam": 300,
                    "role_based": 150,
                    "banned_words": 100,
                    "disposable": 250,
                    "failed_exchange": 80,
                    "accept_all": 350,
                    "unverified": 200,
                    "complained": 50,
                    "bounced": 120,
                    "blacklisted": 0,
                    "available": 40000
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "get": {
        "summary": "List webhook subscriptions",
        "description": "Paginated list of the org's webhook subscriptions — the same subscriptions managed on the dashboard's webhooks page. The signing `secret` is never returned; `has_secret` indicates whether one is set. Always paginated with the universal cursor envelope (default per_page 25, max 100); the cursor is opaque — clients MUST NOT parse it.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Integer in [1, 100]; default 25. Out of range → 400."
            },
            "required": false,
            "description": "Integer in [1, 100]; default 25. Out of range → 400.",
            "name": "per_page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque cursor from a previous response's `pagination.next_cursor`. Unparseable → 400."
            },
            "required": false,
            "description": "Opaque cursor from a previous response's `pagination.next_cursor`. Unparseable → 400.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated webhook subscriptions",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Stable subscription identifier."
                          },
                          "name": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 200,
                            "description": "Display name for the subscription (1-200 chars)."
                          },
                          "url": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 2048,
                            "description": "HTTPS/HTTP endpoint mailfloss POSTs deliveries to. Loopback, link-local and private-network hosts are rejected. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts the subscription is automatically deactivated (`active: false`)."
                          },
                          "events": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "enum": [
                                "status-undeliverable",
                                "status-unknown",
                                "status-risky",
                                "job-finished",
                                "contact-fixed",
                                "contact-unsubscribed",
                                "contact-deleted",
                                "contact-tagged",
                                "contact-updated",
                                "reason-illegitimate",
                                "reason-bounced",
                                "reason-invalid",
                                "reason-blacklisted",
                                "reason-role",
                                "reason-complainer",
                                "reason-spam",
                                "reason-available",
                                "reason-banned",
                                "reason-disposable",
                                "reason-deactivated",
                                "reason-nonexistent",
                                "reason-inbox_full",
                                "reason-unverified",
                                "reason-accept_all"
                              ],
                              "description": "A subscribable event code. `status-*` fire per verified email by verdict; `reason-*` fire per verified email by failure reason; `contact-*` fire on contact-level actions mailfloss performed; `job-finished` fires once per completed verification job. The delivery payload's `data.event` uses the dotted form of the same code (e.g. `status-risky` delivers `status.risky`)."
                            },
                            "description": "Event codes this subscription fires for. May be empty for legacy dashboard-created subscriptions (which then never fire)."
                          },
                          "data_source": {
                            "type": "string",
                            "minLength": 1,
                            "maxLength": 64,
                            "description": "Which connections feed this subscription: the literal \"all\", or a single connection id (as returned by /v1/integrations/{type} `connections[].id`) to scope to that connection only. 422 if the id is not one of your connections."
                          },
                          "active": {
                            "type": "boolean",
                            "description": "Whether the subscription fires. Set false to pause without deleting. Automatically set false by the platform after 5 failed deliveries."
                          },
                          "has_secret": {
                            "type": "boolean",
                            "description": "Whether a signing secret is set (the secret itself is never returned)."
                          },
                          "created_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC creation time — null for subscriptions created in the dashboard before timestamps existed."
                          },
                          "updated_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC last-update time via the API, or null."
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "url",
                          "events",
                          "data_source",
                          "active",
                          "has_secret",
                          "created_at",
                          "updated_at"
                        ]
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Opaque cursor for the next page. `null` when has_more is false. Clients MUST NOT parse this string; the server may change the encoding."
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether more pages remain after this one."
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "aBcDeFgHiJkLmNoPq",
                      "name": "CRM sync — undeliverables",
                      "url": "https://example.com/mailfloss/webhook",
                      "events": [
                        "status-undeliverable",
                        "job-finished"
                      ],
                      "data_source": "all",
                      "active": true,
                      "has_secret": true,
                      "created_at": "2026-07-10T04:12:00.000Z",
                      "updated_at": "2026-07-10T04:12:00.000Z"
                    }
                  ],
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a webhook subscription",
        "description": "Creates a webhook subscription — the same result as adding one on the dashboard's webhooks page. Requires `name`, a publicly-reachable http(s) `url`, and at least one event code from the closed `events` enum. `data_source` scopes which connections feed the subscription: `\"all\"` (default) or a single connection id from GET /v1/integrations/{type}. The optional `secret` enables delivery signing (HMAC-SHA1 of the JSON `data` payload in the `X-Request-Signature` header) and is write-only — it can never be read back. Webhooks are a Business/Pro-plan feature (or the Webhooks add-on): requests from non-entitled plans → `403 forbidden`. The first subscription automatically enables webhook delivery for your account. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts a subscription is automatically deactivated (`active: false`). Honors `Idempotency-Key` (a retried POST with the same key replays the original `201`).",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress)."
            },
            "required": false,
            "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress).",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Display name for the subscription (1-200 chars)."
                  },
                  "url": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048,
                    "description": "HTTPS/HTTP endpoint mailfloss POSTs deliveries to. Loopback, link-local and private-network hosts are rejected. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts the subscription is automatically deactivated (`active: false`)."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "status-undeliverable",
                        "status-unknown",
                        "status-risky",
                        "job-finished",
                        "contact-fixed",
                        "contact-unsubscribed",
                        "contact-deleted",
                        "contact-tagged",
                        "contact-updated",
                        "reason-illegitimate",
                        "reason-bounced",
                        "reason-invalid",
                        "reason-blacklisted",
                        "reason-role",
                        "reason-complainer",
                        "reason-spam",
                        "reason-available",
                        "reason-banned",
                        "reason-disposable",
                        "reason-deactivated",
                        "reason-nonexistent",
                        "reason-inbox_full",
                        "reason-unverified",
                        "reason-accept_all"
                      ],
                      "description": "A subscribable event code. `status-*` fire per verified email by verdict; `reason-*` fire per verified email by failure reason; `contact-*` fire on contact-level actions mailfloss performed; `job-finished` fires once per completed verification job. The delivery payload's `data.event` uses the dotted form of the same code (e.g. `status-risky` delivers `status.risky`)."
                    },
                    "minItems": 1,
                    "maxItems": 24
                  },
                  "data_source": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "description": "Defaults to \"all\"."
                  },
                  "active": {
                    "type": "boolean",
                    "description": "Defaults to true."
                  },
                  "secret": {
                    "type": "string",
                    "maxLength": 256,
                    "description": "Optional signing secret — WRITE-ONLY, never returned by any read. When set, each delivery carries an `X-Request-Signature` header: the hex-encoded HMAC-SHA1 of the JSON-serialized `data` payload keyed with this secret. On PATCH, an empty string clears the secret; omitting the field leaves it unchanged."
                  }
                },
                "required": [
                  "name",
                  "url",
                  "events"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Stable subscription identifier."
                    },
                    "name": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200,
                      "description": "Display name for the subscription (1-200 chars)."
                    },
                    "url": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2048,
                      "description": "HTTPS/HTTP endpoint mailfloss POSTs deliveries to. Loopback, link-local and private-network hosts are rejected. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts the subscription is automatically deactivated (`active: false`)."
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "status-undeliverable",
                          "status-unknown",
                          "status-risky",
                          "job-finished",
                          "contact-fixed",
                          "contact-unsubscribed",
                          "contact-deleted",
                          "contact-tagged",
                          "contact-updated",
                          "reason-illegitimate",
                          "reason-bounced",
                          "reason-invalid",
                          "reason-blacklisted",
                          "reason-role",
                          "reason-complainer",
                          "reason-spam",
                          "reason-available",
                          "reason-banned",
                          "reason-disposable",
                          "reason-deactivated",
                          "reason-nonexistent",
                          "reason-inbox_full",
                          "reason-unverified",
                          "reason-accept_all"
                        ],
                        "description": "A subscribable event code. `status-*` fire per verified email by verdict; `reason-*` fire per verified email by failure reason; `contact-*` fire on contact-level actions mailfloss performed; `job-finished` fires once per completed verification job. The delivery payload's `data.event` uses the dotted form of the same code (e.g. `status-risky` delivers `status.risky`)."
                      },
                      "description": "Event codes this subscription fires for. May be empty for legacy dashboard-created subscriptions (which then never fire)."
                    },
                    "data_source": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64,
                      "description": "Which connections feed this subscription: the literal \"all\", or a single connection id (as returned by /v1/integrations/{type} `connections[].id`) to scope to that connection only. 422 if the id is not one of your connections."
                    },
                    "active": {
                      "type": "boolean",
                      "description": "Whether the subscription fires. Set false to pause without deleting. Automatically set false by the platform after 5 failed deliveries."
                    },
                    "has_secret": {
                      "type": "boolean",
                      "description": "Whether a signing secret is set (the secret itself is never returned)."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC creation time — null for subscriptions created in the dashboard before timestamps existed."
                    },
                    "updated_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC last-update time via the API, or null."
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "events",
                    "data_source",
                    "active",
                    "has_secret",
                    "created_at",
                    "updated_at"
                  ]
                },
                "example": {
                  "id": "aBcDeFgHiJkLmNoPq",
                  "name": "CRM sync — undeliverables",
                  "url": "https://example.com/mailfloss/webhook",
                  "events": [
                    "status-undeliverable",
                    "job-finished"
                  ],
                  "data_source": "all",
                  "active": true,
                  "has_secret": true,
                  "created_at": "2026-07-10T04:12:00.000Z",
                  "updated_at": "2026-07-10T04:12:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reuse while an earlier request with the same key is still in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Idempotency-Key was previously used with a different request body (fingerprint mismatch).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_key_reused",
                    "message": "This Idempotency-Key was already used with a different request body.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "get": {
        "summary": "Get a webhook subscription",
        "description": "Returns one webhook subscription by id. The signing `secret` is never returned (`has_secret` indicates whether one is set). An id that does not exist or belongs to another org → 404 not_found. `created_at`/`updated_at` are null for subscriptions created in the dashboard before timestamps existed.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Webhook subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Stable subscription identifier."
                    },
                    "name": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200,
                      "description": "Display name for the subscription (1-200 chars)."
                    },
                    "url": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2048,
                      "description": "HTTPS/HTTP endpoint mailfloss POSTs deliveries to. Loopback, link-local and private-network hosts are rejected. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts the subscription is automatically deactivated (`active: false`)."
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "status-undeliverable",
                          "status-unknown",
                          "status-risky",
                          "job-finished",
                          "contact-fixed",
                          "contact-unsubscribed",
                          "contact-deleted",
                          "contact-tagged",
                          "contact-updated",
                          "reason-illegitimate",
                          "reason-bounced",
                          "reason-invalid",
                          "reason-blacklisted",
                          "reason-role",
                          "reason-complainer",
                          "reason-spam",
                          "reason-available",
                          "reason-banned",
                          "reason-disposable",
                          "reason-deactivated",
                          "reason-nonexistent",
                          "reason-inbox_full",
                          "reason-unverified",
                          "reason-accept_all"
                        ],
                        "description": "A subscribable event code. `status-*` fire per verified email by verdict; `reason-*` fire per verified email by failure reason; `contact-*` fire on contact-level actions mailfloss performed; `job-finished` fires once per completed verification job. The delivery payload's `data.event` uses the dotted form of the same code (e.g. `status-risky` delivers `status.risky`)."
                      },
                      "description": "Event codes this subscription fires for. May be empty for legacy dashboard-created subscriptions (which then never fire)."
                    },
                    "data_source": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64,
                      "description": "Which connections feed this subscription: the literal \"all\", or a single connection id (as returned by /v1/integrations/{type} `connections[].id`) to scope to that connection only. 422 if the id is not one of your connections."
                    },
                    "active": {
                      "type": "boolean",
                      "description": "Whether the subscription fires. Set false to pause without deleting. Automatically set false by the platform after 5 failed deliveries."
                    },
                    "has_secret": {
                      "type": "boolean",
                      "description": "Whether a signing secret is set (the secret itself is never returned)."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC creation time — null for subscriptions created in the dashboard before timestamps existed."
                    },
                    "updated_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC last-update time via the API, or null."
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "events",
                    "data_source",
                    "active",
                    "has_secret",
                    "created_at",
                    "updated_at"
                  ]
                },
                "example": {
                  "id": "aBcDeFgHiJkLmNoPq",
                  "name": "CRM sync — undeliverables",
                  "url": "https://example.com/mailfloss/webhook",
                  "events": [
                    "status-undeliverable",
                    "job-finished"
                  ],
                  "data_source": "all",
                  "active": true,
                  "has_secret": true,
                  "created_at": "2026-07-10T04:12:00.000Z",
                  "updated_at": "2026-07-10T04:12:00.000Z"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a webhook subscription",
        "description": "Partial update (JSON merge-patch — omitted fields are left unchanged) of a subscription: any of `name`, `url`, `events` (replaces the whole set), `data_source`, `active` (pause/resume without deleting), and `secret`. Setting `secret` to a non-empty string replaces it; an empty string `\"\"` clears it; omitting it leaves it unchanged. At least one field is required. Returns `200` with the refreshed subscription. Non-entitled plans → `403 forbidden`. An id that does not exist or belongs to another org → 404. PATCH is idempotent; it does not consume the `Idempotency-Key` header.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Display name for the subscription (1-200 chars)."
                  },
                  "url": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2048,
                    "description": "HTTPS/HTTP endpoint mailfloss POSTs deliveries to. Loopback, link-local and private-network hosts are rejected. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts the subscription is automatically deactivated (`active: false`)."
                  },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "status-undeliverable",
                        "status-unknown",
                        "status-risky",
                        "job-finished",
                        "contact-fixed",
                        "contact-unsubscribed",
                        "contact-deleted",
                        "contact-tagged",
                        "contact-updated",
                        "reason-illegitimate",
                        "reason-bounced",
                        "reason-invalid",
                        "reason-blacklisted",
                        "reason-role",
                        "reason-complainer",
                        "reason-spam",
                        "reason-available",
                        "reason-banned",
                        "reason-disposable",
                        "reason-deactivated",
                        "reason-nonexistent",
                        "reason-inbox_full",
                        "reason-unverified",
                        "reason-accept_all"
                      ],
                      "description": "A subscribable event code. `status-*` fire per verified email by verdict; `reason-*` fire per verified email by failure reason; `contact-*` fire on contact-level actions mailfloss performed; `job-finished` fires once per completed verification job. The delivery payload's `data.event` uses the dotted form of the same code (e.g. `status-risky` delivers `status.risky`)."
                    },
                    "minItems": 1,
                    "maxItems": 24
                  },
                  "data_source": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "description": "Which connections feed this subscription: the literal \"all\", or a single connection id (as returned by /v1/integrations/{type} `connections[].id`) to scope to that connection only. 422 if the id is not one of your connections."
                  },
                  "active": {
                    "type": "boolean"
                  },
                  "secret": {
                    "anyOf": [
                      {
                        "type": "string",
                        "maxLength": 256,
                        "description": "Optional signing secret — WRITE-ONLY, never returned by any read. When set, each delivery carries an `X-Request-Signature` header: the hex-encoded HMAC-SHA1 of the JSON-serialized `data` payload keyed with this secret. On PATCH, an empty string clears the secret; omitting the field leaves it unchanged."
                      },
                      {
                        "type": "string",
                        "enum": [
                          ""
                        ]
                      }
                    ]
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated subscription",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Stable subscription identifier."
                    },
                    "name": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 200,
                      "description": "Display name for the subscription (1-200 chars)."
                    },
                    "url": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 2048,
                      "description": "HTTPS/HTTP endpoint mailfloss POSTs deliveries to. Loopback, link-local and private-network hosts are rejected. Delivery is best-effort (no ordering guarantee, may burst); after 5 failed delivery attempts the subscription is automatically deactivated (`active: false`)."
                    },
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "status-undeliverable",
                          "status-unknown",
                          "status-risky",
                          "job-finished",
                          "contact-fixed",
                          "contact-unsubscribed",
                          "contact-deleted",
                          "contact-tagged",
                          "contact-updated",
                          "reason-illegitimate",
                          "reason-bounced",
                          "reason-invalid",
                          "reason-blacklisted",
                          "reason-role",
                          "reason-complainer",
                          "reason-spam",
                          "reason-available",
                          "reason-banned",
                          "reason-disposable",
                          "reason-deactivated",
                          "reason-nonexistent",
                          "reason-inbox_full",
                          "reason-unverified",
                          "reason-accept_all"
                        ],
                        "description": "A subscribable event code. `status-*` fire per verified email by verdict; `reason-*` fire per verified email by failure reason; `contact-*` fire on contact-level actions mailfloss performed; `job-finished` fires once per completed verification job. The delivery payload's `data.event` uses the dotted form of the same code (e.g. `status-risky` delivers `status.risky`)."
                      },
                      "description": "Event codes this subscription fires for. May be empty for legacy dashboard-created subscriptions (which then never fire)."
                    },
                    "data_source": {
                      "type": "string",
                      "minLength": 1,
                      "maxLength": 64,
                      "description": "Which connections feed this subscription: the literal \"all\", or a single connection id (as returned by /v1/integrations/{type} `connections[].id`) to scope to that connection only. 422 if the id is not one of your connections."
                    },
                    "active": {
                      "type": "boolean",
                      "description": "Whether the subscription fires. Set false to pause without deleting. Automatically set false by the platform after 5 failed deliveries."
                    },
                    "has_secret": {
                      "type": "boolean",
                      "description": "Whether a signing secret is set (the secret itself is never returned)."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC creation time — null for subscriptions created in the dashboard before timestamps existed."
                    },
                    "updated_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC last-update time via the API, or null."
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "url",
                    "events",
                    "data_source",
                    "active",
                    "has_secret",
                    "created_at",
                    "updated_at"
                  ]
                },
                "example": {
                  "id": "aBcDeFgHiJkLmNoPq",
                  "name": "CRM sync — undeliverables",
                  "url": "https://example.com/mailfloss/webhook",
                  "events": [
                    "status-undeliverable",
                    "job-finished"
                  ],
                  "data_source": "all",
                  "active": true,
                  "has_secret": true,
                  "created_at": "2026-07-10T04:12:00.000Z",
                  "updated_at": "2026-07-10T04:12:00.000Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Delete a webhook subscription",
        "description": "Permanently removes a webhook subscription — deliveries stop immediately. Returns `200` with `{ id, deleted: true }`. An id that does not exist or belongs to another org → 404 not_found (so a repeated DELETE is a safe 404). To pause without deleting, PATCH `active: false` instead.",
        "tags": [
          "Webhooks"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1
            },
            "required": true,
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "deleted": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "deleted"
                  ]
                },
                "example": {
                  "id": "aBcDeFgHiJkLmNoPq",
                  "deleted": true
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/check-key": {
      "get": {
        "summary": "Verify an API key",
        "description": "Validates the supplied api_key or public_key and returns `{verified: true, status: \"success\"}`, plus opt-in extras per query toggle: `check_plan` adds `remove_branding`, `check_credits` adds `credits_remaining` (prepaid + subscription credits summed), `get_token` adds the widget signed-proof triple (`XSRF-TOKEN`, `MFCLIENTID`, `SIGNED-PROOF`, also set as cookies). Used by integrations and the widget to verify auth before issuing further calls. For account name, plan, and named credit buckets, see GET /v1/organization.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "description": "The API key to verify (prefixed `mf_live_`/`mf_rk_` or legacy). Provide this or `public_key`."
            },
            "required": false,
            "description": "The API key to verify (prefixed `mf_live_`/`mf_rk_` or legacy). Provide this or `public_key`.",
            "name": "api_key",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Alternative to api_key for widget/browser-side clients."
            },
            "required": false,
            "description": "Alternative to api_key for widget/browser-side clients.",
            "name": "public_key",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Any truthy value adds `remove_branding` to the response."
            },
            "required": false,
            "description": "Any truthy value adds `remove_branding` to the response.",
            "name": "check_plan",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Any truthy value adds the widget signed-proof token triple."
            },
            "required": false,
            "description": "Any truthy value adds the widget signed-proof token triple.",
            "name": "get_token",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Any truthy value adds `credits_remaining` (freshly read, uncached)."
            },
            "required": false,
            "description": "Any truthy value adds `credits_remaining` (freshly read, uncached).",
            "name": "check_credits",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Key verification result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "verified": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ]
                    },
                    "remove_branding": {
                      "type": "boolean"
                    },
                    "credits_remaining": {
                      "type": "number"
                    },
                    "XSRF-TOKEN": {
                      "type": "string"
                    },
                    "MFCLIENTID": {
                      "type": "string"
                    },
                    "SIGNED-PROOF": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "verified",
                    "status"
                  ]
                },
                "example": {
                  "verified": true,
                  "status": "success",
                  "remove_branding": true,
                  "credits_remaining": 7500
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "summary": "Read the authenticated account holder",
        "description": "Returns the account holder's identity and security posture — name, email, `email_verified`, `two_factor_enabled`, `country`, and signup date — for the account the API key belongs to. Complements GET /v1/organization (plan/credits/usage) and GET /v1/users/me (the holder as a team-roster seat). Scoped implicitly to the authenticated account — no path parameter. Idempotent read; no credit consumption.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authenticated account holder",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Opaque, stable account-holder handle (`usr_<...>`). Matches the `is_primary` member returned by GET /v1/users. Distinct from `email`; clients MUST NOT parse it."
                    },
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder display name, or null when unset."
                    },
                    "email": {
                      "type": "string",
                      "description": "Holder email address (primary email on the account)."
                    },
                    "email_verified": {
                      "type": "boolean",
                      "description": "True when the holder has confirmed their primary email address."
                    },
                    "two_factor_enabled": {
                      "type": "boolean",
                      "description": "True when two-factor authentication (TOTP) is enabled on the account. The 2FA secret itself is never exposed."
                    },
                    "identity_verified": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "verified",
                        "requires_input"
                      ],
                      "description": "Tri-state identity-verification status. `verified` once the holder has completed identity verification; `requires_input` when a verification attempt needs the holder to resubmit; `null` when verification was never requested (the common case). NOT a boolean — `null` is distinct from a failed check."
                    },
                    "phone": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder contact phone in E.164 form (country code included), or null when unset."
                    },
                    "organization": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder organization / company name, or null."
                    },
                    "vat_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "VAT identification number (EU customers), or null."
                    },
                    "country": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder country (ISO code) when on file, else null. Often unset."
                    },
                    "address": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "line1": {
                          "type": "string",
                          "nullable": true
                        },
                        "line2": {
                          "type": "string",
                          "nullable": true
                        },
                        "city": {
                          "type": "string",
                          "nullable": true
                        },
                        "state": {
                          "type": "string",
                          "nullable": true
                        },
                        "postal_code": {
                          "type": "string",
                          "nullable": true
                        },
                        "country": {
                          "type": "string",
                          "nullable": true
                        }
                      },
                      "required": [
                        "line1",
                        "line2",
                        "city",
                        "state",
                        "postal_code",
                        "country"
                      ],
                      "description": "Billing/contact address, or null when no address is on file. Individual sub-fields may be null."
                    },
                    "notifications": {
                      "type": "object",
                      "properties": {
                        "after_flossing": {
                          "type": "boolean"
                        },
                        "weekly_report": {
                          "type": "boolean"
                        },
                        "monthly_report": {
                          "type": "boolean"
                        },
                        "payment_receipts": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "after_flossing",
                        "weekly_report",
                        "monthly_report",
                        "payment_receipts"
                      ],
                      "description": "Email-notification preferences. `after_flossing` = a summary email after each completed floss; `weekly_report` / `monthly_report` = periodic usage reports; `payment_receipts` = billing receipts. Always present."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC account-creation (signup) timestamp, or null when unknown."
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "email",
                    "email_verified",
                    "two_factor_enabled",
                    "identity_verified",
                    "phone",
                    "organization",
                    "vat_id",
                    "country",
                    "address",
                    "notifications",
                    "created_at"
                  ],
                  "description": "Single-resource bare-object body (per v1_contract.md). The account holder this API key belongs to. Clients MUST tolerate unknown additive sibling fields — future additions ship as additive non-breaking changes."
                },
                "example": {
                  "id": "usr_amFuZUBhY21lLmNvbQ",
                  "name": "Jane Smith",
                  "email": "jane@acme.com",
                  "email_verified": true,
                  "two_factor_enabled": true,
                  "identity_verified": "verified",
                  "phone": "+1-415-555-0142",
                  "organization": "Acme Corp",
                  "vat_id": null,
                  "country": "US",
                  "address": {
                    "line1": "500 Market St",
                    "line2": null,
                    "city": "San Francisco",
                    "state": "CA",
                    "postal_code": "94105",
                    "country": "US"
                  },
                  "notifications": {
                    "after_flossing": true,
                    "weekly_report": false,
                    "monthly_report": false,
                    "payment_receipts": true
                  },
                  "created_at": "2026-01-15T08:45:00Z"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update the authenticated account holder",
        "description": "Partial update (JSON merge-patch — omitted fields are left unchanged) of the account holder's contact details, organization, address, and email-notification preferences. Body may set any of: `name`, `phone`, `organization`, `vat_id`, `country` (a null clears a scalar), `address` (a partial sub-object), and `notifications` (a partial map of `after_flossing` / `weekly_report` / `monthly_report` / `payment_receipts`). At least one field is required. Returns `200` with the refreshed account (same shape as the GET). Requires the elevated `write:account` scope (owner/admin keys). `email` (login identity), password, two-factor, and the org-level stop-at-credit-limit setting are NOT updatable here. PATCH is idempotent; it does not consume the `Idempotency-Key` header. Plain document write: dashboard follow-on side effects (e.g. the Canadian tax-rate recompute on a CA province change) do NOT run — change CA provinces in the dashboard.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Holder display name."
                  },
                  "phone": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 40,
                    "description": "Contact phone (null clears it)."
                  },
                  "organization": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 200,
                    "description": "Organization / company name (null clears it)."
                  },
                  "vat_id": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 60,
                    "description": "VAT identification number (null clears it)."
                  },
                  "country": {
                    "type": "string",
                    "nullable": true,
                    "maxLength": 60,
                    "description": "Country (ISO code; null clears it)."
                  },
                  "address": {
                    "type": "object",
                    "properties": {
                      "line1": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 200
                      },
                      "line2": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 200
                      },
                      "city": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 120
                      },
                      "state": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 120
                      },
                      "postal_code": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 40
                      },
                      "country": {
                        "type": "string",
                        "nullable": true,
                        "maxLength": 60
                      }
                    },
                    "additionalProperties": false,
                    "description": "Partial address update. A null sub-field clears it. Merges per-subfield (the dashboard replaces the whole address). Plain document write only: does NOT trigger the dashboard's follow-on side effects — notably the Canadian tax-rate recompute on a CA province change. Change CA provinces in the dashboard."
                  },
                  "notifications": {
                    "type": "object",
                    "properties": {
                      "after_flossing": {
                        "type": "boolean"
                      },
                      "weekly_report": {
                        "type": "boolean"
                      },
                      "monthly_report": {
                        "type": "boolean"
                      },
                      "payment_receipts": {
                        "type": "boolean"
                      }
                    },
                    "additionalProperties": false,
                    "description": "Partial email-notification preference update."
                  }
                },
                "additionalProperties": false,
                "description": "Partial update (JSON merge-patch — omitted fields are left unchanged) of the account holder's contact details, organization, address, and email-notification preferences. Returns 200 with the refreshed account (same shape as GET). Email change, password, and 2FA are NOT updatable here."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated account holder",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Opaque, stable account-holder handle (`usr_<...>`). Matches the `is_primary` member returned by GET /v1/users. Distinct from `email`; clients MUST NOT parse it."
                    },
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder display name, or null when unset."
                    },
                    "email": {
                      "type": "string",
                      "description": "Holder email address (primary email on the account)."
                    },
                    "email_verified": {
                      "type": "boolean",
                      "description": "True when the holder has confirmed their primary email address."
                    },
                    "two_factor_enabled": {
                      "type": "boolean",
                      "description": "True when two-factor authentication (TOTP) is enabled on the account. The 2FA secret itself is never exposed."
                    },
                    "identity_verified": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "verified",
                        "requires_input"
                      ],
                      "description": "Tri-state identity-verification status. `verified` once the holder has completed identity verification; `requires_input` when a verification attempt needs the holder to resubmit; `null` when verification was never requested (the common case). NOT a boolean — `null` is distinct from a failed check."
                    },
                    "phone": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder contact phone in E.164 form (country code included), or null when unset."
                    },
                    "organization": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder organization / company name, or null."
                    },
                    "vat_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "VAT identification number (EU customers), or null."
                    },
                    "country": {
                      "type": "string",
                      "nullable": true,
                      "description": "Holder country (ISO code) when on file, else null. Often unset."
                    },
                    "address": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "line1": {
                          "type": "string",
                          "nullable": true
                        },
                        "line2": {
                          "type": "string",
                          "nullable": true
                        },
                        "city": {
                          "type": "string",
                          "nullable": true
                        },
                        "state": {
                          "type": "string",
                          "nullable": true
                        },
                        "postal_code": {
                          "type": "string",
                          "nullable": true
                        },
                        "country": {
                          "type": "string",
                          "nullable": true
                        }
                      },
                      "required": [
                        "line1",
                        "line2",
                        "city",
                        "state",
                        "postal_code",
                        "country"
                      ],
                      "description": "Billing/contact address, or null when no address is on file. Individual sub-fields may be null."
                    },
                    "notifications": {
                      "type": "object",
                      "properties": {
                        "after_flossing": {
                          "type": "boolean"
                        },
                        "weekly_report": {
                          "type": "boolean"
                        },
                        "monthly_report": {
                          "type": "boolean"
                        },
                        "payment_receipts": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "after_flossing",
                        "weekly_report",
                        "monthly_report",
                        "payment_receipts"
                      ],
                      "description": "Email-notification preferences. `after_flossing` = a summary email after each completed floss; `weekly_report` / `monthly_report` = periodic usage reports; `payment_receipts` = billing receipts. Always present."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC account-creation (signup) timestamp, or null when unknown."
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "email",
                    "email_verified",
                    "two_factor_enabled",
                    "identity_verified",
                    "phone",
                    "organization",
                    "vat_id",
                    "country",
                    "address",
                    "notifications",
                    "created_at"
                  ],
                  "description": "Single-resource bare-object body (per v1_contract.md). The account holder this API key belongs to. Clients MUST tolerate unknown additive sibling fields — future additions ship as additive non-breaking changes."
                },
                "example": {
                  "id": "usr_amFuZUBhY21lLmNvbQ",
                  "name": "Jane Smith",
                  "email": "jane@acme.com",
                  "email_verified": true,
                  "two_factor_enabled": true,
                  "identity_verified": "verified",
                  "phone": "+1-415-555-0142",
                  "organization": "Acme Corp",
                  "vat_id": null,
                  "country": "US",
                  "address": {
                    "line1": "500 Market St",
                    "line2": null,
                    "city": "San Francisco",
                    "state": "CA",
                    "postal_code": "94105",
                    "country": "US"
                  },
                  "notifications": {
                    "after_flossing": true,
                    "weekly_report": false,
                    "monthly_report": false,
                    "payment_receipts": true
                  },
                  "created_at": "2026-01-15T08:45:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/organization": {
      "get": {
        "summary": "Read the authenticated organization",
        "description": "Returns plan, named credit buckets (`prepaid` + `subscription`), current-period usage, contact email, and `status` for the org the API key belongs to. Scoped implicitly to the authenticated org — no `:id` path parameter. Idempotent read; no credit consumption. Unlike credit-consuming endpoints, this read still succeeds for a *restricted* account (`suspended` / `paused` / `under_review` / `needs_identity_verification`) and reports the state in `status` — so an automated client can detect, for example, a billing suspension and act on it. A `banned` account is the one exception: it returns `403 forbidden`.",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authenticated organization",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "nullable": true,
                      "description": "Display name of the organization. May be `null` if the org was created without one."
                    },
                    "email": {
                      "type": "string",
                      "nullable": true,
                      "description": "The account owner's email — derived from the linked Meteor user doc (`Meteor.users` `emails[0].address`). `null` if the linked user is missing or has no email on file."
                    },
                    "plan": {
                      "type": "string",
                      "description": "Customer-facing plan slug (e.g. \"free\", \"pro\", \"business\"). Sourced from `org.planName` (lowercased) when present, falling back to `org.planType` IF that field is itself a friendly slug. When the org's `planType` is a Stripe price object ID (`price_*`) and no `planName` is set, this field returns `\"unknown\"` — never expose a `price_*` handle here. Consumers that need the raw Stripe price ID can read the additive `plan_id` field."
                    },
                    "plan_id": {
                      "type": "string",
                      "nullable": true,
                      "description": "Raw `org.planType` Mongo value, exposed additively for SDK consumers that want to cross-reference with Stripe. May be a friendly slug (legacy orgs) or a Stripe price ID (`price_*`). `null` when the field is missing on the org doc. Prefer `plan` for display."
                    },
                    "credits": {
                      "type": "object",
                      "properties": {
                        "prepaid": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Prepaid credit-pack balance (the customer's extra credits, purchased outside the subscription). Mongo field name: `extraCredits` — surfaced here under the more accurate name."
                        },
                        "subscription": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Subscription plan's monthly credit allotment remaining for the current period. Mongo field name: `freeMatches` — the legacy name is misleading; the credits are not \"free\" / \"complimentary\", they're what the customer pays for in their plan."
                        },
                        "total": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Derived convenience: `prepaid + subscription`. The total credits the org can spend right now."
                        }
                      },
                      "required": [
                        "prepaid",
                        "subscription",
                        "total"
                      ]
                    },
                    "usage": {
                      "type": "object",
                      "properties": {
                        "current_period_start": {
                          "type": "string",
                          "description": "ISO 8601 UTC timestamp for the start of the current usage period. Today this is the 1st of the current calendar month at 00:00 UTC. Future revisions may switch to per-org billing-anniversary boundaries; clients should treat the values as authoritative for the returned period."
                        },
                        "current_period_end": {
                          "type": "string",
                          "description": "ISO 8601 UTC timestamp for the end of the current usage period (exclusive). Pair-wise with current_period_start."
                        },
                        "current_period_total": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Credits consumed during the current usage period — every credit spend, matching the usage number shown in the dashboard. Sourced from Mongo `usage`; defaults to 0 if the counter is undefined on the org doc."
                        }
                      },
                      "required": [
                        "current_period_start",
                        "current_period_end",
                        "current_period_total"
                      ]
                    },
                    "trial": {
                      "type": "object",
                      "properties": {
                        "active": {
                          "type": "boolean",
                          "description": "Whether the org is currently in its free trial (Mongo `inTrial`). A trialing org otherwise looks like a paid org (it carries the paid plan name), so consumers MUST read this field to detect trial state."
                        },
                        "started_at": {
                          "type": "string",
                          "nullable": true,
                          "description": "ISO 8601 UTC timestamp the trial started (Mongo `trial_start_date`). `null` when unknown."
                        },
                        "ends_at": {
                          "type": "string",
                          "nullable": true,
                          "description": "ISO 8601 UTC end of the trial. Reads the persisted Mongo `trial_end_date` when present (the value the dashboard shows); for legacy docs that predate that field it is derived as started_at + 7 days (trials run 7 days). `null` when the org is not in a trial."
                        }
                      },
                      "required": [
                        "active",
                        "started_at",
                        "ends_at"
                      ],
                      "description": "Free-trial state. Additive (2026-06)."
                    },
                    "created_at": {
                      "type": "string",
                      "description": "ISO 8601 UTC timestamp of org creation."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "suspended",
                        "paused",
                        "banned",
                        "under_review",
                        "needs_identity_verification"
                      ],
                      "description": "Coarse organization state (read-only, server-managed). `active` = good standing. Each non-active state means API work requests are denied with 403: `suspended` / `paused` (billing/admin holds), `banned` (abuse), `under_review`, `needs_identity_verification`. When several internal flags are set, the most severe is reported (precedence: suspended > paused > under_review > banned > needs_identity_verification, mirroring the dashboard). `needs_identity_verification` is reported when the org is flagged for identity review or the account owner is under a payment-review identity hold."
                    },
                    "entitlements": {
                      "type": "object",
                      "properties": {
                        "webhooks": {
                          "type": "boolean",
                          "description": "Outbound webhook subscriptions — native on Business/Pro, or via the Webhooks add-on."
                        },
                        "instafloss": {
                          "type": "boolean",
                          "description": "Instafloss real-time verification — native on Business/Pro, or via the Instafloss add-on."
                        },
                        "typo_fixer": {
                          "type": "boolean",
                          "description": "Automatic typo/misspelling correction — native on Business/Pro, or via the Fix-typos add-on."
                        },
                        "exclusions": {
                          "type": "boolean",
                          "description": "Auto-action exclusions — native on Business/Pro, or via the Auto-action-exclusions add-on."
                        },
                        "update_tags": {
                          "type": "boolean",
                          "description": "Update custom fields / tags — native on Pro, or via the Auto-update-attributes add-on."
                        },
                        "sso": {
                          "type": "boolean",
                          "description": "Single Sign-On — Pro-exclusive (no add-on)."
                        },
                        "autofloss": {
                          "type": "boolean",
                          "description": "Autofloss scheduled auto-import — any paid plan."
                        },
                        "decay_basic": {
                          "type": "boolean",
                          "description": "Decay Protection — any paid plan."
                        },
                        "decay_custom": {
                          "type": "boolean",
                          "description": "Custom Decay Protection schedules — the Custom-Decay add-on only."
                        }
                      },
                      "required": [
                        "webhooks",
                        "instafloss",
                        "typo_fixer",
                        "exclusions",
                        "update_tags",
                        "sso",
                        "autofloss",
                        "decay_basic",
                        "decay_custom"
                      ],
                      "description": "Feature entitlements computed from the org’s plan tier and active add-ons — the authoritative \"what can this account use\" map (a feature is true if native to the tier OR purchased as an add-on). Read-only; managed via billing/support. Additive (2026-06)."
                    }
                  },
                  "required": [
                    "name",
                    "email",
                    "plan",
                    "plan_id",
                    "credits",
                    "usage",
                    "trial",
                    "created_at",
                    "status",
                    "entitlements"
                  ],
                  "description": "Read-only single-resource bare-object body (per v1_contract.md) — there is no PATCH for the organization; `status` and `credits` are server-managed. Team seats are not included here; retrieve the roster via `GET /v1/users`. Clients MUST tolerate unknown additive sibling fields — future additions (e.g. `team`, `webhooks_url`) ship as additive non-breaking changes."
                },
                "example": {
                  "name": "Acme Corp",
                  "email": "jane@acme.com",
                  "plan": "pro",
                  "plan_id": "price_1A2b3C4d5E6f7G8h",
                  "credits": {
                    "prepaid": 10000,
                    "subscription": 5000,
                    "total": 15000
                  },
                  "usage": {
                    "current_period_start": "2026-06-01T00:00:00Z",
                    "current_period_end": "2026-07-01T00:00:00Z",
                    "current_period_total": 2400
                  },
                  "trial": {
                    "active": false,
                    "started_at": null,
                    "ends_at": null
                  },
                  "created_at": "2026-01-15T09:00:00Z",
                  "status": "active",
                  "entitlements": {
                    "webhooks": true,
                    "instafloss": true,
                    "typo_fixer": true,
                    "exclusions": true,
                    "update_tags": true,
                    "sso": true,
                    "autofloss": true,
                    "decay_basic": true,
                    "decay_custom": false
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations": {
      "get": {
        "summary": "List all integrations (connected + disconnected)",
        "description": "Returns the full set of 40 ESP integrations, with `connected: true` for those the org has actively connected. Each entry carries `connection_count` and a deterministic `primary` summary because `(org, type)` is 1:N — customers can connect multiple accounts of the same ESP. `primary` is the connection with the lowest `createdAt` (or `null` when `connection_count: 0`); use it for chatbot shortcuts like \"is X connected?\" without a follow-up DETAIL request. Always paginated with a cursor envelope, even though the underlying set is bounded — so SDKs and chatbots use one pagination code path across every collection endpoint. Default per_page = 10, max 100. Cursor is opaque (base64url JSON); clients MUST NOT parse it. Sort order: connected ESPs first (alphabetical by type), then disconnected.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Integer in [1, 100]; default 10. Out of range → 400 invalid_request."
            },
            "required": false,
            "description": "Integer in [1, 100]; default 10. Out of range → 400 invalid_request.",
            "name": "per_page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque pagination cursor from a previous response's `pagination.next_cursor`. Unparseable cursor → 400 invalid_request."
            },
            "required": false,
            "description": "Opaque pagination cursor from a previous response's `pagination.next_cursor`. Unparseable cursor → 400 invalid_request.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated integration summaries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string",
                            "description": "ESP slug (one of the 40 in INTEGRATION_TYPES). Stable identifier for the integration."
                          },
                          "connected": {
                            "type": "boolean",
                            "description": "Whether this org has at least one active connection for this ESP. Equivalent to `connection_count > 0`."
                          },
                          "connection_count": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Number of active connections this org has for the ESP. `(org, type)` is 1:N — customers can connect multiple accounts of the same ESP."
                          },
                          "primary": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              }
                            },
                            "required": [
                              "id",
                              "name"
                            ],
                            "description": "Deterministic primary-connection summary. Selected as the connection with the lowest `createdAt`. `null` when no active connections exist."
                          },
                          "disconnected_count": {
                            "type": "integer",
                            "minimum": 0,
                            "description": "Number of connections for this ESP that have been DISCONNECTED (soft-disabled via the dashboard or the API) and are no longer syncing. NOT counted in `connection_count`; `connected` stays based on active connections only. A non-zero value with `connected: false` means \"this ESP was set up before but is currently disconnected\" — surface that instead of claiming it was never connected. Defaults to 0."
                          }
                        },
                        "required": [
                          "type",
                          "connected",
                          "connection_count",
                          "primary",
                          "disconnected_count"
                        ]
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Opaque cursor for the next page. `null` when has_more is false. Clients MUST NOT parse this string; the server may change the encoding."
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether more pages remain after this one."
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "type": "mailchimp",
                      "connected": true,
                      "connection_count": 1,
                      "primary": {
                        "id": "conn_5d4c3b2a1e0f9z",
                        "name": "Main Marketing List"
                      },
                      "disconnected_count": 0
                    },
                    {
                      "type": "klaviyo",
                      "connected": false,
                      "connection_count": 0,
                      "primary": null,
                      "disconnected_count": 0
                    }
                  ],
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}": {
      "get": {
        "summary": "Read one integration with all of its connections",
        "description": "Returns the per-type universal fields (`type`, `connected`, `connection_count`) plus the full `connections[]` array for the integration `type`. `(org, type)` is 1:N: a customer can connect multiple accounts of the same ESP (e.g. two Mailchimp accounts for different brands); each entry in `connections[]` is one connection with its own stable `id`, customer-set `name`, `status`, and per-connection settings. Connections are ordered by `createdAt` asc (oldest first); `connections[0]` matches the `primary` returned by the LIST endpoint. When the org has no active connections for this type, `connected: false` + `connection_count: 0` + `connections: []` — the response shape stays symmetric so chatbots don't need try/catch on 404 to ask \"is X connected?\". Unknown `type` (not in the closed ESP enum, including hidden internal types like `csv`/`api`/`input`/`zapier`) → 404 not_found.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Integration detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "connected": {
                      "type": "boolean"
                    },
                    "connection_count": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "connections": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Connection-stable identifier (flossSettings doc _id). Use this to address writes once the future PATCH/POST/DELETE endpoints ship at /v1/integrations/{type}/connections/{id}."
                          },
                          "name": {
                            "type": "string",
                            "description": "Customer-set display name from `flossSettings.name`. Falls back to the registry display name (e.g. \"Mailchimp\") when the doc has no name."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "disconnected"
                            ],
                            "description": "Connection state for a single connection. \"active\" = a syncing connection in `connections[]` (`disabled: { $ne: true }`). \"disconnected\" = a soft-disabled connection in `disconnected_connections[]` — `disabled: true` with a deliberate-disconnect reason (dashboard or API); retained but not syncing, and NOT counted in `connection_count`. (Distinct from the legacy `disconnected: true` flag, which is excluded from the response entirely.) Richer states (needs_reauth, error) are reserved for future additive releases once per-ESP detection ships."
                          },
                          "created_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC timestamp from `flossSettings.createdAt`."
                          },
                          "last_synced_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC timestamp of the most recent sync. `null` in PR C — per-ESP source mapping ships additively in a later PR."
                          },
                          "settings": {
                            "type": "object",
                            "properties": {
                              "aggressiveness": {
                                "type": "string",
                                "enum": [
                                  "normal",
                                  "aggressive",
                                  "custom"
                                ],
                                "description": "Floss aggressiveness mode. \"normal\" = recommended preset, \"aggressive\" = all checks on, \"custom\" = per-check control (also returned for any connection not in a preset mode)."
                              },
                              "manual_mode": {
                                "type": "boolean",
                                "description": "Manual Mode. When enabled, addresses that fail verification are held for you to review and action in the dashboard instead of being auto-removed. When disabled, failures are handled automatically."
                              },
                              "autofloss": {
                                "type": "boolean",
                                "description": "Whether Autofloss (scheduled daily auto-import + clean) is enabled for this connection. Always `false` for integrations that do not support Autofloss (e.g. beehiiv, clickfunnels). Set via PATCH `autofloss`."
                              },
                              "decay_protection": {
                                "type": "boolean",
                                "description": "Whether Decay Protection (periodic re-verification of aging contacts) is enabled for this connection. Set via PATCH `decay_protection`."
                              },
                              "instafloss": {
                                "type": "boolean",
                                "description": "Whether Instafloss (real-time verification of new contacts) is enabled for this connection. Separate from Autofloss. Read-only for now (toggling Instafloss ships later). (Stored internally as Superfloss.)"
                              },
                              "action": {
                                "type": "string",
                                "nullable": true,
                                "enum": [
                                  "unsubscribe",
                                  "delete",
                                  "update_tags",
                                  "update_custom_fields",
                                  "do_nothing"
                                ],
                                "description": "What mailfloss DOES with invalid emails on this connection — the dashboard's \"Auto-…\" action choice (stored as flossType). `null` when unset or unrecognized. Read-only: changing it is a dashboard action. Additive (2026-06)."
                              },
                              "checks": {
                                "type": "object",
                                "properties": {
                                  "disposable": {
                                    "type": "boolean"
                                  },
                                  "nonexistent": {
                                    "type": "boolean"
                                  },
                                  "deactivated": {
                                    "type": "boolean"
                                  },
                                  "inbox_full": {
                                    "type": "boolean"
                                  },
                                  "bounced": {
                                    "type": "boolean"
                                  },
                                  "banned_words": {
                                    "type": "boolean"
                                  },
                                  "role_based": {
                                    "type": "boolean"
                                  },
                                  "complainers": {
                                    "type": "boolean"
                                  },
                                  "spam": {
                                    "type": "boolean"
                                  },
                                  "failed_exchange": {
                                    "type": "boolean"
                                  },
                                  "unverified": {
                                    "type": "boolean"
                                  },
                                  "accept_all": {
                                    "type": "boolean"
                                  }
                                },
                                "required": [
                                  "disposable",
                                  "nonexistent",
                                  "deactivated",
                                  "inbox_full",
                                  "bounced",
                                  "banned_words",
                                  "role_based",
                                  "complainers",
                                  "spam",
                                  "failed_exchange",
                                  "unverified",
                                  "accept_all"
                                ],
                                "description": "The 12 floss check gates. `true` means emails matching that check are removed. In normal/aggressive mode these reflect the preset; in custom mode they reflect the customer's choices."
                              },
                              "blacklist_count": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Number of per-connection blacklist keyword rules (always-remove). The rules themselves are read via a dedicated endpoint (follow-up)."
                              },
                              "whitelist_count": {
                                "type": "integer",
                                "minimum": 0,
                                "description": "Number of per-connection whitelist keyword rules (never-remove)."
                              }
                            },
                            "required": [
                              "aggressiveness",
                              "manual_mode",
                              "autofloss",
                              "decay_protection",
                              "instafloss",
                              "action",
                              "checks",
                              "blacklist_count",
                              "whitelist_count"
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "status",
                          "created_at",
                          "last_synced_at",
                          "settings"
                        ]
                      }
                    },
                    "disconnected_connections": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Connection-stable identifier (flossSettings doc _id)."
                          },
                          "name": {
                            "type": "string",
                            "description": "Customer-set display name, or the registry fallback."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "disconnected"
                            ]
                          },
                          "created_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC `flossSettings.createdAt`."
                          },
                          "disconnected_at": {
                            "type": "string",
                            "nullable": true,
                            "description": "ISO 8601 UTC timestamp the connection was disconnected (`flossSettings.disabledAt`)."
                          }
                        },
                        "required": [
                          "id",
                          "name",
                          "status",
                          "created_at",
                          "disconnected_at"
                        ],
                        "description": "A previously-connected, now-disconnected connection. `disconnected_at` is when it was disconnected. Disconnect does NOT imply bad credentials — it is a deliberate soft-disable; credential validity is a separate signal."
                      },
                      "description": "Connections of this ESP that were disconnected (no longer syncing). Separate from active `connections[]`; empty when none. Lets a caller distinguish \"never connected\" from \"connected before, now disconnected\"."
                    }
                  },
                  "required": [
                    "type",
                    "connected",
                    "connection_count",
                    "connections",
                    "disconnected_connections"
                  ]
                },
                "example": {
                  "type": "mailchimp",
                  "connected": true,
                  "connection_count": 1,
                  "connections": [
                    {
                      "id": "conn_5d4c3b2a1e0f9z",
                      "name": "Main Marketing List",
                      "status": "active",
                      "created_at": "2026-02-10T14:20:00Z",
                      "last_synced_at": null,
                      "settings": {
                        "aggressiveness": "normal",
                        "manual_mode": false,
                        "autofloss": true,
                        "decay_protection": true,
                        "instafloss": false,
                        "action": "unsubscribe",
                        "checks": {
                          "disposable": true,
                          "nonexistent": true,
                          "deactivated": true,
                          "inbox_full": true,
                          "bounced": true,
                          "banned_words": true,
                          "role_based": true,
                          "complainers": true,
                          "spam": true,
                          "failed_exchange": true,
                          "unverified": true,
                          "accept_all": false
                        },
                        "blacklist_count": 5,
                        "whitelist_count": 2
                      }
                    }
                  ],
                  "disconnected_connections": [
                    {
                      "id": "conn_4c3b2a1e0f9z8y",
                      "name": "Old Account",
                      "status": "disconnected",
                      "created_at": "2025-11-05T09:15:00Z",
                      "disconnected_at": "2026-05-22T16:45:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}/connections": {
      "post": {
        "summary": "Connect a new connection (with credentials)",
        "description": "Creates a brand-new connection for the ESP `type` with fresh credentials. The credentials shape depends on `type`; the single-key ESPs currently connectable via the API (Klaviyo, MailerLite) take `{ \"credentials\": { \"api_key\": \"...\" }, \"name\"?: \"...\" }`. The key is validated against the ESP live before anything is stored (a rejected key → `400 invalid_credentials`, nothing written), then encrypted at rest; it is never returned. On success returns `201` with the new connection in the same shape as GET /v1/integrations/{type}/connections/{id}. A valid ESP slug that cannot be connected via the API yet (OAuth ESPs, or ESPs not wired) → `422 unsupported_integration` (connect it in the dashboard); an unknown slug → `404`. `(org, type)` is 1:N, so this always creates a new connection. Honors `Idempotency-Key` (a retried POST with the same key replays the original `201` instead of creating a duplicate).",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum. Only connectable ESPs (Klaviyo today) succeed; others → 422."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum. Only connectable ESPs (Klaviyo today) succeed; others → 422.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress)."
            },
            "required": false,
            "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress).",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Optional display name for the connection. Defaults to the ESP display name (e.g. \"Klaviyo\", \"MailerLite\") when omitted."
                  },
                  "credentials": {
                    "anyOf": [
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Klaviyo private API key (e.g. `pk_...`). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Klaviyo credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "MailerLite API key (new-API token). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "MailerLite credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Brevo (formerly Sendinblue) API key (e.g. `xkeysib-…`). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Brevo credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "GetResponse API key (bare key — connector adds the \"api-key \" X-Auth-Token prefix). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "GetResponse credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_url": {
                            "type": "string",
                            "format": "uri",
                            "pattern": "^https:\\/\\/[a-z0-9-]+\\.(?:api-us[0-9]+\\.com|activehosted\\.com)\\/?$",
                            "description": "Your ActiveCampaign account API base URL (e.g. `https://youraccount.api-us1.com`). HTTPS only; hostname must match the AC account pattern. Encrypted at rest; never returned."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your ActiveCampaign API key (Settings → Developer). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_url",
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "ActiveCampaign credentials (account URL + API key)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "ConvertKit API key (Settings → Advanced → API). Encrypted at rest; never returned."
                          },
                          "api_secret": {
                            "type": "string",
                            "minLength": 1,
                            "description": "ConvertKit API secret (Settings → Advanced → API). Required for subscriber exports; encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key",
                          "api_secret"
                        ],
                        "additionalProperties": false,
                        "description": "ConvertKit credentials (api_key for reads + api_secret for imports — both required)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Drip API key (Settings → My User Settings → API Token). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Drip credentials. The account_id is auto-discovered from /v2/accounts during connect."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "pattern": "^[A-Za-z0-9]+-[a-z0-9]+$",
                            "description": "Mailchimp API key in `<key>-<datacenter>` format. Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Mailchimp credentials. Datacenter is parsed from the api_key suffix."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Iterable server-side API key (Integrations → API Keys). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Iterable credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Moosend API key (Settings → Account → API Key). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Moosend credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Gist Private API Key (Settings → Developer). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Gist credentials."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "api_url": {
                            "type": "string",
                            "format": "uri",
                            "pattern": "^https:\\/\\/rest\\.[a-z0-9-]+\\.braze\\.(?:com|eu)\\/?$",
                            "description": "Your Braze REST endpoint (e.g. `https://rest.iad-01.braze.com`). HTTPS only; hostname must match the Braze REST pattern. Encrypted at rest; never returned."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Braze REST API key (Settings → API Keys → REST API Keys). Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "api_url",
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Braze credentials (REST endpoint URL + API key)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "data_center": {
                            "type": "string",
                            "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$",
                            "description": "Your DailyStory data center subdomain (e.g. `us-1` for https://us-1.dailystory.com). Encrypted at rest; never returned."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your DailyStory API key. Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "data_center",
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "DailyStory credentials (data center subdomain + API key)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "site_id": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Customer.io Site ID (Account Settings → API Credentials → Track API Keys). Encrypted at rest; never returned."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Customer.io Track API Key (Account Settings → API Credentials → Track API Keys). Pairs with site_id for Basic Auth on track.customer.io. Encrypted at rest; never returned."
                          },
                          "app_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Customer.io App API Key (Account Settings → API Credentials → App API Keys). Used as the Bearer token on api.customer.io / api-eu.customer.io. Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "site_id",
                          "api_key",
                          "app_key"
                        ],
                        "additionalProperties": false,
                        "description": "Customer.io credentials (Site ID + Track API Key + App API Key — region is auto-discovered)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "subdomain": {
                            "type": "string",
                            "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$",
                            "description": "Your Sendlane account subdomain (the part before `.sendlane.com` in your dashboard URL). Stored alongside the credentials; not encrypted (it is a DNS label, not a secret)."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Sendlane v1 API key (Account → API → API Key). Used as the `api=` query parameter on https://<subdomain>.sendlane.com/api/v1/... — encrypted at rest; never returned."
                          },
                          "api_secret": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Sendlane v1 API secret / hash (Account → API → API Hash). Used as the `hash=` query parameter on https://<subdomain>.sendlane.com/api/v1/... — encrypted at rest; never returned."
                          },
                          "api_token": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Sendlane v2 API token (Account → API → API Token). Used as the Bearer token on https://api.sendlane.com/v2/... — encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "subdomain",
                          "api_key",
                          "api_secret",
                          "api_token"
                        ],
                        "additionalProperties": false,
                        "description": "Sendlane credentials (subdomain + v1 api_key/api_secret + v2 api_token)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "publication_id": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Beehiiv publication ID (Dashboard → Settings → API → the `pub_…` token next to your newsletter). Encrypted at rest; never returned."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Beehiiv API key (Dashboard → Settings → API → API Key). Used as the Bearer token on https://api.beehiiv.com/v2/... — encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "publication_id",
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "Beehiiv credentials (publication ID + API key)."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "workspace": {
                            "type": "string",
                            "pattern": "^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$",
                            "description": "Your ClickFunnels workspace subdomain (e.g. `acme` for https://acme.myclickfunnels.com). Encrypted at rest; never returned."
                          },
                          "api_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your ClickFunnels API key (Dashboard → My Profile → API Keys). Used as the Bearer token on https://<workspace>.myclickfunnels.com/api/v2/... — encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "workspace",
                          "api_key"
                        ],
                        "additionalProperties": false,
                        "description": "ClickFunnels credentials (workspace subdomain + API key). The v1 API supports only the headless API-key flow; the OAuth flow is out of scope."
                      },
                      {
                        "type": "object",
                        "properties": {
                          "url": {
                            "type": "string",
                            "description": "Your Ghost site URL — the HTTPS origin only (e.g. `https://example.ghost.io` or `https://blog.example.com`). The connector appends `/ghost/api/admin/...` itself. Stored plaintext (it is a domain, not a secret)."
                          },
                          "content_key": {
                            "type": "string",
                            "minLength": 1,
                            "description": "Your Ghost Content API key (Settings → Integrations → your integration → \"Content API Key\"). Encrypted at rest; never returned."
                          },
                          "admin_key": {
                            "type": "string",
                            "pattern": "^[0-9a-f]+:[0-9a-f]+$",
                            "description": "Your Ghost Admin API key (Settings → Integrations → your integration → \"Admin API Key\"). The format is `<id>:<secret>`, both halves lowercase hex; the secret half is hex-decoded and used to sign HS256 JWTs against your Ghost site. Encrypted at rest; never returned."
                          }
                        },
                        "required": [
                          "url",
                          "content_key",
                          "admin_key"
                        ],
                        "additionalProperties": false,
                        "description": "Ghost credentials (site URL + Content API key + Admin API key)."
                      }
                    ],
                    "description": "Per-type credentials. The accepted shape depends on the `{type}` path param — see the union members for each connectable ESP. Encrypted at rest; never returned or logged."
                  }
                },
                "required": [
                  "credentials"
                ],
                "additionalProperties": false,
                "description": "Create-a-connection request body (credentials shape varies by ESP type)."
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Connection created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Connection-stable identifier (flossSettings doc _id). Use this to address writes once the future PATCH/POST/DELETE endpoints ship at /v1/integrations/{type}/connections/{id}."
                    },
                    "name": {
                      "type": "string",
                      "description": "Customer-set display name from `flossSettings.name`. Falls back to the registry display name (e.g. \"Mailchimp\") when the doc has no name."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "disconnected"
                      ],
                      "description": "Connection state for a single connection. \"active\" = a syncing connection in `connections[]` (`disabled: { $ne: true }`). \"disconnected\" = a soft-disabled connection in `disconnected_connections[]` — `disabled: true` with a deliberate-disconnect reason (dashboard or API); retained but not syncing, and NOT counted in `connection_count`. (Distinct from the legacy `disconnected: true` flag, which is excluded from the response entirely.) Richer states (needs_reauth, error) are reserved for future additive releases once per-ESP detection ships."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp from `flossSettings.createdAt`."
                    },
                    "last_synced_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp of the most recent sync. `null` in PR C — per-ESP source mapping ships additively in a later PR."
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "aggressiveness": {
                          "type": "string",
                          "enum": [
                            "normal",
                            "aggressive",
                            "custom"
                          ],
                          "description": "Floss aggressiveness mode. \"normal\" = recommended preset, \"aggressive\" = all checks on, \"custom\" = per-check control (also returned for any connection not in a preset mode)."
                        },
                        "manual_mode": {
                          "type": "boolean",
                          "description": "Manual Mode. When enabled, addresses that fail verification are held for you to review and action in the dashboard instead of being auto-removed. When disabled, failures are handled automatically."
                        },
                        "autofloss": {
                          "type": "boolean",
                          "description": "Whether Autofloss (scheduled daily auto-import + clean) is enabled for this connection. Always `false` for integrations that do not support Autofloss (e.g. beehiiv, clickfunnels). Set via PATCH `autofloss`."
                        },
                        "decay_protection": {
                          "type": "boolean",
                          "description": "Whether Decay Protection (periodic re-verification of aging contacts) is enabled for this connection. Set via PATCH `decay_protection`."
                        },
                        "instafloss": {
                          "type": "boolean",
                          "description": "Whether Instafloss (real-time verification of new contacts) is enabled for this connection. Separate from Autofloss. Read-only for now (toggling Instafloss ships later). (Stored internally as Superfloss.)"
                        },
                        "action": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "unsubscribe",
                            "delete",
                            "update_tags",
                            "update_custom_fields",
                            "do_nothing"
                          ],
                          "description": "What mailfloss DOES with invalid emails on this connection — the dashboard's \"Auto-…\" action choice (stored as flossType). `null` when unset or unrecognized. Read-only: changing it is a dashboard action. Additive (2026-06)."
                        },
                        "checks": {
                          "type": "object",
                          "properties": {
                            "disposable": {
                              "type": "boolean"
                            },
                            "nonexistent": {
                              "type": "boolean"
                            },
                            "deactivated": {
                              "type": "boolean"
                            },
                            "inbox_full": {
                              "type": "boolean"
                            },
                            "bounced": {
                              "type": "boolean"
                            },
                            "banned_words": {
                              "type": "boolean"
                            },
                            "role_based": {
                              "type": "boolean"
                            },
                            "complainers": {
                              "type": "boolean"
                            },
                            "spam": {
                              "type": "boolean"
                            },
                            "failed_exchange": {
                              "type": "boolean"
                            },
                            "unverified": {
                              "type": "boolean"
                            },
                            "accept_all": {
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "disposable",
                            "nonexistent",
                            "deactivated",
                            "inbox_full",
                            "bounced",
                            "banned_words",
                            "role_based",
                            "complainers",
                            "spam",
                            "failed_exchange",
                            "unverified",
                            "accept_all"
                          ],
                          "description": "The 12 floss check gates. `true` means emails matching that check are removed. In normal/aggressive mode these reflect the preset; in custom mode they reflect the customer's choices."
                        },
                        "blacklist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection blacklist keyword rules (always-remove). The rules themselves are read via a dedicated endpoint (follow-up)."
                        },
                        "whitelist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection whitelist keyword rules (never-remove)."
                        }
                      },
                      "required": [
                        "aggressiveness",
                        "manual_mode",
                        "autofloss",
                        "decay_protection",
                        "instafloss",
                        "action",
                        "checks",
                        "blacklist_count",
                        "whitelist_count"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "status",
                    "created_at",
                    "last_synced_at",
                    "settings"
                  ]
                },
                "example": {
                  "id": "conn_5d4c3b2a1e0f9z",
                  "name": "Main Marketing List",
                  "status": "active",
                  "created_at": "2026-02-10T14:20:00Z",
                  "last_synced_at": null,
                  "settings": {
                    "aggressiveness": "normal",
                    "manual_mode": false,
                    "autofloss": true,
                    "decay_protection": true,
                    "instafloss": false,
                    "action": "unsubscribe",
                    "checks": {
                      "disposable": true,
                      "nonexistent": true,
                      "deactivated": true,
                      "inbox_full": true,
                      "bounced": true,
                      "banned_words": true,
                      "role_based": true,
                      "complainers": true,
                      "spam": true,
                      "failed_exchange": true,
                      "unverified": true,
                      "accept_all": false
                    },
                    "blacklist_count": 5,
                    "whitelist_count": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reuse while an earlier request with the same key is still in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Unprocessable — either an Idempotency-Key fingerprint mismatch or an unsupported integration type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "examples": {
                  "fingerprint_mismatch": {
                    "summary": "Idempotency-Key reused with a different body",
                    "value": {
                      "error": {
                        "code": "idempotency_key_reused",
                        "message": "This Idempotency-Key was already used with a different request body.",
                        "type": "invalid_request_error",
                        "request_id": "req_1a2b3c4d5e6f"
                      }
                    }
                  },
                  "unsupported_integration": {
                    "summary": "ESP not connectable via the API yet",
                    "value": {
                      "error": {
                        "code": "unsupported_integration",
                        "message": "This integration type cannot be managed via the API yet — connect it in the dashboard.",
                        "type": "invalid_request_error",
                        "request_id": "req_1a2b3c4d5e6f"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}/connections/{id}": {
      "get": {
        "summary": "Read one connection",
        "description": "Returns a single connection by id — the same per-connection shape as one entry of GET /v1/integrations/{type}'s `connections[]` (stable `id`, `name`, `status`, `created_at`, `last_synced_at`, the universal `settings` object, and the optional per-type extension). A connection `id` that does not exist or belongs to another org → 404 not_found.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Connection detail",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Connection-stable identifier (flossSettings doc _id). Use this to address writes once the future PATCH/POST/DELETE endpoints ship at /v1/integrations/{type}/connections/{id}."
                    },
                    "name": {
                      "type": "string",
                      "description": "Customer-set display name from `flossSettings.name`. Falls back to the registry display name (e.g. \"Mailchimp\") when the doc has no name."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "disconnected"
                      ],
                      "description": "Connection state for a single connection. \"active\" = a syncing connection in `connections[]` (`disabled: { $ne: true }`). \"disconnected\" = a soft-disabled connection in `disconnected_connections[]` — `disabled: true` with a deliberate-disconnect reason (dashboard or API); retained but not syncing, and NOT counted in `connection_count`. (Distinct from the legacy `disconnected: true` flag, which is excluded from the response entirely.) Richer states (needs_reauth, error) are reserved for future additive releases once per-ESP detection ships."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp from `flossSettings.createdAt`."
                    },
                    "last_synced_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp of the most recent sync. `null` in PR C — per-ESP source mapping ships additively in a later PR."
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "aggressiveness": {
                          "type": "string",
                          "enum": [
                            "normal",
                            "aggressive",
                            "custom"
                          ],
                          "description": "Floss aggressiveness mode. \"normal\" = recommended preset, \"aggressive\" = all checks on, \"custom\" = per-check control (also returned for any connection not in a preset mode)."
                        },
                        "manual_mode": {
                          "type": "boolean",
                          "description": "Manual Mode. When enabled, addresses that fail verification are held for you to review and action in the dashboard instead of being auto-removed. When disabled, failures are handled automatically."
                        },
                        "autofloss": {
                          "type": "boolean",
                          "description": "Whether Autofloss (scheduled daily auto-import + clean) is enabled for this connection. Always `false` for integrations that do not support Autofloss (e.g. beehiiv, clickfunnels). Set via PATCH `autofloss`."
                        },
                        "decay_protection": {
                          "type": "boolean",
                          "description": "Whether Decay Protection (periodic re-verification of aging contacts) is enabled for this connection. Set via PATCH `decay_protection`."
                        },
                        "instafloss": {
                          "type": "boolean",
                          "description": "Whether Instafloss (real-time verification of new contacts) is enabled for this connection. Separate from Autofloss. Read-only for now (toggling Instafloss ships later). (Stored internally as Superfloss.)"
                        },
                        "action": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "unsubscribe",
                            "delete",
                            "update_tags",
                            "update_custom_fields",
                            "do_nothing"
                          ],
                          "description": "What mailfloss DOES with invalid emails on this connection — the dashboard's \"Auto-…\" action choice (stored as flossType). `null` when unset or unrecognized. Read-only: changing it is a dashboard action. Additive (2026-06)."
                        },
                        "checks": {
                          "type": "object",
                          "properties": {
                            "disposable": {
                              "type": "boolean"
                            },
                            "nonexistent": {
                              "type": "boolean"
                            },
                            "deactivated": {
                              "type": "boolean"
                            },
                            "inbox_full": {
                              "type": "boolean"
                            },
                            "bounced": {
                              "type": "boolean"
                            },
                            "banned_words": {
                              "type": "boolean"
                            },
                            "role_based": {
                              "type": "boolean"
                            },
                            "complainers": {
                              "type": "boolean"
                            },
                            "spam": {
                              "type": "boolean"
                            },
                            "failed_exchange": {
                              "type": "boolean"
                            },
                            "unverified": {
                              "type": "boolean"
                            },
                            "accept_all": {
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "disposable",
                            "nonexistent",
                            "deactivated",
                            "inbox_full",
                            "bounced",
                            "banned_words",
                            "role_based",
                            "complainers",
                            "spam",
                            "failed_exchange",
                            "unverified",
                            "accept_all"
                          ],
                          "description": "The 12 floss check gates. `true` means emails matching that check are removed. In normal/aggressive mode these reflect the preset; in custom mode they reflect the customer's choices."
                        },
                        "blacklist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection blacklist keyword rules (always-remove). The rules themselves are read via a dedicated endpoint (follow-up)."
                        },
                        "whitelist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection whitelist keyword rules (never-remove)."
                        }
                      },
                      "required": [
                        "aggressiveness",
                        "manual_mode",
                        "autofloss",
                        "decay_protection",
                        "instafloss",
                        "action",
                        "checks",
                        "blacklist_count",
                        "whitelist_count"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "status",
                    "created_at",
                    "last_synced_at",
                    "settings"
                  ]
                },
                "example": {
                  "id": "conn_5d4c3b2a1e0f9z",
                  "name": "Main Marketing List",
                  "status": "active",
                  "created_at": "2026-02-10T14:20:00Z",
                  "last_synced_at": null,
                  "settings": {
                    "aggressiveness": "normal",
                    "manual_mode": false,
                    "autofloss": true,
                    "decay_protection": true,
                    "instafloss": false,
                    "action": "unsubscribe",
                    "checks": {
                      "disposable": true,
                      "nonexistent": true,
                      "deactivated": true,
                      "inbox_full": true,
                      "bounced": true,
                      "banned_words": true,
                      "role_based": true,
                      "complainers": true,
                      "spam": true,
                      "failed_exchange": true,
                      "unverified": true,
                      "accept_all": false
                    },
                    "blacklist_count": 5,
                    "whitelist_count": 2
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "patch": {
        "summary": "Update a connection's settings",
        "description": "Partial update (JSON merge-patch — omitted fields are left unchanged) of a connection's cleansing settings. Body may set any of: `manual_mode` (boolean; mode-independent), `autofloss` / `decay_protection` (paid-plan booleans — 403 on the free tier), `aggressiveness` (`normal`/`aggressive` cascade canonical values to all 12 check gates, `custom` detaches from a preset without changing gates), and `checks` (a partial map of the 12 gates — editing ANY gate switches `aggressiveness` to `custom`). `aggressiveness` and `checks` are mutually exclusive in one request (editing checks already implies custom) → 400 if both are sent. At least one field is required. Returns `200` with the refreshed connection (same shape as the GET). A connection `id` that does not exist or belongs to another org → 404. PATCH is idempotent; it does not consume the `Idempotency-Key` header.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "manual_mode": {
                    "type": "boolean",
                    "description": "Toggle Manual Mode for this connection. When enabled, addresses that fail verification are held for you to review/action in the dashboard instead of being auto-removed. Mode-independent — does not change the aggressiveness preset."
                  },
                  "aggressiveness": {
                    "type": "string",
                    "enum": [
                      "normal",
                      "aggressive",
                      "custom"
                    ],
                    "description": "Set the aggressiveness preset. \"normal\"/\"aggressive\" cascade canonical values to all 12 gates; \"custom\" detaches from a preset without changing gate values. Mutually exclusive with `checks` in one request."
                  },
                  "checks": {
                    "type": "object",
                    "properties": {
                      "disposable": {
                        "type": "boolean"
                      },
                      "nonexistent": {
                        "type": "boolean"
                      },
                      "deactivated": {
                        "type": "boolean"
                      },
                      "inbox_full": {
                        "type": "boolean"
                      },
                      "bounced": {
                        "type": "boolean"
                      },
                      "banned_words": {
                        "type": "boolean"
                      },
                      "role_based": {
                        "type": "boolean"
                      },
                      "complainers": {
                        "type": "boolean"
                      },
                      "spam": {
                        "type": "boolean"
                      },
                      "failed_exchange": {
                        "type": "boolean"
                      },
                      "unverified": {
                        "type": "boolean"
                      },
                      "accept_all": {
                        "type": "boolean"
                      }
                    },
                    "additionalProperties": false,
                    "description": "Partial set of the 12 floss check gates to update. Each is optional; omitted gates keep their current value. Editing ANY gate switches the connection to `custom` aggressiveness. Unknown gate names are rejected."
                  },
                  "autofloss": {
                    "type": "boolean",
                    "description": "Toggle Autofloss (scheduled daily auto-import + clean) for this connection. Paid-plan feature: returns 403 on the free tier, and 422 for integrations that do not support Autofloss (e.g. beehiiv, clickfunnels). The resulting state reads back as `settings.autofloss`; Instafloss is the separate `settings.instafloss` flag."
                  },
                  "decay_protection": {
                    "type": "boolean",
                    "description": "Toggle Decay Protection (periodic re-verification of aging contacts) for this connection. Paid-plan feature: returns 403 on the free tier. Enabling resets any custom decay scheduling to the default. Reads back as `settings.decay_protection`."
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "unsubscribe",
                      "delete",
                      "do_nothing",
                      "update_custom_fields"
                    ],
                    "description": "Set the auto-action mailfloss takes on invalid emails for this connection: `delete` (remove), `unsubscribe`, `do_nothing` (scan only), or `update_custom_fields` (auto-update the contact’s custom fields). The first three are not plan-gated; `update_custom_fields` is the \"Auto-Update Attributes\" feature and returns 403 unless the plan includes it (Pro) or the Auto-Update Attributes add-on is active. Returns 422 for ESPs that cannot perform the chosen action (e.g. `delete` on ConvertKit / MailerLite / Klaviyo / Moosend / Remarkety; `unsubscribe` on ClickFunnels). The auto-action and Manual Mode are INDEPENDENT settings (as in the dashboard): setting an action leaves `manual_mode` unchanged, and the two may be sent together. The read shape additionally exposes `update_tags` (dashboard-only, not writable). Reads back as `settings.action`."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated connection",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Connection-stable identifier (flossSettings doc _id). Use this to address writes once the future PATCH/POST/DELETE endpoints ship at /v1/integrations/{type}/connections/{id}."
                    },
                    "name": {
                      "type": "string",
                      "description": "Customer-set display name from `flossSettings.name`. Falls back to the registry display name (e.g. \"Mailchimp\") when the doc has no name."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "disconnected"
                      ],
                      "description": "Connection state for a single connection. \"active\" = a syncing connection in `connections[]` (`disabled: { $ne: true }`). \"disconnected\" = a soft-disabled connection in `disconnected_connections[]` — `disabled: true` with a deliberate-disconnect reason (dashboard or API); retained but not syncing, and NOT counted in `connection_count`. (Distinct from the legacy `disconnected: true` flag, which is excluded from the response entirely.) Richer states (needs_reauth, error) are reserved for future additive releases once per-ESP detection ships."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp from `flossSettings.createdAt`."
                    },
                    "last_synced_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp of the most recent sync. `null` in PR C — per-ESP source mapping ships additively in a later PR."
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "aggressiveness": {
                          "type": "string",
                          "enum": [
                            "normal",
                            "aggressive",
                            "custom"
                          ],
                          "description": "Floss aggressiveness mode. \"normal\" = recommended preset, \"aggressive\" = all checks on, \"custom\" = per-check control (also returned for any connection not in a preset mode)."
                        },
                        "manual_mode": {
                          "type": "boolean",
                          "description": "Manual Mode. When enabled, addresses that fail verification are held for you to review and action in the dashboard instead of being auto-removed. When disabled, failures are handled automatically."
                        },
                        "autofloss": {
                          "type": "boolean",
                          "description": "Whether Autofloss (scheduled daily auto-import + clean) is enabled for this connection. Always `false` for integrations that do not support Autofloss (e.g. beehiiv, clickfunnels). Set via PATCH `autofloss`."
                        },
                        "decay_protection": {
                          "type": "boolean",
                          "description": "Whether Decay Protection (periodic re-verification of aging contacts) is enabled for this connection. Set via PATCH `decay_protection`."
                        },
                        "instafloss": {
                          "type": "boolean",
                          "description": "Whether Instafloss (real-time verification of new contacts) is enabled for this connection. Separate from Autofloss. Read-only for now (toggling Instafloss ships later). (Stored internally as Superfloss.)"
                        },
                        "action": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "unsubscribe",
                            "delete",
                            "update_tags",
                            "update_custom_fields",
                            "do_nothing"
                          ],
                          "description": "What mailfloss DOES with invalid emails on this connection — the dashboard's \"Auto-…\" action choice (stored as flossType). `null` when unset or unrecognized. Read-only: changing it is a dashboard action. Additive (2026-06)."
                        },
                        "checks": {
                          "type": "object",
                          "properties": {
                            "disposable": {
                              "type": "boolean"
                            },
                            "nonexistent": {
                              "type": "boolean"
                            },
                            "deactivated": {
                              "type": "boolean"
                            },
                            "inbox_full": {
                              "type": "boolean"
                            },
                            "bounced": {
                              "type": "boolean"
                            },
                            "banned_words": {
                              "type": "boolean"
                            },
                            "role_based": {
                              "type": "boolean"
                            },
                            "complainers": {
                              "type": "boolean"
                            },
                            "spam": {
                              "type": "boolean"
                            },
                            "failed_exchange": {
                              "type": "boolean"
                            },
                            "unverified": {
                              "type": "boolean"
                            },
                            "accept_all": {
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "disposable",
                            "nonexistent",
                            "deactivated",
                            "inbox_full",
                            "bounced",
                            "banned_words",
                            "role_based",
                            "complainers",
                            "spam",
                            "failed_exchange",
                            "unverified",
                            "accept_all"
                          ],
                          "description": "The 12 floss check gates. `true` means emails matching that check are removed. In normal/aggressive mode these reflect the preset; in custom mode they reflect the customer's choices."
                        },
                        "blacklist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection blacklist keyword rules (always-remove). The rules themselves are read via a dedicated endpoint (follow-up)."
                        },
                        "whitelist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection whitelist keyword rules (never-remove)."
                        }
                      },
                      "required": [
                        "aggressiveness",
                        "manual_mode",
                        "autofloss",
                        "decay_protection",
                        "instafloss",
                        "action",
                        "checks",
                        "blacklist_count",
                        "whitelist_count"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "status",
                    "created_at",
                    "last_synced_at",
                    "settings"
                  ]
                },
                "example": {
                  "id": "conn_5d4c3b2a1e0f9z",
                  "name": "Main Marketing List",
                  "status": "active",
                  "created_at": "2026-02-10T14:20:00Z",
                  "last_synced_at": null,
                  "settings": {
                    "aggressiveness": "normal",
                    "manual_mode": false,
                    "autofloss": true,
                    "decay_protection": true,
                    "instafloss": false,
                    "action": "unsubscribe",
                    "checks": {
                      "disposable": true,
                      "nonexistent": true,
                      "deactivated": true,
                      "inbox_full": true,
                      "bounced": true,
                      "banned_words": true,
                      "role_based": true,
                      "complainers": true,
                      "spam": true,
                      "failed_exchange": true,
                      "unverified": true,
                      "accept_all": false
                    },
                    "blacklist_count": 5,
                    "whitelist_count": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "The integration type cannot be managed via the API yet (unsupported_integration).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unsupported_integration",
                    "message": "This integration type cannot be managed via the API yet — connect it in the dashboard.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Disconnect a connection",
        "description": "Disconnects (soft-disables) a connection. Its data and stored credentials are retained — this stops all cleansing/processing for the connection and removes it from subsequent reads (it disappears from GET /v1/integrations and GET .../connections/{id}). Returns `200` with a small confirmation `{ id, type, status: \"disconnected\", disconnected_at }`; no credential fields are returned. A connection `id` that does not exist, belongs to another org, or is already disconnected → 404 not_found (so a repeated DELETE is a safe 404). A connection with Instafloss (Superfloss) enabled is refused with `409 conflict` — its live real-time webhooks must not be left pointed at a disabled connection, so disable Instafloss first, then disconnect (mirrors the dashboard). Reconnecting a connection is a future additive endpoint.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Disconnect confirmation",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Connection-stable identifier (flossSettings doc _id) that was disconnected."
                    },
                    "type": {
                      "type": "string",
                      "description": "ESP slug the connection belonged to (one of INTEGRATION_TYPES)."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "disconnected"
                      ],
                      "description": "Always \"disconnected\". The connection is removed from active reads. A disconnect is permanent — to use this platform again, connect it anew."
                    },
                    "disconnected_at": {
                      "type": "string",
                      "description": "ISO 8601 UTC timestamp the disconnect was recorded."
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "status",
                    "disconnected_at"
                  ],
                  "description": "Confirmation returned when a connection is disconnected."
                },
                "example": {
                  "id": "conn_5d4c3b2a1e0f9z",
                  "type": "mailchimp",
                  "status": "disconnected",
                  "disconnected_at": "2026-06-20T14:30:00Z"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "The connection is in a state that must change before this operation can run — e.g. a connection with Instafloss (Superfloss) enabled cannot be disconnected until Instafloss is disabled first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "conflict",
                    "message": "This connection has Instafloss (Superfloss) enabled. Disable Instafloss before disconnecting.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}/connections/{id}/sync": {
      "post": {
        "summary": "Re-sync a connection's audiences",
        "description": "Re-runs the live list/audience fetch for a connection using its stored credentials and refreshes the connection's audiences — use it when lists were renamed/added upstream, or when the connect-time fetch came back empty. The stored credentials are validated against the ESP as part of the fetch: a rejected key → `400 invalid_credentials` and nothing is changed; an unreachable ESP → `503`. On success returns `200` with the refreshed connection (same shape as GET .../connections/{id}). A connection `id` that does not exist or belongs to another org → `404`; a connection of a type that can't be managed via the API → `422 unsupported_integration`.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Refreshed connection",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Connection-stable identifier (flossSettings doc _id). Use this to address writes once the future PATCH/POST/DELETE endpoints ship at /v1/integrations/{type}/connections/{id}."
                    },
                    "name": {
                      "type": "string",
                      "description": "Customer-set display name from `flossSettings.name`. Falls back to the registry display name (e.g. \"Mailchimp\") when the doc has no name."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "disconnected"
                      ],
                      "description": "Connection state for a single connection. \"active\" = a syncing connection in `connections[]` (`disabled: { $ne: true }`). \"disconnected\" = a soft-disabled connection in `disconnected_connections[]` — `disabled: true` with a deliberate-disconnect reason (dashboard or API); retained but not syncing, and NOT counted in `connection_count`. (Distinct from the legacy `disconnected: true` flag, which is excluded from the response entirely.) Richer states (needs_reauth, error) are reserved for future additive releases once per-ESP detection ships."
                    },
                    "created_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp from `flossSettings.createdAt`."
                    },
                    "last_synced_at": {
                      "type": "string",
                      "nullable": true,
                      "description": "ISO 8601 UTC timestamp of the most recent sync. `null` in PR C — per-ESP source mapping ships additively in a later PR."
                    },
                    "settings": {
                      "type": "object",
                      "properties": {
                        "aggressiveness": {
                          "type": "string",
                          "enum": [
                            "normal",
                            "aggressive",
                            "custom"
                          ],
                          "description": "Floss aggressiveness mode. \"normal\" = recommended preset, \"aggressive\" = all checks on, \"custom\" = per-check control (also returned for any connection not in a preset mode)."
                        },
                        "manual_mode": {
                          "type": "boolean",
                          "description": "Manual Mode. When enabled, addresses that fail verification are held for you to review and action in the dashboard instead of being auto-removed. When disabled, failures are handled automatically."
                        },
                        "autofloss": {
                          "type": "boolean",
                          "description": "Whether Autofloss (scheduled daily auto-import + clean) is enabled for this connection. Always `false` for integrations that do not support Autofloss (e.g. beehiiv, clickfunnels). Set via PATCH `autofloss`."
                        },
                        "decay_protection": {
                          "type": "boolean",
                          "description": "Whether Decay Protection (periodic re-verification of aging contacts) is enabled for this connection. Set via PATCH `decay_protection`."
                        },
                        "instafloss": {
                          "type": "boolean",
                          "description": "Whether Instafloss (real-time verification of new contacts) is enabled for this connection. Separate from Autofloss. Read-only for now (toggling Instafloss ships later). (Stored internally as Superfloss.)"
                        },
                        "action": {
                          "type": "string",
                          "nullable": true,
                          "enum": [
                            "unsubscribe",
                            "delete",
                            "update_tags",
                            "update_custom_fields",
                            "do_nothing"
                          ],
                          "description": "What mailfloss DOES with invalid emails on this connection — the dashboard's \"Auto-…\" action choice (stored as flossType). `null` when unset or unrecognized. Read-only: changing it is a dashboard action. Additive (2026-06)."
                        },
                        "checks": {
                          "type": "object",
                          "properties": {
                            "disposable": {
                              "type": "boolean"
                            },
                            "nonexistent": {
                              "type": "boolean"
                            },
                            "deactivated": {
                              "type": "boolean"
                            },
                            "inbox_full": {
                              "type": "boolean"
                            },
                            "bounced": {
                              "type": "boolean"
                            },
                            "banned_words": {
                              "type": "boolean"
                            },
                            "role_based": {
                              "type": "boolean"
                            },
                            "complainers": {
                              "type": "boolean"
                            },
                            "spam": {
                              "type": "boolean"
                            },
                            "failed_exchange": {
                              "type": "boolean"
                            },
                            "unverified": {
                              "type": "boolean"
                            },
                            "accept_all": {
                              "type": "boolean"
                            }
                          },
                          "required": [
                            "disposable",
                            "nonexistent",
                            "deactivated",
                            "inbox_full",
                            "bounced",
                            "banned_words",
                            "role_based",
                            "complainers",
                            "spam",
                            "failed_exchange",
                            "unverified",
                            "accept_all"
                          ],
                          "description": "The 12 floss check gates. `true` means emails matching that check are removed. In normal/aggressive mode these reflect the preset; in custom mode they reflect the customer's choices."
                        },
                        "blacklist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection blacklist keyword rules (always-remove). The rules themselves are read via a dedicated endpoint (follow-up)."
                        },
                        "whitelist_count": {
                          "type": "integer",
                          "minimum": 0,
                          "description": "Number of per-connection whitelist keyword rules (never-remove)."
                        }
                      },
                      "required": [
                        "aggressiveness",
                        "manual_mode",
                        "autofloss",
                        "decay_protection",
                        "instafloss",
                        "action",
                        "checks",
                        "blacklist_count",
                        "whitelist_count"
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "name",
                    "status",
                    "created_at",
                    "last_synced_at",
                    "settings"
                  ]
                },
                "example": {
                  "id": "conn_5d4c3b2a1e0f9z",
                  "name": "Main Marketing List",
                  "status": "active",
                  "created_at": "2026-02-10T14:20:00Z",
                  "last_synced_at": null,
                  "settings": {
                    "aggressiveness": "normal",
                    "manual_mode": false,
                    "autofloss": true,
                    "decay_protection": true,
                    "instafloss": false,
                    "action": "unsubscribe",
                    "checks": {
                      "disposable": true,
                      "nonexistent": true,
                      "deactivated": true,
                      "inbox_full": true,
                      "bounced": true,
                      "banned_words": true,
                      "role_based": true,
                      "complainers": true,
                      "spam": true,
                      "failed_exchange": true,
                      "unverified": true,
                      "accept_all": false
                    },
                    "blacklist_count": 5,
                    "whitelist_count": 2
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "The integration type cannot be managed via the API yet (unsupported_integration).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unsupported_integration",
                    "message": "This integration type cannot be managed via the API yet — connect it in the dashboard.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}/connections/{id}/test": {
      "post": {
        "summary": "Test a connection's credentials",
        "description": "Checks whether a connection's stored credentials still work — a live, read-only probe against the ESP that writes nothing. Returns `200` with `{ credential_valid, tested_at }`: `true` if the ESP accepted the key just now, or `false` (plus a `reason`) if it rejected them — a rejected key is still a successful test, not an error. Only an ESP we couldn't reach surfaces as `503` (the result is genuinely unknown). A connection `id` that does not exist or belongs to another org → `404`; a type that can't be managed via the API → `422 unsupported_integration`. Ideal for an \"is my integration healthy?\" check before relying on it. Do NOT send an `Idempotency-Key` with this endpoint — it is a live probe, and a replayed cached result would defeat the point (poll it fresh each time).",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Credential test result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "Connection-stable identifier (flossSettings doc _id) that was tested."
                    },
                    "type": {
                      "type": "string",
                      "description": "ESP slug the connection belongs to (one of INTEGRATION_TYPES)."
                    },
                    "credential_valid": {
                      "type": "boolean",
                      "description": "true if the stored credentials were accepted by the live ESP just now; false if the ESP rejected them."
                    },
                    "tested_at": {
                      "type": "string",
                      "description": "ISO 8601 UTC timestamp the test was performed."
                    },
                    "reason": {
                      "type": "string",
                      "description": "Present only when `credential_valid` is false — a human-readable reason from the ESP (e.g. \"The API key was rejected\"). Credential-free."
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "credential_valid",
                    "tested_at"
                  ],
                  "description": "Result of a connection credential health check."
                },
                "example": {
                  "id": "conn_5d4c3b2a1e0f9z",
                  "type": "klaviyo",
                  "credential_valid": true,
                  "tested_at": "2026-06-20T14:30:00Z"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "The integration type cannot be managed via the API yet (unsupported_integration).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "unsupported_integration",
                    "message": "This integration type cannot be managed via the API yet — connect it in the dashboard.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}/connections/{id}/keywords/{list}": {
      "get": {
        "summary": "List a connection's keyword rules",
        "description": "Returns the per-connection keyword rules for the chosen `{list}` (`blacklist` = always-remove, `whitelist` = never-remove). The universal `settings` object on GET /v1/integrations/{type} only reports the COUNTS (`blacklist_count`/`whitelist_count`); this endpoint returns the rules themselves. Each rule carries a stable `id`, the `keyword`, its `match` strategy (`exact`/`contains`), and the `applies_to` segment toggles (localpart/domain/email). Always paginated with the universal cursor envelope (default per_page = 50, max 100); the cursor is opaque — clients MUST NOT parse it. Unknown `type` or `list`, or a connection `id` that does not exist or belongs to another org, → 404 not_found.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "blacklist",
                "whitelist"
              ],
              "description": "Which keyword list: `blacklist` (matching addresses are always removed) or `whitelist` (matching addresses are never removed)."
            },
            "required": true,
            "description": "Which keyword list: `blacklist` (matching addresses are always removed) or `whitelist` (matching addresses are never removed).",
            "name": "list",
            "in": "path"
          },
          {
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                }
              ],
              "description": "Integer in [1, 100]; default 50. Out of range → 400 invalid_request."
            },
            "required": false,
            "description": "Integer in [1, 100]; default 50. Out of range → 400 invalid_request.",
            "name": "per_page",
            "in": "query"
          },
          {
            "schema": {
              "type": "string",
              "description": "Opaque pagination cursor from a previous response's `pagination.next_cursor`. Unparseable cursor → 400 invalid_request."
            },
            "required": false,
            "description": "Opaque pagination cursor from a previous response's `pagination.next_cursor`. Unparseable cursor → 400 invalid_request.",
            "name": "cursor",
            "in": "query"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated keyword rules",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Stable identifier for this rule (the keyword document _id). Use it to address the rule in DELETE .../keywords/{list}/{ruleId}."
                          },
                          "keyword": {
                            "type": "string",
                            "description": "The keyword/phrase matched against the address. Stored with spaces stripped; case is preserved as the customer entered it."
                          },
                          "match": {
                            "type": "string",
                            "enum": [
                              "exact",
                              "contains"
                            ],
                            "description": "Match strategy. \"exact\" = the targeted address segment must equal the keyword; \"contains\" = it must contain the keyword as a substring."
                          },
                          "applies_to": {
                            "type": "object",
                            "properties": {
                              "localpart": {
                                "type": "boolean",
                                "description": "Match against the local part (before the @)."
                              },
                              "domain": {
                                "type": "boolean",
                                "description": "Match against the domain (after the @)."
                              },
                              "email": {
                                "type": "boolean",
                                "description": "Match against the full email address."
                              }
                            },
                            "required": [
                              "localpart",
                              "domain",
                              "email"
                            ],
                            "description": "Which segments of an address the keyword is matched against. Mirrors the dashboard keyword-rule editor's localpart / domain / email toggles."
                          }
                        },
                        "required": [
                          "id",
                          "keyword",
                          "match",
                          "applies_to"
                        ]
                      }
                    },
                    "pagination": {
                      "type": "object",
                      "properties": {
                        "next_cursor": {
                          "type": "string",
                          "nullable": true,
                          "description": "Opaque cursor for the next page. `null` when has_more is false. Clients MUST NOT parse this string; the server may change the encoding."
                        },
                        "has_more": {
                          "type": "boolean",
                          "description": "Whether more pages remain after this one."
                        }
                      },
                      "required": [
                        "next_cursor",
                        "has_more"
                      ]
                    }
                  },
                  "required": [
                    "data",
                    "pagination"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "kw_7f8e9d0c1b2a3f4e",
                      "keyword": "noreply",
                      "match": "exact",
                      "applies_to": {
                        "localpart": true,
                        "domain": false,
                        "email": false
                      }
                    },
                    {
                      "id": "kw_6e7d8c9b0a1f2e3d",
                      "keyword": "mailinator",
                      "match": "contains",
                      "applies_to": {
                        "localpart": false,
                        "domain": true,
                        "email": false
                      }
                    }
                  ],
                  "pagination": {
                    "next_cursor": null,
                    "has_more": false
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Add keyword rules",
        "description": "Adds one or more rules to the connection's `{list}`. Body is always an array: `{ rules: [ { keyword, match, applies_to }, … ] }` (1–1000) — a single keyword is just an array of one. Each `applies_to` must enable at least one of localpart/domain/email. **All-or-nothing**: if any item is invalid the whole request is rejected `400 invalid_request` and nothing is written. Keywords are stored space-stripped and upserted on keyword (re-adding the same keyword updates the existing rule in place — no duplicate). Returns `200` with `{ data: [rules] }` (each with its stable `id`, deduped, in first-seen order). Unknown `type` or `list`, or an unowned connection, → 404 not_found. Honors `Idempotency-Key`.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "blacklist",
                "whitelist"
              ],
              "description": "Which keyword list: `blacklist` (matching addresses are always removed) or `whitelist` (matching addresses are never removed)."
            },
            "required": true,
            "description": "Which keyword list: `blacklist` (matching addresses are always removed) or `whitelist` (matching addresses are never removed).",
            "name": "list",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress)."
            },
            "required": false,
            "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress).",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "rules": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "keyword": {
                          "type": "string",
                          "minLength": 1,
                          "description": "The keyword/phrase to match. Leading/internal/trailing spaces are stripped on save; case is preserved. Must be non-empty after stripping."
                        },
                        "match": {
                          "type": "string",
                          "enum": [
                            "exact",
                            "contains"
                          ],
                          "description": "Match strategy. \"exact\" = the targeted address segment must equal the keyword; \"contains\" = it must contain the keyword as a substring."
                        },
                        "applies_to": {
                          "type": "object",
                          "properties": {
                            "localpart": {
                              "type": "boolean",
                              "description": "Match the local part (before the @). Default false."
                            },
                            "domain": {
                              "type": "boolean",
                              "description": "Match the domain (after the @). Default false."
                            },
                            "email": {
                              "type": "boolean",
                              "description": "Match the full email address. Default false."
                            }
                          },
                          "description": "Which segments to match against. Omit a segment to leave it false; at least one must be true."
                        }
                      },
                      "required": [
                        "keyword",
                        "match",
                        "applies_to"
                      ]
                    },
                    "minItems": 1,
                    "maxItems": 1000,
                    "description": "Rules to add (1–1000). Each has the same shape as the single-add body. All-or-nothing — one malformed rule rejects the whole batch."
                  }
                },
                "required": [
                  "rules"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Rules added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "description": "Stable identifier for this rule (the keyword document _id). Use it to address the rule in DELETE .../keywords/{list}/{ruleId}."
                          },
                          "keyword": {
                            "type": "string",
                            "description": "The keyword/phrase matched against the address. Stored with spaces stripped; case is preserved as the customer entered it."
                          },
                          "match": {
                            "type": "string",
                            "enum": [
                              "exact",
                              "contains"
                            ],
                            "description": "Match strategy. \"exact\" = the targeted address segment must equal the keyword; \"contains\" = it must contain the keyword as a substring."
                          },
                          "applies_to": {
                            "type": "object",
                            "properties": {
                              "localpart": {
                                "type": "boolean",
                                "description": "Match against the local part (before the @)."
                              },
                              "domain": {
                                "type": "boolean",
                                "description": "Match against the domain (after the @)."
                              },
                              "email": {
                                "type": "boolean",
                                "description": "Match against the full email address."
                              }
                            },
                            "required": [
                              "localpart",
                              "domain",
                              "email"
                            ],
                            "description": "Which segments of an address the keyword is matched against. Mirrors the dashboard keyword-rule editor's localpart / domain / email toggles."
                          }
                        },
                        "required": [
                          "id",
                          "keyword",
                          "match",
                          "applies_to"
                        ]
                      }
                    }
                  },
                  "required": [
                    "data"
                  ]
                },
                "example": {
                  "data": [
                    {
                      "id": "kw_7f8e9d0c1b2a3f4e",
                      "keyword": "noreply",
                      "match": "exact",
                      "applies_to": {
                        "localpart": true,
                        "domain": false,
                        "email": false
                      }
                    },
                    {
                      "id": "kw_6e7d8c9b0a1f2e3d",
                      "keyword": "mailinator",
                      "match": "contains",
                      "applies_to": {
                        "localpart": false,
                        "domain": true,
                        "email": false
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reuse while an earlier request with the same key is still in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Idempotency-Key was previously used with a different request body (fingerprint mismatch).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_key_reused",
                    "message": "This Idempotency-Key was already used with a different request body.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/integrations/{type}/connections/{id}/keywords/{list}/{ruleId}": {
      "delete": {
        "summary": "Delete a keyword rule",
        "description": "Removes one rule from the connection's `{list}` by its `ruleId`. Scoped to the connection — a rule id that does not exist on this connection (or belongs to another org) → 404 not_found. Returns `200` with `{ deleted: true, id }`.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "enum": [
                "activecampaign",
                "activetrail",
                "autopilot",
                "aweber",
                "beehiiv",
                "benchmark",
                "braze",
                "brevo",
                "campaigner",
                "campaignmonitor",
                "clickfunnels",
                "constantcontact",
                "convertkit",
                "customerio",
                "dailystory",
                "drip",
                "emma",
                "encharge",
                "getresponse",
                "ghost",
                "gist",
                "gohighlevel",
                "hubspot",
                "icontact",
                "infusionsoft",
                "intercom",
                "iterable",
                "klaviyo",
                "mailchimp",
                "mailerlite",
                "mailigen",
                "mailup",
                "mautic",
                "moosend",
                "ontraport",
                "remarkety",
                "sendlane",
                "sendpulse",
                "verticalresponse",
                "wishpond"
              ],
              "description": "ESP slug; one of the 40 in the closed enum."
            },
            "required": true,
            "description": "ESP slug; one of the 40 in the closed enum.",
            "name": "type",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type})."
            },
            "required": true,
            "description": "Connection id (the `id` from a connection in GET /v1/integrations/{type}).",
            "name": "id",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "enum": [
                "blacklist",
                "whitelist"
              ],
              "description": "Which keyword list: `blacklist` (matching addresses are always removed) or `whitelist` (matching addresses are never removed)."
            },
            "required": true,
            "description": "Which keyword list: `blacklist` (matching addresses are always removed) or `whitelist` (matching addresses are never removed).",
            "name": "list",
            "in": "path"
          },
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "description": "Rule id (the `id` returned by the list/add endpoints)."
            },
            "required": true,
            "description": "Rule id (the `id` returned by the list/add endpoints).",
            "name": "ruleId",
            "in": "path"
          }
        ],
        "responses": {
          "200": {
            "description": "Rule deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "deleted",
                    "id"
                  ]
                },
                "example": {
                  "deleted": true,
                  "id": "kw_7f8e9d0c1b2a3f4e"
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "404": {
            "description": "The requested resource does not exist or belongs to another organization.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "The requested resource was not found.",
                    "type": "not_found_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/erasures": {
      "post": {
        "summary": "Erase stored verification records (data privacy)",
        "description": "Data-privacy erasure API — permanently removes stored verification records for the supplied email addresses. (Renamed from /v1/delete for clarity.) See https://developers.mailfloss.com for the public docs.",
        "tags": [
          "Privacy"
        ],
        "security": [
          {
            "apiKeyHeader": []
          }
        ],
        "parameters": [
          {
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress)."
            },
            "required": false,
            "description": "Optional Stripe-style idempotency key. POST requests carrying this header are deduplicated for 24 hours: a retry with the same key returns the cached response (with header `Idempotent-Replayed: true`) instead of re-running the handler. Different request body with the same key returns 422 (fingerprint mismatch). Concurrent retries while the first is still in flight return 409 (in progress).",
            "name": "Idempotency-Key",
            "in": "header"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "emails": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Delete acknowledgement",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "success"
                      ],
                      "description": "Always `\"success\"` when the erasure request was accepted and processed. Erasure is idempotent: addresses that were not present are a no-op that still returns `\"success\"`."
                    }
                  },
                  "required": [
                    "status"
                  ]
                },
                "example": {
                  "status": "success"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request — a query or body parameter failed validation. Branch on `code`, not the message.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "A required parameter is missing or malformed.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentication failed — the API key is missing, malformed, unknown, expired, or revoked. (Expired/revoked/unknown are intentionally indistinguishable.)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "invalid_api_key",
                    "message": "Invalid API key.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the API key lacks the required scope, or the account is restricted (suspended / paused / banned).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "forbidden_scope",
                    "message": "API key does not have the required scope.",
                    "type": "authentication_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "409": {
            "description": "Idempotency-Key reuse while an earlier request with the same key is still in progress.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry shortly.",
                    "type": "conflict_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "422": {
            "description": "Idempotency-Key was previously used with a different request body (fingerprint mismatch).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "idempotency_key_reused",
                    "message": "This Idempotency-Key was already used with a different request body.",
                    "type": "invalid_request_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited — too many requests. Honor the Retry-After / X-RateLimit-* response headers.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "rate_limited",
                    "message": "Rate limit exceeded. Retry after the period indicated by the Retry-After header.",
                    "type": "rate_limit_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "500": {
            "description": "Unexpected server error or timeout.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "server_error",
                    "message": "An unexpected error occurred. Please retry.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          },
          "503": {
            "description": "Service temporarily unavailable, or an upstream dependency could not be reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                },
                "example": {
                  "error": {
                    "code": "service_unavailable",
                    "message": "The service is temporarily unavailable. Please retry shortly.",
                    "type": "api_error",
                    "request_id": "req_1a2b3c4d5e6f"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}