{
  "openapi": "3.0.3",
  "info": {
    "title": "FTK Marketing API",
    "version": "0.1.0",
    "description": "Multi-tenant email marketing over Amazon SES. Tenants authenticate with `Authorization: Bearer ftkm_...`."
  },
  "components": {
    "securitySchemes": {
      "tenantKey": {
        "type": "http",
        "scheme": "bearer"
      },
      "operatorToken": {
        "type": "http",
        "scheme": "bearer"
      },
      "partnerKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Partner key (ftkp_...). On /v1 routes also send X-FTK-Tenant: <tenant id> or ref:<external_ref>."
      }
    }
  },
  "paths": {
    "/health": {
      "get": {
        "summary": "Liveness probe",
        "tags": [
          "health"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/openapi.json": {
      "get": {
        "summary": "This document",
        "tags": [
          "openapi.json"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/v1/me": {
      "get": {
        "summary": "Current tenant and limits",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/stats": {
      "get": {
        "summary": "Bounce, complaint and volume metrics on 24h and 7d windows",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/notifications": {
      "get": {
        "summary": "Notifications addressed to the tenant (e.g. auto-pause)",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/domains": {
      "post": {
        "summary": "Register a sending domain; returns the DNS records to publish",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "get": {
        "summary": "List sending domains",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/domains/{id}/check": {
      "post": {
        "summary": "Re-check DKIM and MAIL FROM verification",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/domains/{id}/tracking": {
      "put": {
        "summary": "Set the click-tracking subdomain; returns the CNAME to publish",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/domains/{id}/tracking/check": {
      "post": {
        "summary": "Verify the tracking CNAME",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/senders": {
      "post": {
        "summary": "Create a sender on a registered domain",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "get": {
        "summary": "List senders",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/lists": {
      "post": {
        "summary": "Create a list",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "get": {
        "summary": "List lists",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/lists/{id}/import": {
      "post": {
        "summary": "Import contacts from CSV (text/csv, up to 50,000 rows, consent_text query parameter required)",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/lists/{id}/contacts": {
      "get": {
        "summary": "List contacts (limit up to 500, offset); the X-Total-Count header has the total",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "post": {
        "summary": "Bulk upsert up to 1000 contacts as JSON ({contacts:[{email,first_name,last_name,tags,consent:{text,ip}}], consent_text?, source?}); returns imported/updated/rejected. Idempotent by email: safe to retry and to split larger syncs into several calls",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/v1/lists/{id}/subscribe": {
      "post": {
        "summary": "Form subscription with double opt-in",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/suppressions": {
      "get": {
        "summary": "List suppressed addresses",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "post": {
        "summary": "Manually suppress an address",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/suppressions/{email}": {
      "delete": {
        "summary": "Remove a manual suppression",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/contacts/{email}/export": {
      "get": {
        "summary": "Export a contact's data (GDPR)",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/contacts/{email}": {
      "delete": {
        "summary": "Erase a contact's personal data and keep it suppressed",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "email",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "get": {
        "summary": "Contact details, status and the lists it belongs to",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/v1/campaigns": {
      "post": {
        "summary": "Create a campaign from html or blocks",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "get": {
        "summary": "List campaigns",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/campaigns/{id}": {
      "get": {
        "summary": "Campaign status, recipient counts, clicks and estimated opens",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      },
      "put": {
        "summary": "Update a draft campaign",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/v1/campaigns/{id}/preview": {
      "get": {
        "summary": "Rendered preview",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/campaigns/{id}/check": {
      "get": {
        "summary": "Content check: blocking errors and warnings",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/campaigns/{id}/test": {
      "post": {
        "summary": "Send a test to up to 5 addresses",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/campaigns/{id}/schedule": {
      "post": {
        "summary": "Schedule or send now",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/v1/campaigns/{id}/cancel": {
      "post": {
        "summary": "Cancel a campaign",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/u/{token}": {
      "get": {
        "summary": "Unsubscribe page",
        "tags": [
          "u"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      },
      "post": {
        "summary": "RFC 8058 one-click unsubscribe",
        "tags": [
          "u"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      }
    },
    "/c/{token}": {
      "get": {
        "summary": "Double opt-in confirmation",
        "tags": [
          "c"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      }
    },
    "/r/{token}": {
      "get": {
        "summary": "Click redirect on the tenant tracking domain",
        "tags": [
          "r"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      }
    },
    "/o/{file}": {
      "get": {
        "summary": "Open pixel on the tenant tracking domain (estimate)",
        "tags": [
          "o"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "file",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": []
      }
    },
    "/sns/ses": {
      "post": {
        "summary": "SES events via SNS; signature verified",
        "tags": [
          "sns"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/admin/tenants": {
      "get": {
        "summary": "Tenants with metrics",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "operatorToken": []
          }
        ]
      },
      "post": {
        "summary": "Create a tenant and its first API key (returned once)",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        }
      }
    },
    "/admin/tenants/{id}/{action}": {
      "post": {
        "summary": "pause, resume or suspend a tenant",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "action",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "operatorToken": []
          }
        ]
      }
    },
    "/admin/review-flags": {
      "get": {
        "summary": "Open review flags",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "operatorToken": []
          }
        ]
      }
    },
    "/admin/review-flags/{id}/resolve": {
      "post": {
        "summary": "Resolve a review flag",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "operatorToken": []
          }
        ]
      }
    },
    "/admin/blocked-domains/{domain}": {
      "put": {
        "summary": "Add a domain to the link blocklist",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "operatorToken": []
          }
        ]
      },
      "delete": {
        "summary": "Remove a domain from the blocklist",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "security": [
          {
            "operatorToken": []
          }
        ]
      }
    },
    "/v1/api-keys": {
      "post": {
        "summary": "Issue an additional API key (returned once); rotate by issuing, switching, then revoking the old one",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        }
      },
      "get": {
        "summary": "List API key prefixes",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        }
      }
    },
    "/v1/api-keys/{prefix}": {
      "delete": {
        "summary": "Revoke a key; the last active key cannot be revoked",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "parameters": [
          {
            "name": "prefix",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/auth/signup": {
      "post": {
        "summary": "Self-serve signup: creates a tenant on the trial plan and sends a verification mail",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/auth/verify": {
      "post": {
        "summary": "Confirm the email address with the emailed token",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/auth/login": {
      "post": {
        "summary": "Log in with email and password; sets the session cookie",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/auth/logout": {
      "post": {
        "summary": "End the session",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/auth/forgot": {
      "post": {
        "summary": "Request a password reset mail",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/auth/reset": {
      "post": {
        "summary": "Set a new password with the emailed token",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/auth/me": {
      "get": {
        "summary": "Current user, tenant, plan and monthly usage (session)",
        "tags": [
          "auth"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": []
      }
    },
    "/v1/plan": {
      "get": {
        "summary": "Plan and monthly usage",
        "tags": [
          "v1"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "tenantKey": []
          }
        ]
      }
    },
    "/admin/plans": {
      "get": {
        "summary": "List plans",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "operatorToken": []
          }
        ]
      }
    },
    "/admin/tenants/{id}/plan": {
      "post": {
        "summary": "Assign a plan to a tenant",
        "tags": [
          "admin"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error: {\"error\": \"<code>\"}"
          }
        },
        "security": [
          {
            "operatorToken": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ]
      }
    },
    "/v1/campaigns/{id}/preview.html": {
      "get": {
        "summary": "Rendered preview as sandboxed HTML",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "text/html"
          }
        }
      }
    },
    "/v1/domains/{id}/instructions": {
      "post": {
        "summary": "Email the DNS instructions for a domain to up to 3 recipients (sent from the operator address, replies go to the account owner)",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/plans": {
      "get": {
        "summary": "Public list of active plans (used by the product site)",
        "tags": [
          "public"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/v1/billing": {
      "get": {
        "summary": "Billing state: current plan, subscription status, plans that can be purchased",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/v1/billing/checkout": {
      "post": {
        "summary": "Start a Stripe Checkout session for a paid plan; returns the URL to redirect to",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/v1/billing/portal": {
      "post": {
        "summary": "Open the Stripe customer portal (change plan, payment method, invoices, cancel)",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/stripe/webhook": {
      "post": {
        "summary": "Stripe webhook (signature verified)",
        "tags": [
          "public"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/v1/lists/{id}/contacts/{email}": {
      "delete": {
        "summary": "Remove a contact from a list (the contact is kept; use DELETE /v1/contacts/{email} to erase)",
        "tags": [
          "v1"
        ],
        "security": [
          {
            "tenantKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/partner/v1/me": {
      "get": {
        "summary": "Partner profile, tenant count, default limits and caps",
        "tags": [
          "partner"
        ],
        "security": [
          {
            "partnerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/partner/v1/tenants": {
      "post": {
        "summary": "Create an end-customer workspace {name, external_ref?, limits?}",
        "tags": [
          "partner"
        ],
        "security": [
          {
            "partnerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      },
      "get": {
        "summary": "List the partner's tenants (limit up to 500, offset, external_ref, status); X-Total-Count has the total",
        "tags": [
          "partner"
        ],
        "security": [
          {
            "partnerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/partner/v1/tenants/{id}": {
      "get": {
        "summary": "One tenant by id or ref:<external_ref>",
        "tags": [
          "partner"
        ],
        "security": [
          {
            "partnerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      },
      "patch": {
        "summary": "Update name and limits; pause or resume (paused_manual / active). Auto-pauses and suspensions are operator-only",
        "tags": [
          "partner"
        ],
        "security": [
          {
            "partnerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/partner/v1/usage": {
      "get": {
        "summary": "Usage per tenant for a month (YYYY-MM), JSON or format=csv, for invoicing",
        "tags": [
          "partner"
        ],
        "security": [
          {
            "partnerKey": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/admin/partners": {
      "post": {
        "summary": "Create a partner and its first API key (shown once)",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      },
      "get": {
        "summary": "List partners with tenant count and this month's volume",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/admin/partners/{id}/status": {
      "post": {
        "summary": "Suspend or reactivate a partner (blocks its keys and its tenants' sending)",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/admin/partners/{id}/keys": {
      "post": {
        "summary": "Issue an additional partner API key",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/admin/partners/{id}/usage": {
      "get": {
        "summary": "Partner usage for invoicing, JSON or format=csv",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    },
    "/admin/partners/{id}/billing": {
      "patch": {
        "summary": "Set how a partner pays (postpaid or prepaid) and the monthly send cap that bounds our credit exposure",
        "tags": [
          "admin"
        ],
        "security": [
          {
            "operatorToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "4XX": {
            "description": "Error"
          }
        }
      }
    }
  }
}
