diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1ec659f7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +apps/api/openapi.json linguist-generated=true -diff diff --git a/.github/workflows/code-quality.yaml b/.github/workflows/code-quality.yaml index 872b6072..267c07d4 100644 --- a/.github/workflows/code-quality.yaml +++ b/.github/workflows/code-quality.yaml @@ -23,3 +23,4 @@ jobs: - run: bun run format:check - run: bun run build:packages - run: bun run typecheck + - run: bun --filter @sendlit/api openapi:check diff --git a/AGENTS.md b/AGENTS.md index bceb2665..27d54148 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ ## Development Tips - Don't duplicate stuff over and over. Re-use existing code and libraries. -- While making changes to the `apps/api` directory, make sure the REST API documentation and MCP server are updated as well. +- While making changes to the `apps/api` directory, make sure the REST API documentation and MCP server are updated as well. After changing the API contract, run `bun --filter @sendlit/api openapi:generate` and commit `apps/api/openapi.json` (FrontLit publishes the API reference from it; CI fails if it is stale). - For UI components, use shadcn/ui exclusively. Always use Shadcn CLI for installing components. Never hand roll standard Shadcn components. Prefer shadcn/ui components over browser-native components. - When dealing with a large change, work through it in layers: money-path correctness first, then enforcement, then dashboard/self-serve, then ops and docs. - If you are a Grok model, make sure you run the linter and tests before declaring any task done. diff --git a/apps/api/openapi.json b/apps/api/openapi.json new file mode 100644 index 00000000..6f635c1e --- /dev/null +++ b/apps/api/openapi.json @@ -0,0 +1,35069 @@ +{ + "openapi": "3.0.2", + "paths": { + "/account/deletion": { + "get": { + "description": "Dashboard session only. Lists every organization you belong to and what deleting your account does to it: `delete` when you are its last owner (it is deleted and its contacts' personal data erased), `leave` otherwise. `blocker` says what has to happen first: `active_subscription_exists` or `billing_checkout_pending` (cancel billing), `last_organization_owner` (other people are members: make one of them an owner or remove them), `billing_manager_required` (you pay for a running subscription of an organization you would leave).\n\n**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Preview deleting your account", + "tags": [ + "account" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "organizations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "action": { + "type": "string", + "enum": [ + "delete", + "leave" + ] + }, + "teamCount": { + "type": "integer" + }, + "blocker": { + "type": "string", + "enum": [ + "active_subscription_exists", + "billing_checkout_pending", + "last_organization_owner", + "billing_manager_required" + ], + "nullable": true + } + }, + "required": [ + "organizationId", + "name", + "status", + "role", + "action", + "teamCount", + "blocker" + ] + } + }, + "blocked": { + "type": "boolean" + } + }, + "required": [ + "email", + "organizations", + "blocked" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "Dashboard session only, with the account's email typed to confirm and the code from `POST /account/deletion/code` (`400 email_mismatch`, `400 invalid_code`). Fails with `409 account_deletion_blocked`, listing the blocking organizations, while any blocker from `GET /account/deletion` remains; nothing changes then. Otherwise every organization you are the last owner of is deleted like `DELETE /organizations/:organizationId` and its contacts, signup confirmations, recipient addresses and raw webhook payloads are erased (suppression hashes stay); you leave every other organization and team; your sessions, sign-in methods, OAuth tokens and consents end; and your name and email are erased. Billing records and audit events keep an anonymous id. Signing in again with the same email creates a new, empty account.\n\n**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Delete your account", + "tags": [ + "account" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "minLength": 1 + }, + "code": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "email", + "code" + ] + } + } + } + }, + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "organizations": { + "type": "array", + "items": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "action": { + "type": "string", + "enum": [ + "delete", + "leave" + ] + }, + "teamCount": { + "type": "integer" + }, + "blocker": { + "type": "string", + "enum": [ + "active_subscription_exists", + "billing_checkout_pending", + "last_organization_owner", + "billing_manager_required" + ], + "nullable": true + } + }, + "required": [ + "organizationId", + "name", + "status", + "role", + "action", + "teamCount", + "blocker" + ] + } + }, + "blocked": { + "type": "boolean" + }, + "error": { + "type": "string", + "enum": [ + "account_deletion_blocked" + ] + } + }, + "required": [ + "organizations", + "blocked", + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/account/deletion/code": { + "post": { + "description": "Dashboard session only. Sends a one-time code, valid for 5 minutes, to the account's email. `POST /account/deletion` needs it.\n\n**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Email a code to confirm deleting your account", + "tags": [ + "account" + ], + "parameters": [], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts": { + "post": { + "description": "Creates a contact (subscriber). If a contact with the same email already exists for this team, the existing contact is returned.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create a contact", + "tags": [ + "Contacts" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + } + }, + "required": [ + "email" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "subscribed": { + "type": "boolean" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsubscribeToken": { + "type": "string" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "contactId", + "email", + "subscribed", + "tags", + "unsubscribeToken", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "get": { + "description": "Returns a paginated list of contacts. Pass filter as serialized ContactFilterWithAggregator JSON for inline filtering, or segmentId to only return contacts currently matching that saved segment's filter (404 if the segment doesn't exist). SendLit supports fixed generic contact filters over first-class fields, tags, and custom fields; client-specific concepts should be synced into namespaced tags or customFields. q, filter, and segmentId combine with AND. The response's total reflects the combined filters.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List contacts", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "q", + "in": "query", + "description": "Search term matched against email or name", + "schema": { + "type": "string" + } + }, + { + "name": "segmentId", + "in": "query", + "description": "Only return contacts matching this saved segment's filter; combines with q using AND", + "schema": { + "type": "string" + } + }, + { + "name": "filter", + "in": "query", + "description": "Serialized ContactFilterWithAggregator JSON; combines with q and segmentId using AND", + "schema": { + "type": "string" + } + }, + { + "name": "excludeFilter", + "in": "query", + "description": "Serialized ContactFilterWithAggregator JSON; contacts matching it are left out", + "schema": { + "type": "string" + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "rowsPerPage", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "subscribed": { + "type": "boolean" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsubscribeToken": { + "type": "string" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "contactId", + "email", + "subscribed", + "tags", + "unsubscribeToken", + "createdAt", + "updatedAt" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/import": { + "post": { + "description": "Creates or updates up to 1000 contacts per request, matched by email. A status can only get stricter (subscribed < unsubscribed < bounced < complained): an import never re-subscribes an unsubscribed or suppressed contact, and bounced or complained rows are imported unsubscribed and added to the suppression list. Paused contacts stay subscribed with the `paused` tag. Tags and custom fields are merged into existing contacts, and an earlier createdAt replaces a later one. Imports don't trigger sequences. Subscribed contacts over the plan's limit are skipped with `plan_limit_reached`.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Import contacts", + "tags": [ + "Contacts" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contacts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string", + "description": "The source platform's status, e.g. beehiiv's Active/Paused/Inactive/Pending or Mailchimp's subscribed/unsubscribed/cleaned. Empty means subscribed." + }, + "createdAt": { + "type": "string", + "description": "Original signup date; kept as the contact's createdAt" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "row": { + "type": "integer" + } + }, + "required": [ + "email" + ] + }, + "minItems": 1, + "maxItems": 1000 + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "maxItems": 20, + "description": "Added to every imported contact" + }, + "pending": { + "type": "string", + "enum": [ + "skip", + "unsubscribed" + ], + "description": "Contacts who never confirmed (Pending / Needs approval): skip them (default) or import them unsubscribed with the `pending` tag" + }, + "source": { + "type": "string", + "maxLength": 200, + "description": "Where the rows came from, e.g. the CSV file name; kept in the audit log" + } + }, + "required": [ + "contacts" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "created": { + "type": "integer" + }, + "updated": { + "type": "integer" + }, + "unchanged": { + "type": "integer" + }, + "suppressed": { + "type": "integer" + }, + "skipped": { + "type": "array", + "items": { + "type": "object", + "properties": { + "row": { + "type": "integer" + }, + "email": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "invalid_email", + "duplicate", + "pending_confirmation", + "unknown_status", + "plan_limit_reached" + ] + } + }, + "required": [ + "email", + "reason" + ] + } + } + }, + "required": [ + "created", + "updated", + "unchanged", + "suppressed", + "skipped" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/export": { + "get": { + "description": "Returns every contact, or those matching segmentId and/or filter, as CSV: email, name, status (subscribed, unsubscribed, bounced or complained), tags, created_at, suppression_reason, then one column per custom field.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Export contacts as CSV", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "segmentId", + "in": "query", + "description": "Only export this saved segment", + "schema": { + "type": "string" + } + }, + { + "name": "filter", + "in": "query", + "description": "Serialized ContactFilterWithAggregator JSON; combines with segmentId", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200" + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/bulk-delete/preview": { + "post": { + "description": "Counts the contacts a bulk delete would remove (and how many are unsubscribed) without deleting anything. Pass the count to bulkDelete as expectedCount. 404 when segmentId doesn't exist.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Preview a bulk contact deletion", + "tags": [ + "Contacts" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactIds": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1, + "maxItems": 1000, + "description": "Contact IDs, up to 1000. Unknown IDs are ignored." + }, + "match": { + "type": "object", + "properties": { + "q": { + "type": "string", + "description": "Search term matched against email or name" + }, + "segmentId": { + "type": "string", + "description": "Only contacts in this saved segment" + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ], + "description": "Inline ContactFilterWithAggregator; combines with q and segmentId" + } + }, + "description": "Every contact matching q, segmentId and filter (AND). `{}` selects every contact in the team." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "description": "Contacts that would be deleted right now" + }, + "unsubscribed": { + "type": "integer", + "description": "How many of them are unsubscribed; their addresses are added to the suppression list" + } + }, + "required": [ + "count", + "unsubscribed" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/bulk-delete": { + "post": { + "description": "Permanently deletes up to 1000 contacts by id, or every contact matching q, segmentId and filter. With match, pass the preview's count as expectedCount; if the live count differs nothing is deleted (409 count_changed, with the new count). Only contacts that existed when the request arrived are deleted. Up to 1000 contacts are deleted before the response (200, status completed); larger selections run in the background (202, status queued) and report progress at GET /contacts/bulk-delete/{deletionId}. Each contact's custom field values, sequence progress, events and delivery records are deleted with it, and no sequence triggers fire. Suppressions are kept, and the address of every deleted unsubscribed contact is added to the suppression list (reason manual), so re-importing or re-adding it never mails it again. This is not a full privacy erasure: delivery logs and the suppression list keep the address.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete contacts in bulk", + "tags": [ + "Contacts" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactIds": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "minItems": 1, + "maxItems": 1000, + "description": "Contact IDs, up to 1000. Unknown IDs are ignored." + }, + "match": { + "type": "object", + "properties": { + "q": { + "type": "string", + "description": "Search term matched against email or name" + }, + "segmentId": { + "type": "string", + "description": "Only contacts in this saved segment" + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ], + "description": "Inline ContactFilterWithAggregator; combines with q and segmentId" + } + }, + "description": "Every contact matching q, segmentId and filter (AND). `{}` selects every contact in the team." + }, + "expectedCount": { + "type": "integer", + "minimum": 0, + "description": "The count from the preview. Required with match: if the live count differs, nothing is deleted and the response is 409 count_changed." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deletionId": { + "type": "string", + "description": "Background job id for large deletions; null when it ran inline", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "queued", + "running", + "completed", + "failed" + ] + }, + "count": { + "type": "integer", + "description": "Contacts selected for deletion" + }, + "deleted": { + "type": "integer", + "description": "Contacts deleted so far" + }, + "suppressed": { + "type": "integer", + "description": "Unsubscribed addresses newly added to the suppression list" + } + }, + "required": [ + "deletionId", + "status", + "count", + "deleted", + "suppressed" + ] + } + } + } + }, + "202": { + "description": "202", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deletionId": { + "type": "string", + "description": "Background job id for large deletions; null when it ran inline", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "queued", + "running", + "completed", + "failed" + ] + }, + "count": { + "type": "integer", + "description": "Contacts selected for deletion" + }, + "deleted": { + "type": "integer", + "description": "Contacts deleted so far" + }, + "suppressed": { + "type": "integer", + "description": "Unsubscribed addresses newly added to the suppression list" + } + }, + "required": [ + "deletionId", + "status", + "count", + "deleted", + "suppressed" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "count": { + "type": "integer" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/bulk-delete/{deletionId}": { + "get": { + "description": "Progress of a background bulk delete. Finished deletions are kept for 24 hours.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a bulk contact deletion", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "deletionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deletionId": { + "type": "string", + "description": "Background job id for large deletions; null when it ran inline", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "queued", + "running", + "completed", + "failed" + ] + }, + "count": { + "type": "integer", + "description": "Contacts selected for deletion" + }, + "deleted": { + "type": "integer", + "description": "Contacts deleted so far" + }, + "suppressed": { + "type": "integer", + "description": "Unsubscribed addresses newly added to the suppression list" + } + }, + "required": [ + "deletionId", + "status", + "count", + "deleted", + "suppressed" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/{contactId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "subscribed": { + "type": "boolean" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsubscribeToken": { + "type": "string" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "contactId", + "email", + "subscribed", + "tags", + "unsubscribeToken", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "subscribed": { + "type": "boolean" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "subscribed": { + "type": "boolean" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsubscribeToken": { + "type": "string" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "contactId", + "email", + "subscribed", + "tags", + "unsubscribeToken", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/{contactId}/tags/{tag}": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Add a tag to a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "tag", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "subscribed": { + "type": "boolean" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsubscribeToken": { + "type": "string" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "contactId", + "email", + "subscribed", + "tags", + "unsubscribeToken", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Remove a tag from a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "tag", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "subscribed": { + "type": "boolean" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsubscribeToken": { + "type": "string" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "contactId", + "email", + "subscribed", + "tags", + "unsubscribeToken", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/{contactId}/deliveries": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List broadcasts/sequence emails delivered to a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/contacts/{contactId}/events": { + "get": { + "description": "Newest first by occurredAt. `enrolledSequences` lists the sequences each event started.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List the custom events received for a contact", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "contactId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 50 + } + }, + { + "name": "name", + "in": "query", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 128 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "eventId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "properties": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "occurredAt": { + "type": "string" + }, + "receivedAt": { + "type": "string" + }, + "source": { + "type": "string", + "enum": [ + "api", + "session", + "mcp" + ] + }, + "backfill": { + "type": "boolean" + }, + "triggers": { + "type": "boolean" + }, + "enrolledSequences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "title": { + "type": "string" + } + }, + "required": [ + "sequenceId", + "title" + ] + } + } + }, + "required": [ + "eventId", + "name", + "properties", + "occurredAt", + "receivedAt", + "source", + "backfill", + "triggers", + "enrolledSequences" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/segments": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create a saved segment", + "tags": [ + "Segments" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ] + } + }, + "required": [ + "name", + "filter" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "segmentId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ] + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "segmentId", + "name", + "filter", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List saved segments", + "tags": [ + "Segments" + ], + "parameters": [], + "responses": { + "200": { + "description": "200" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/segments/{segmentId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a saved segment", + "tags": [ + "Segments" + ], + "parameters": [ + { + "name": "segmentId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "segmentId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ] + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "segmentId", + "name", + "filter", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update a saved segment", + "tags": [ + "Segments" + ], + "parameters": [ + { + "name": "segmentId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "segmentId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ] + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "segmentId", + "name", + "filter", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete a saved segment", + "tags": [ + "Segments" + ], + "parameters": [ + { + "name": "segmentId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/tags": { + "get": { + "description": "Returns every tag carried by at least one of the team's contacts, sorted by name, with the number of contacts that carry it.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List tags", + "tags": [ + "Tags" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "contactCount": { + "type": "integer" + } + }, + "required": [ + "tag", + "contactCount" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/tags/{tag}/references": { + "get": { + "description": "Returns how many contacts carry the tag and the segments, broadcasts, sequences (audience filters, tag triggers, email tag actions) and signup forms that name it. Call it before deleting a tag.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List where a tag is used", + "tags": [ + "Tags" + ], + "parameters": [ + { + "name": "tag", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "contactCount": { + "type": "integer" + }, + "segments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "segmentId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "segmentId", + "name" + ] + } + }, + "sequences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "broadcast", + "sequence" + ] + }, + "status": { + "type": "string" + }, + "usages": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "filter", + "exclude_filter", + "trigger", + "email_action" + ] + } + } + }, + "required": [ + "sequenceId", + "title", + "type", + "status", + "usages" + ] + } + }, + "signupForms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "formId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "formId", + "name" + ] + } + } + }, + "required": [ + "tag", + "contactCount", + "segments", + "sequences", + "signupForms" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/tags/{tag}": { + "patch": { + "description": "Renames the tag on every contact, and in segments, broadcast and sequence filters and tag triggers, sequence email tag actions and signup forms. Renaming to a tag that already exists merges the two. Sent broadcasts keep their original filters. Renaming doesn't trigger tag-added or tag-removed sequences. 404 `tag_not_found` when nothing uses the tag; 400 `tag_unchanged` when the new name is the same.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Rename a tag", + "tags": [ + "Tags" + ], + "parameters": [ + { + "name": "tag", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "description": "The tag's new name" + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "previousTag": { + "type": "string" + }, + "contactsUpdated": { + "type": "integer" + }, + "segmentsUpdated": { + "type": "integer" + }, + "sequencesUpdated": { + "type": "integer" + }, + "signupFormsUpdated": { + "type": "integer" + } + }, + "required": [ + "tag", + "previousTag", + "contactsUpdated", + "segmentsUpdated", + "sequencesUpdated", + "signupFormsUpdated" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Removes the tag from every contact of the team. Segments, filters, triggers, email actions and signup forms that name the tag are left as they are and returned in `references`: a filter on a deleted tag matches nobody, and a trigger or form can add it again. Check `GET /tags/:tag/references` first. Deleting doesn't trigger tag-removed sequences. 404 `tag_not_found` when nothing uses the tag.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete a tag from every contact", + "tags": [ + "Tags" + ], + "parameters": [ + { + "name": "tag", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "contactsUpdated": { + "type": "integer" + }, + "references": { + "type": "object", + "properties": { + "tag": { + "type": "string" + }, + "contactCount": { + "type": "integer" + }, + "segments": { + "type": "array", + "items": { + "type": "object", + "properties": { + "segmentId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "segmentId", + "name" + ] + } + }, + "sequences": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "broadcast", + "sequence" + ] + }, + "status": { + "type": "string" + }, + "usages": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "filter", + "exclude_filter", + "trigger", + "email_action" + ] + } + } + }, + "required": [ + "sequenceId", + "title", + "type", + "status", + "usages" + ] + } + }, + "signupForms": { + "type": "array", + "items": { + "type": "object", + "properties": { + "formId": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "formId", + "name" + ] + } + } + }, + "required": [ + "tag", + "contactCount", + "segments", + "sequences", + "signupForms" + ] + } + }, + "required": [ + "tag", + "contactsUpdated", + "references" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/media": { + "get": { + "description": "Returns the team's uploaded MediaLit-backed media. Unsplash and external URL images are not stored here.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List uploaded media", + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "pageSize", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + } + }, + { + "name": "query", + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "mediaId": { + "type": "string" + }, + "url": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + }, + "mediaLitId": { + "type": "string" + }, + "fileName": { + "type": "string", + "nullable": true + }, + "mimeType": { + "type": "string", + "nullable": true + }, + "size": { + "type": "number", + "nullable": true + }, + "width": { + "type": "number", + "nullable": true + }, + "height": { + "type": "number", + "nullable": true + }, + "alt": { + "type": "string", + "nullable": true + }, + "caption": { + "type": "string", + "nullable": true + }, + "createdAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + }, + "updatedAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + } + }, + "required": [ + "mediaId", + "url", + "mediaLitId" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/media/presigned": { + "post": { + "description": "Returns a short-lived MediaLit signature and endpoint. Upload image files directly to `${endpoint}/media/create/resumable` with the signature in the `x-medialit-signature` header.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Generate a MediaLit upload signature", + "tags": [ + "Media" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "signature": { + "type": "string" + }, + "endpoint": { + "type": "string" + } + }, + "required": [ + "signature", + "endpoint" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "500": { + "description": "500", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/media/{mediaId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get uploaded media", + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "mediaId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "mediaId": { + "type": "string" + }, + "url": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + }, + "mediaLitId": { + "type": "string" + }, + "fileName": { + "type": "string", + "nullable": true + }, + "mimeType": { + "type": "string", + "nullable": true + }, + "size": { + "type": "number", + "nullable": true + }, + "width": { + "type": "number", + "nullable": true + }, + "height": { + "type": "number", + "nullable": true + }, + "alt": { + "type": "string", + "nullable": true + }, + "caption": { + "type": "string", + "nullable": true + }, + "createdAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + }, + "updatedAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + } + }, + "required": [ + "mediaId", + "url", + "mediaLitId" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update uploaded media metadata", + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "mediaId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "alt": { + "type": "string", + "nullable": true + }, + "caption": { + "type": "string", + "nullable": true + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "mediaId": { + "type": "string" + }, + "url": { + "type": "string" + }, + "thumbnailUrl": { + "type": "string", + "nullable": true + }, + "mediaLitId": { + "type": "string" + }, + "fileName": { + "type": "string", + "nullable": true + }, + "mimeType": { + "type": "string", + "nullable": true + }, + "size": { + "type": "number", + "nullable": true + }, + "width": { + "type": "number", + "nullable": true + }, + "height": { + "type": "number", + "nullable": true + }, + "alt": { + "type": "string", + "nullable": true + }, + "caption": { + "type": "string", + "nullable": true + }, + "createdAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + }, + "updatedAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + } + }, + "required": [ + "mediaId", + "url", + "mediaLitId" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Deletes the MediaLit file and SendLit media row only when no saved email content references it.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete unused uploaded media", + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "mediaId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/media/{mediaId}/references": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List uploaded media references", + "tags": [ + "Media" + ], + "parameters": [ + { + "name": "mediaId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "resourceType": { + "type": "string", + "enum": [ + "TEMPLATE", + "SEQUENCE_EMAIL", + "BROADCAST_WEB_PAGE" + ] + }, + "resourcePublicId": { + "type": "string" + }, + "parentResourcePublicId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + }, + "updatedAt": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "string", + "format": "date-time" + } + ], + "nullable": true + } + }, + "required": [ + "resourceType", + "resourcePublicId" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/system-templates": { + "get": { + "description": "Not team-scoped—the same for every team. Filter by purpose. System templates are starters and must be duplicated into a team-owned template before transactional sending.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List built-in starting templates", + "tags": [ + "Templates" + ], + "parameters": [ + { + "name": "purpose", + "in": "query", + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails.", + "schema": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ] + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "templateId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "purpose": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ], + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails." + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "requiredVariables": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Sorted, server-discovered Liquid paths that must be supplied." + }, + "variableDefinitions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "minLength": 1, + "description": "Dot-separated Liquid variable path." + }, + "description": { + "type": "string", + "description": "Human-readable value description." + }, + "example": { + "description": "Example value used in generated requests.", + "nullable": true + } + }, + "required": [ + "path", + "description" + ] + }, + "description": "Documentation metadata for built-in starter variables; runtime discovery remains authoritative." + } + }, + "required": [ + "templateId", + "title", + "description", + "purpose", + "content", + "requiredVariables" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/templates": { + "post": { + "description": "Purpose is immutable. Marketing content must contain exactly one final managed footer; transactional content must not contain a footer or marketing-only variables.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create an email template", + "tags": [ + "Templates" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "minLength": 1 + }, + "purpose": { + "default": "marketing", + "type": "string", + "enum": [ + "marketing", + "transactional" + ], + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails." + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "properties": { + "colors": { + "type": "object", + "properties": { + "background": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "foreground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "border": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "accent": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "accentForeground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + } + }, + "required": [ + "background", + "foreground", + "border", + "accent", + "accentForeground" + ], + "additionalProperties": false + }, + "typography": { + "type": "object", + "properties": { + "header": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "text": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "link": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "required": [ + "header", + "text", + "link" + ], + "additionalProperties": false + }, + "interactives": { + "type": "object", + "properties": { + "button": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + }, + "border": { + "type": "object", + "properties": { + "width": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "radius": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "style": { + "type": "string", + "minLength": 1 + } + } + } + }, + "additionalProperties": true + }, + "link": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + } + }, + "additionalProperties": true + } + }, + "required": [ + "button", + "link" + ], + "additionalProperties": false + }, + "structure": { + "type": "object", + "properties": { + "page": { + "type": "object", + "properties": { + "background": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "foreground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "width": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "marginY": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "borderWidth": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "borderStyle": { + "type": "string", + "minLength": 1 + }, + "borderRadius": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + }, + "additionalProperties": false + }, + "section": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + } + }, + "additionalProperties": false + } + }, + "required": [ + "page", + "section" + ], + "additionalProperties": false + } + }, + "required": [ + "colors", + "typography", + "interactives", + "structure" + ], + "additionalProperties": false + }, + "meta": { + "type": "object", + "properties": { + "previewText": { + "type": "string" + }, + "utm": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "medium": { + "type": "string" + }, + "campaign": { + "type": "string" + } + }, + "required": [ + "source", + "medium", + "campaign" + ], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "content": { + "type": "array", + "items": { + "discriminator": { + "propertyName": "blockType" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "content": { + "type": "string" + } + }, + "required": [ + "content" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "text" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "url": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "text", + "url" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "link" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "src": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "src" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "image" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "blockType": { + "type": "string", + "enum": [ + "separator" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "height": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + }, + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "spacer" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "left": { + "type": "string" + }, + "right": { + "type": "string" + }, + "ratio": { + "type": "string", + "enum": [ + "11", + "12", + "21" + ] + }, + "stack": { + "type": "boolean" + } + }, + "required": [ + "left", + "right" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "columns" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "network": { + "type": "string", + "enum": [ + "website", + "instagram", + "linkedin", + "youtube", + "x", + "facebook", + "tiktok" + ] + }, + "url": { + "type": "string", + "pattern": "^(?:https?:\\/\\/\\S+)?$" + } + }, + "required": [ + "network", + "url" + ] + } + } + }, + "required": [ + "links" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "social" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": [ + "code" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "html" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "pattern": "^[A-Za-z0-9][A-Za-z0-9 _.-]{0,39}$" + }, + "has": { + "type": "boolean" + }, + "content": { + "type": "string" + } + }, + "required": [ + "tag", + "content" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "conditional" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "blockType": { + "type": "string", + "enum": [ + "footer" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + } + ] + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + } + }, + "required": [ + "title", + "content" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "templateId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "purpose": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ], + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails." + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "requiredVariables": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Sorted, server-discovered Liquid paths that must be supplied. Defaults and inactive guarded branches are omitted." + }, + "validationError": { + "type": "string", + "description": "Present only for a retired template that predates the managed-footer format. It cannot be edited, duplicated, or sent; recreate it instead." + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "templateId", + "title", + "purpose", + "content", + "requiredVariables", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "variables": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "get": { + "description": "Returns team-owned templates with their immutable purpose and server-computed requiredVariables. Optionally filter by purpose.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List email templates", + "tags": [ + "Templates" + ], + "parameters": [ + { + "name": "purpose", + "in": "query", + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails.", + "schema": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ] + } + } + ], + "responses": { + "200": { + "description": "200" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/templates/{templateId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get an email template", + "tags": [ + "Templates" + ], + "parameters": [ + { + "name": "templateId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "templateId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "purpose": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ], + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails." + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "requiredVariables": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Sorted, server-discovered Liquid paths that must be supplied. Defaults and inactive guarded branches are omitted." + }, + "validationError": { + "type": "string", + "description": "Present only for a retired template that predates the managed-footer format. It cannot be edited, duplicated, or sent; recreate it instead." + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "templateId", + "title", + "purpose", + "content", + "requiredVariables", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "variables": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update an email template", + "tags": [ + "Templates" + ], + "parameters": [ + { + "name": "templateId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "minLength": 1 + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "properties": { + "colors": { + "type": "object", + "properties": { + "background": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "foreground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "border": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "accent": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "accentForeground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + } + }, + "required": [ + "background", + "foreground", + "border", + "accent", + "accentForeground" + ], + "additionalProperties": false + }, + "typography": { + "type": "object", + "properties": { + "header": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "text": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "link": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "required": [ + "header", + "text", + "link" + ], + "additionalProperties": false + }, + "interactives": { + "type": "object", + "properties": { + "button": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + }, + "border": { + "type": "object", + "properties": { + "width": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "radius": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "style": { + "type": "string", + "minLength": 1 + } + } + } + }, + "additionalProperties": true + }, + "link": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + } + }, + "additionalProperties": true + } + }, + "required": [ + "button", + "link" + ], + "additionalProperties": false + }, + "structure": { + "type": "object", + "properties": { + "page": { + "type": "object", + "properties": { + "background": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "foreground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "width": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "marginY": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "borderWidth": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "borderStyle": { + "type": "string", + "minLength": 1 + }, + "borderRadius": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + }, + "additionalProperties": false + }, + "section": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + } + }, + "additionalProperties": false + } + }, + "required": [ + "page", + "section" + ], + "additionalProperties": false + } + }, + "required": [ + "colors", + "typography", + "interactives", + "structure" + ], + "additionalProperties": false + }, + "meta": { + "type": "object", + "properties": { + "previewText": { + "type": "string" + }, + "utm": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "medium": { + "type": "string" + }, + "campaign": { + "type": "string" + } + }, + "required": [ + "source", + "medium", + "campaign" + ], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "content": { + "type": "array", + "items": { + "discriminator": { + "propertyName": "blockType" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "content": { + "type": "string" + } + }, + "required": [ + "content" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "text" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "url": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "text", + "url" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "link" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "src": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "src" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "image" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "blockType": { + "type": "string", + "enum": [ + "separator" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "height": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + }, + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "spacer" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "left": { + "type": "string" + }, + "right": { + "type": "string" + }, + "ratio": { + "type": "string", + "enum": [ + "11", + "12", + "21" + ] + }, + "stack": { + "type": "boolean" + } + }, + "required": [ + "left", + "right" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "columns" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "network": { + "type": "string", + "enum": [ + "website", + "instagram", + "linkedin", + "youtube", + "x", + "facebook", + "tiktok" + ] + }, + "url": { + "type": "string", + "pattern": "^(?:https?:\\/\\/\\S+)?$" + } + }, + "required": [ + "network", + "url" + ] + } + } + }, + "required": [ + "links" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "social" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": [ + "code" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "html" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "pattern": "^[A-Za-z0-9][A-Za-z0-9 _.-]{0,39}$" + }, + "has": { + "type": "boolean" + }, + "content": { + "type": "string" + } + }, + "required": [ + "tag", + "content" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "conditional" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "blockType": { + "type": "string", + "enum": [ + "footer" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + } + ] + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "templateId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "purpose": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ], + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails." + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "requiredVariables": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Sorted, server-discovered Liquid paths that must be supplied. Defaults and inactive guarded branches are omitted." + }, + "validationError": { + "type": "string", + "description": "Present only for a retired template that predates the managed-footer format. It cannot be edited, duplicated, or sent; recreate it instead." + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "templateId", + "title", + "purpose", + "content", + "requiredVariables", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "variables": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Permanently deletes the template. Its images that no other template or email uses are deleted from the media library and storage, unless a transactional email was sent with the template.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete an email template", + "tags": [ + "Templates" + ], + "parameters": [ + { + "name": "templateId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/templates/{templateId}/duplicate": { + "post": { + "description": "Creates a new team-owned copy with the same immutable purpose as the source template.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Duplicate an email template", + "tags": [ + "Templates" + ], + "parameters": [ + { + "name": "templateId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "templateId": { + "type": "string" + }, + "title": { + "type": "string" + }, + "purpose": { + "type": "string", + "enum": [ + "marketing", + "transactional" + ], + "description": "Immutable sending purpose. Marketing templates require a final managed footer; transactional templates are footer-free and are valid for POST /emails." + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "requiredVariables": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Sorted, server-discovered Liquid paths that must be supplied. Defaults and inactive guarded branches are omitted." + }, + "validationError": { + "type": "string", + "description": "Present only for a retired template that predates the managed-footer format. It cannot be edited, duplicated, or sent; recreate it instead." + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "templateId", + "title", + "purpose", + "content", + "requiredVariables", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + }, + "variables": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences": { + "post": { + "description": "templateId must identify a marketing team template or marketing system starter. Transactional templates return 422 template_not_marketing.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create a broadcast or a sequence", + "tags": [ + "Sequences" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "broadcast", + "sequence" + ] + }, + "templateId": { + "type": "string", + "minLength": 1 + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "type", + "templateId" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "template_not_marketing" + ] + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "get": { + "description": "Archived broadcasts are left out unless `archived` is `only` or `include`.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List broadcasts or sequences", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "type", + "in": "query", + "required": true, + "schema": { + "type": "string", + "enum": [ + "broadcast", + "sequence" + ] + } + }, + { + "name": "archived", + "in": "query", + "description": "Archived broadcasts: exclude (default) hides them, only lists just them, include lists both", + "schema": { + "type": "string", + "enum": [ + "exclude", + "only", + "include" + ] + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "itemsPerPage", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 50 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a broadcast or sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "A broadcast's title is its email's subject, so once the broadcast has been sent, changing the title returns 400 with the error \"This broadcast has already been sent\". `viewInBrowser` can be changed at any time; turning it off also takes down the web versions linked from emails already sent.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update a broadcast or sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "description": "Broadcasts use date:occurred. Sequences: subscriber:added, tag:added, tag:removed, event:custom or contact:property_changed." + }, + "triggerData": { + "type": "string", + "description": "Tag name for tag triggers, event name for event:custom, custom field key for contact:property_changed." + }, + "triggerFilter": { + "oneOf": [ + { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "gt", + "gte", + "lt", + "lte", + "exists", + "not_exists" + ] + }, + "value": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + }, + "required": [ + "key", + "condition" + ] + }, + "minItems": 1, + "maxItems": 10 + } + }, + "required": [ + "aggregator", + "filters" + ] + }, + { + "type": "object", + "properties": { + "from": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "nullable": true + }, + "to": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ], + "nullable": true + } + }, + "additionalProperties": false + } + ], + "nullable": true, + "description": "event:custom: { aggregator, filters: [{ key, condition, value }] }. contact:property_changed: { from?, to? }. null clears it." + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ], + "description": "Sequences only. every_time (default), once, or once_per_period with reentryPeriodDays." + }, + "reentryPeriodDays": { + "type": "integer", + "minimum": 1, + "maximum": 365, + "nullable": true + }, + "filter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ], + "description": "Audience: subscribed contacts matching this filter; empty means everyone" + }, + "excludeFilter": { + "type": "object", + "properties": { + "aggregator": { + "type": "string", + "enum": [ + "and", + "or" + ] + }, + "filters": { + "type": "array", + "items": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "email" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "contains", + "not_contains" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "tag" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "subscription" + ] + }, + "condition": { + "type": "string", + "enum": [ + "is" + ] + }, + "value": { + "type": "string", + "enum": [ + "subscribed", + "unsubscribed" + ] + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "signedUp" + ] + }, + "condition": { + "type": "string", + "enum": [ + "before", + "after", + "on" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "condition", + "value" + ] + }, + { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "customField" + ] + }, + "key": { + "type": "string", + "minLength": 1 + }, + "condition": { + "type": "string", + "enum": [ + "is", + "is_not", + "contains", + "not_contains", + "has", + "not_has", + "before", + "after", + "on", + "exists", + "not_exists" + ] + }, + "value": { + "type": "string" + }, + "valueLabel": { + "type": "string" + } + }, + "required": [ + "name", + "key", + "condition" + ] + } + ] + } + } + }, + "required": [ + "aggregator", + "filters" + ], + "nullable": true, + "description": "Contacts matching this filter are left out of the audience, even when they match `filter`. `null` clears it." + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "viewInBrowser": { + "type": "boolean", + "description": "Whether emails link a hosted web version (\"View in browser\"). `false` leaves the link out of later sends and takes down the web versions behind links already sent. Can be changed at any time." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Permanently deletes the broadcast or sequence, its emails and triggers. An active sequence or a sending broadcast can't be deleted: pause it first (409 `sequence_active`). A draft or scheduled broadcast can be deleted; one that has started sending can't (409 `broadcast_sent`): archive it instead with POST /sequences/{sequenceId}/archive. Contacts still enrolled in a paused sequence leave it, and its delivery, open and click history is deleted too, so its stats are gone; check GET /sequences/{sequenceId}/deletion-preview first. The outbound message log and suppressions are kept. Images it uses that nothing else uses are deleted from the media library and storage; images used by sent mail are kept.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete a broadcast or sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/deletion-preview": { + "get": { + "description": "How many contacts are enrolled and how many emails it has sent, and whether it can be deleted right now. Nothing is changed.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Preview what deleting a broadcast or sequence removes", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "The broadcast or sequence's current status" + }, + "blocker": { + "type": "string", + "enum": [ + "sequence_active", + "broadcast_sent" + ], + "description": "Why it can't be deleted right now (sequence_active: pause it first; broadcast_sent: archive the broadcast instead), or null when it can", + "nullable": true + }, + "enrolledCount": { + "type": "integer", + "description": "Contacts currently in the sequence; deleting it stops their emails" + }, + "emailsSentCount": { + "type": "integer", + "description": "Emails it has sent; their delivery, open and click history is deleted" + } + }, + "required": [ + "status", + "blocker", + "enrolledCount", + "emailsSentCount" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/archive": { + "post": { + "description": "Hides a sent broadcast from the broadcasts list (see `archived` on GET /sequences) and from the public web archive, its feed and sitemap. Its report, delivery history and images are kept, and recipients' \"view in browser\" links keep working. 409 `broadcast_not_sent` for a draft or scheduled broadcast (delete it instead) and `sequence_active` while it is sending. Archiving an archived broadcast changes nothing. 404 for sequences.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Archive a sent broadcast", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/unarchive": { + "post": { + "description": "Puts an archived broadcast back in the broadcasts list, and back on the web archive when it is published there.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Restore an archived broadcast", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/emails": { + "post": { + "description": "templateId must identify a marketing template; transactional templates return 422 template_not_marketing.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Add an email to a sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "templateId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "templateId" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "template_not_marketing" + ] + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/emails/{emailId}": { + "patch": { + "description": "When templateId is supplied, it must identify a marketing template; transactional templates return 422 template_not_marketing. Edits to an active sequence's email apply to later sends only: the \"view in browser\" page of an email already sent keeps showing what was sent. A sent broadcast can't be edited; the request returns it unchanged.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update an email within a sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "emailId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "properties": { + "colors": { + "type": "object", + "properties": { + "background": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "foreground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "border": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "accent": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "accentForeground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + } + }, + "required": [ + "background", + "foreground", + "border", + "accent", + "accentForeground" + ], + "additionalProperties": false + }, + "typography": { + "type": "object", + "properties": { + "header": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "text": { + "type": "object", + "properties": {}, + "additionalProperties": true + }, + "link": { + "type": "object", + "properties": {}, + "additionalProperties": true + } + }, + "required": [ + "header", + "text", + "link" + ], + "additionalProperties": false + }, + "interactives": { + "type": "object", + "properties": { + "button": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + }, + "border": { + "type": "object", + "properties": { + "width": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "radius": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "style": { + "type": "string", + "minLength": 1 + } + } + } + }, + "additionalProperties": true + }, + "link": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + } + }, + "additionalProperties": true + } + }, + "required": [ + "button", + "link" + ], + "additionalProperties": false + }, + "structure": { + "type": "object", + "properties": { + "page": { + "type": "object", + "properties": { + "background": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "foreground": { + "type": "string", + "pattern": "^#(?:[\\da-f]{3,4}|[\\da-f]{6}|[\\da-f]{8})$" + }, + "width": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "marginY": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "borderWidth": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "borderStyle": { + "type": "string", + "minLength": 1 + }, + "borderRadius": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + }, + "additionalProperties": false + }, + "section": { + "type": "object", + "properties": { + "padding": { + "type": "object", + "properties": { + "x": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + }, + "y": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + } + } + }, + "additionalProperties": false + } + }, + "required": [ + "page", + "section" + ], + "additionalProperties": false + } + }, + "required": [ + "colors", + "typography", + "interactives", + "structure" + ], + "additionalProperties": false + }, + "meta": { + "type": "object", + "properties": { + "previewText": { + "type": "string" + }, + "utm": { + "type": "object", + "properties": { + "source": { + "type": "string" + }, + "medium": { + "type": "string" + }, + "campaign": { + "type": "string" + } + }, + "required": [ + "source", + "medium", + "campaign" + ], + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "content": { + "type": "array", + "items": { + "discriminator": { + "propertyName": "blockType" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "content": { + "type": "string" + } + }, + "required": [ + "content" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "text" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "url": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "text", + "url" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "link" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "src": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "src" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "image" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "blockType": { + "type": "string", + "enum": [ + "separator" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "height": { + "type": "string", + "pattern": "^\\d+(?:\\.\\d+)?px$" + } + }, + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "spacer" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "left": { + "type": "string" + }, + "right": { + "type": "string" + }, + "ratio": { + "type": "string", + "enum": [ + "11", + "12", + "21" + ] + }, + "stack": { + "type": "boolean" + } + }, + "required": [ + "left", + "right" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "columns" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "links": { + "type": "array", + "items": { + "type": "object", + "properties": { + "network": { + "type": "string", + "enum": [ + "website", + "instagram", + "linkedin", + "youtube", + "x", + "facebook", + "tiktok" + ] + }, + "url": { + "type": "string", + "pattern": "^(?:https?:\\/\\/\\S+)?$" + } + }, + "required": [ + "network", + "url" + ] + } + } + }, + "required": [ + "links" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "social" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "code": { + "type": "string" + } + }, + "required": [ + "code" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "html" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "properties": { + "tag": { + "type": "string", + "pattern": "^[A-Za-z0-9][A-Za-z0-9 _.-]{0,39}$" + }, + "has": { + "type": "boolean" + }, + "content": { + "type": "string" + } + }, + "required": [ + "tag", + "content" + ], + "additionalProperties": true + }, + "blockType": { + "type": "string", + "enum": [ + "conditional" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "settings": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "blockType": { + "type": "string", + "enum": [ + "footer" + ] + } + }, + "required": [ + "settings", + "blockType" + ] + } + ] + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "templateId": { + "type": "string", + "minLength": 1 + }, + "actionType": { + "type": "string", + "enum": [ + "tag:add", + "tag:remove" + ] + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "published": { + "type": "boolean" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "template_not_marketing" + ] + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Removes the email. Images it uses that nothing else uses are deleted from the media library and storage; images used by sent mail are kept.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Remove an email from a sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "emailId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/emails/{emailId}/test": { + "post": { + "description": "Renders the email exactly like a real send (merge tags, footer, tracking) and sends it with a `[Test]` subject prefix through the delivery source the real send would use. Its \"view in browser\" link shows the email as it was when tested, not later edits. Merge tags use the recipient's contact data when they are a contact. Opens, clicks and the unsubscribe link in a test change nothing. Suppressed addresses are skipped. Every recipient counts as one test: by default a team can send 100 test emails in any 24 hours (50 in any 3 hours) and 20 per email per 24 hours, after which this returns 429 `test_send_limit_reached` or `test_send_email_limit_reached`. Tests through an organization's shared mailbox also count against the team's shared-delivery quota (429 `organization_team_quota_exhausted` or `organization_quota_exhausted`).\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Send a test of a broadcast or sequence email", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "emailId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "array", + "items": { + "type": "string", + "format": "email" + }, + "maxItems": 5, + "description": "Up to 5 addresses. Defaults to the signed-in user's address; API keys must set it." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sent": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Addresses the test was sent to" + }, + "skipped": { + "type": "array", + "items": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "reason": { + "type": "string", + "enum": [ + "suppressed" + ] + } + }, + "required": [ + "email", + "reason" + ] + }, + "description": "Addresses on the suppression list, which never get mail" + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "eventSample": { + "type": "string", + "enum": [ + "last_event", + "none" + ] + } + }, + "required": [ + "sent", + "skipped", + "lastTestSentAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/start": { + "post": { + "description": "Requires a non-empty mailing address in the team's general settings. On SendLit Cloud, a new organization's first broadcast to 1,000 or more contacts through its own ESP is reviewed once before it can start (400 `broadcast_held_for_review`); start it again once approved.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Start a broadcast or activate a sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/pause": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Pause an active sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sequenceId": { + "type": "string" + }, + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "string" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ], + "nullable": true + }, + "triggerType": { + "type": "string", + "enum": [ + "subscriber:added", + "tag:added", + "tag:removed", + "date:occurred", + "event:custom", + "contact:property_changed" + ], + "nullable": true + }, + "triggerData": { + "type": "string", + "nullable": true + }, + "triggerFilter": { + "nullable": true + }, + "reentry": { + "type": "string", + "enum": [ + "every_time", + "once", + "once_per_period" + ] + }, + "reentryPeriodDays": { + "type": "integer", + "nullable": true + }, + "filter": { + "nullable": true + }, + "excludeFilter": { + "nullable": true + }, + "entrantsCount": { + "type": "integer", + "minimum": 0 + }, + "emailsOrder": { + "type": "array", + "items": { + "type": "string" + } + }, + "entrants": { + "type": "array", + "items": { + "type": "string" + } + }, + "report": { + "nullable": true + }, + "viewInBrowser": { + "type": "boolean" + }, + "archivedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "subject": { + "type": "string" + }, + "content": { + "type": "object", + "properties": { + "style": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "meta": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "content": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "nullable": true + } + } + } + }, + "required": [ + "style", + "meta", + "content" + ] + }, + "delayInMillis": { + "type": "number" + }, + "published": { + "type": "boolean" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "actionType": { + "type": "string", + "nullable": true + }, + "actionData": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + }, + "lastTestSentAt": { + "type": "string", + "nullable": true + }, + "contentScanVerdict": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "emailId", + "subject", + "content", + "delayInMillis", + "published", + "createdAt", + "updatedAt" + ] + } + }, + "pacing": { + "type": "object", + "properties": { + "reason": { + "type": "string", + "enum": [ + "new_account" + ] + }, + "hourlyLimit": { + "type": "integer" + }, + "dailyLimit": { + "type": "integer" + }, + "remaining": { + "type": "integer" + }, + "estimatedCompletionAt": { + "type": "string" + } + }, + "required": [ + "reason", + "hourlyLimit", + "dailyLimit", + "remaining", + "estimatedCompletionAt" + ], + "nullable": true + } + }, + "required": [ + "sequenceId", + "type", + "title", + "status", + "deliverySource", + "emailsOrder", + "entrants", + "viewInBrowser", + "createdAt", + "updatedAt", + "emails" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/web": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a broadcast's web publishing settings", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "published": { + "type": "boolean" + }, + "slug": { + "type": "string", + "nullable": true + }, + "seoTitle": { + "type": "string", + "nullable": true + }, + "seoDescription": { + "type": "string", + "nullable": true + }, + "ogImage": { + "type": "string", + "nullable": true + }, + "noindex": { + "type": "boolean" + }, + "canonicalUrl": { + "type": "string", + "nullable": true + }, + "live": { + "type": "boolean" + }, + "publishedAt": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + } + }, + "required": [ + "published", + "slug", + "seoTitle", + "seoDescription", + "ogImage", + "noindex", + "canonicalUrl", + "live", + "publishedAt", + "url" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "Only broadcasts can be published. The page goes live once the broadcast is sent. Publishing without a slug makes one from the title; 409 `slug_taken` when another issue of the team uses the slug.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Publish a broadcast to the web, or change how it's published", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "published": { + "type": "boolean", + "description": "Publish the broadcast as a public web page once it's sent" + }, + "slug": { + "type": "string", + "minLength": 1, + "maxLength": 100, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "description": "URL slug; defaults to one made from the title" + }, + "seoTitle": { + "type": "string", + "maxLength": 200, + "nullable": true + }, + "seoDescription": { + "type": "string", + "maxLength": 500, + "nullable": true + }, + "ogImage": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Image for social previews. An image uploaded to MediaLit is added to the media library and can't be deleted while the page uses it" + }, + "noindex": { + "type": "boolean", + "description": "Ask search engines not to index the page" + }, + "canonicalUrl": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Canonical URL, when a platform hosts the issue on its own site" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "published": { + "type": "boolean" + }, + "slug": { + "type": "string", + "nullable": true + }, + "seoTitle": { + "type": "string", + "nullable": true + }, + "seoDescription": { + "type": "string", + "nullable": true + }, + "ogImage": { + "type": "string", + "nullable": true + }, + "noindex": { + "type": "boolean" + }, + "canonicalUrl": { + "type": "string", + "nullable": true + }, + "live": { + "type": "boolean" + }, + "publishedAt": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + } + }, + "required": [ + "published", + "slug", + "seoTitle", + "seoDescription", + "ogImage", + "noindex", + "canonicalUrl", + "live", + "publishedAt", + "url" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/stats": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delivery / open-rate / click-through-rate stats for a sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "sent": { + "type": "number" + }, + "openRate": { + "type": "number" + }, + "clickThroughRate": { + "type": "number" + }, + "subscribersCount": { + "type": "number" + }, + "bounceRate": { + "type": "number" + }, + "complaintRate": { + "type": "number" + } + }, + "required": [ + "sent", + "openRate", + "clickThroughRate", + "subscribersCount" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/schedule": { + "get": { + "description": "For each email, in send order: how many contacts in the sequence will get it next and the earliest of their due times. A contact counts once, against the email they're waiting for, so emails they've had or will get later don't count them. Each contact's delay counts from their own entry, so their due times differ; list them with `GET /sequences/{sequenceId}/emails/{emailId}/waiting`. Due times are the earliest a contact can get the email; pauses and sending limits can make it later.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Contacts waiting for each email of a sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "waiting": { + "type": "number", + "description": "Contacts in the sequence who are waiting for their next email" + }, + "emails": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailId": { + "type": "string" + }, + "waiting": { + "type": "number", + "description": "Contacts whose next email is this one" + }, + "nextSendAt": { + "type": "number", + "description": "The earliest due time among them (epoch millis), not a time they share: each contact's delay counts from their own entry. null when no one is waiting", + "nullable": true + } + }, + "required": [ + "emailId", + "waiting", + "nextSendAt" + ] + } + } + }, + "required": [ + "waiting", + "emails" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/emails/{emailId}/waiting": { + "get": { + "description": "The contacts whose next email in the sequence is this one, soonest due first, each with their own due time. `q` matches email or name. Pages hold `limit` contacts (default 10, at most 100); pass `nextCursor` as `after` for the next page. `total` counts every match. A cursor that can't be read returns 400 `invalid_cursor`.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Contacts waiting for a sequence email", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "emailId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "after", + "in": "query", + "description": "The previous page's `nextCursor`; omit for the first page", + "schema": { + "type": "string" + } + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + } + }, + { + "name": "q", + "in": "query", + "description": "Matches the contact's email or name", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "contactId": { + "type": "string" + }, + "email": { + "type": "string" + }, + "name": { + "type": "string", + "nullable": true + }, + "nextSendAt": { + "type": "number", + "description": "When this contact is due to get the email (epoch millis)" + } + }, + "required": [ + "contactId", + "email", + "name", + "nextSendAt" + ] + } + }, + "total": { + "type": "number", + "description": "Every waiting contact that matches, across all pages" + }, + "nextCursor": { + "type": "string", + "description": "Pass as `after` for the next page; null on the last page", + "nullable": true + } + }, + "required": [ + "items", + "total", + "nextCursor" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sequences/{sequenceId}/subscribers": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List contact ids that have received this sequence", + "tags": [ + "Sequences" + ], + "parameters": [ + { + "name": "sequenceId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "page", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "limit", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "200" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/emails": { + "post": { + "description": "Requires a non-empty mailing address in the team's general settings. `templateId` must identify a team-owned transactional template; marketing templates return 422 template_not_transactional. Unguarded Liquid variables must be supplied in `variables`, or the API returns 422 missing_template_variables without creating a row or queue job. Inline `html` is sent verbatim without Liquid rendering. address, unsubscribe_link, and subscriber are marketing-only server-owned values and are unavailable to transactional templates.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Send a transactional email", + "tags": [ + "Transactional Emails" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "format": "email" + }, + "subject": { + "type": "string", + "minLength": 1 + }, + "templateId": { + "type": "string", + "minLength": 1 + }, + "html": { + "type": "string", + "minLength": 1 + }, + "text": { + "type": "string", + "minLength": 1, + "description": "Plain-text part, sent verbatim. When omitted it is generated from the HTML." + }, + "variables": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "replyTo": { + "type": "string", + "format": "email" + }, + "headers": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "idempotencyKey": { + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "trackOpens": { + "type": "boolean" + }, + "trackClicks": { + "type": "boolean" + }, + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "type" + ] + } + ] + } + }, + "required": [ + "to", + "subject" + ] + } + } + } + }, + "responses": { + "202": { + "description": "202", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "txeId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "queued", + "sent", + "failed", + "bounced", + "suppressed" + ] + } + }, + "required": [ + "txeId", + "status" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "missing_template_variables" + ] + }, + "missingVariables": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + } + }, + "required": [ + "error", + "missingVariables" + ] + }, + { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "template_not_transactional" + ] + } + }, + "required": [ + "error" + ] + }, + { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + ] + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List transactional emails", + "tags": [ + "Transactional Emails" + ], + "parameters": [ + { + "name": "status", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "queued", + "sent", + "failed", + "bounced", + "suppressed" + ] + } + }, + { + "name": "createdAfter", + "in": "query", + "description": "Millisecond timestamp lower bound (inclusive)", + "schema": { + "type": "integer", + "nullable": true + } + }, + { + "name": "createdBefore", + "in": "query", + "description": "Millisecond timestamp upper bound (exclusive)", + "schema": { + "type": "integer", + "nullable": true + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "itemsPerPage", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 50 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "txeId": { + "type": "string" + }, + "to": { + "type": "string" + }, + "from": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "subject": { + "type": "string" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "variables": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "status": { + "type": "string", + "enum": [ + "queued", + "sent", + "failed", + "bounced", + "suppressed" + ] + }, + "error": { + "type": "string", + "nullable": true + }, + "trackOpens": { + "type": "boolean" + }, + "trackClicks": { + "type": "boolean" + }, + "openCount": { + "type": "number" + }, + "clickCount": { + "type": "number" + }, + "sentAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "txeId", + "to", + "from", + "replyTo", + "subject", + "templateId", + "variables", + "status", + "error", + "trackOpens", + "trackClicks", + "openCount", + "clickCount", + "sentAt", + "createdAt", + "updatedAt" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/emails/{txeId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a transactional email", + "tags": [ + "Transactional Emails" + ], + "parameters": [ + { + "name": "txeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "txeId": { + "type": "string" + }, + "to": { + "type": "string" + }, + "from": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "subject": { + "type": "string" + }, + "templateId": { + "type": "string", + "nullable": true + }, + "variables": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "status": { + "type": "string", + "enum": [ + "queued", + "sent", + "failed", + "bounced", + "suppressed" + ] + }, + "error": { + "type": "string", + "nullable": true + }, + "trackOpens": { + "type": "boolean" + }, + "trackClicks": { + "type": "boolean" + }, + "openCount": { + "type": "number" + }, + "clickCount": { + "type": "number" + }, + "sentAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "html": { + "type": "string", + "nullable": true + }, + "text": { + "type": "string", + "nullable": true + } + }, + "required": [ + "txeId", + "to", + "from", + "replyTo", + "subject", + "templateId", + "variables", + "status", + "error", + "trackOpens", + "trackClicks", + "openCount", + "clickCount", + "sentAt", + "createdAt", + "updatedAt", + "html" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/events": { + "post": { + "description": "Stores the event, then starts every active sequence whose trigger is `event:custom` with this name and whose filters match. An unknown email creates the contact unless `createContact` is false (then 404). An unknown `contactId` is always 404. The same `idempotencyKey` within 30 days returns the original event with `duplicate: true`. Events with `backfill: true`, or an `occurredAt` more than 72 hours old, are stored but never start a sequence.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Track a custom event", + "tags": [ + "Events" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "contactId": { + "type": "string", + "minLength": 1 + }, + "eventName": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "properties": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "occurredAt": { + "type": "string", + "format": "date-time", + "description": "When it happened. At most 24 h in the future; defaults to now." + }, + "idempotencyKey": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "The same key within 30 days stores one event and enrolls once." + }, + "backfill": { + "type": "boolean", + "description": "Record only: the event never starts a sequence." + }, + "createContact": { + "type": "boolean", + "description": "Create the contact when the email is unknown (default true). Ignored with contactId." + }, + "contact": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "customFields": { + "default": {}, + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + }, + { + "type": "array", + "items": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "boolean" + } + ] + } + } + ] + } + } + }, + "additionalProperties": false, + "description": "Applied to the contact. Custom fields are merged into the existing ones." + } + }, + "required": [ + "eventName" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "202": { + "description": "202", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "eventId": { + "type": "string" + }, + "contactId": { + "type": "string" + }, + "duplicate": { + "type": "boolean" + }, + "contactCreated": { + "type": "boolean" + } + }, + "required": [ + "eventId", + "contactId", + "duplicate", + "contactCreated" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/events/definitions": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List the team's events", + "tags": [ + "Events" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "origin": { + "type": "string", + "enum": [ + "received", + "manual" + ] + }, + "description": { + "type": "string", + "nullable": true + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "date", + "object", + "array" + ] + }, + "declared": { + "type": "boolean" + } + }, + "required": [ + "key", + "type", + "declared" + ] + } + }, + "firstSeenAt": { + "type": "string", + "nullable": true + }, + "lastSeenAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "sequenceCount": { + "type": "integer" + } + }, + "required": [ + "name", + "origin", + "description", + "properties", + "firstSeenAt", + "lastSeenAt", + "createdAt", + "sequenceCount" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Register an event before it is sent", + "tags": [ + "Events" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "description": { + "type": "string", + "maxLength": 500 + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "date", + "object", + "array" + ] + } + }, + "required": [ + "key", + "type" + ] + }, + "maxItems": 100 + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "origin": { + "type": "string", + "enum": [ + "received", + "manual" + ] + }, + "description": { + "type": "string", + "nullable": true + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "date", + "object", + "array" + ] + }, + "declared": { + "type": "boolean" + } + }, + "required": [ + "key", + "type", + "declared" + ] + } + }, + "firstSeenAt": { + "type": "string", + "nullable": true + }, + "lastSeenAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "sequenceCount": { + "type": "integer" + }, + "lastProperties": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + } + }, + "required": [ + "name", + "origin", + "description", + "properties", + "firstSeenAt", + "lastSeenAt", + "createdAt", + "sequenceCount", + "lastProperties" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/events/definitions/{name}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get an event, with its last payload", + "tags": [ + "Events" + ], + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "origin": { + "type": "string", + "enum": [ + "received", + "manual" + ] + }, + "description": { + "type": "string", + "nullable": true + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "date", + "object", + "array" + ] + }, + "declared": { + "type": "boolean" + } + }, + "required": [ + "key", + "type", + "declared" + ] + } + }, + "firstSeenAt": { + "type": "string", + "nullable": true + }, + "lastSeenAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "sequenceCount": { + "type": "integer" + }, + "lastProperties": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + } + }, + "required": [ + "name", + "origin", + "description", + "properties", + "firstSeenAt", + "lastSeenAt", + "createdAt", + "sequenceCount", + "lastProperties" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update an event's description or declared properties", + "tags": [ + "Events" + ], + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "description": { + "type": "string", + "maxLength": 500, + "nullable": true + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "date", + "object", + "array" + ] + } + }, + "required": [ + "key", + "type" + ] + }, + "maxItems": 100, + "description": "Replaces the declared properties. Observed keys stay." + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "origin": { + "type": "string", + "enum": [ + "received", + "manual" + ] + }, + "description": { + "type": "string", + "nullable": true + }, + "properties": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string", + "pattern": "^[A-Za-z_][A-Za-z0-9_]{0,63}$" + }, + "type": { + "type": "string", + "enum": [ + "string", + "number", + "boolean", + "date", + "object", + "array" + ] + }, + "declared": { + "type": "boolean" + } + }, + "required": [ + "key", + "type", + "declared" + ] + } + }, + "firstSeenAt": { + "type": "string", + "nullable": true + }, + "lastSeenAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "sequenceCount": { + "type": "integer" + }, + "lastProperties": { + "type": "object", + "additionalProperties": { + "nullable": true + }, + "nullable": true + } + }, + "required": [ + "name", + "origin", + "description", + "properties", + "firstSeenAt", + "lastSeenAt", + "createdAt", + "sequenceCount", + "lastProperties" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Stored events are kept, and a later event with this name registers it again. Returns 409 event_definition_in_use while a sequence uses it as its trigger.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Remove an event from the list", + "tags": [ + "Events" + ], + "parameters": [ + { + "name": "name", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List the team's user-managed ESP configurations", + "tags": [ + "Settings" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create a user-managed ESP configuration", + "tags": [ + "Settings" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": [ + "smtp", + "sendgrid", + "mailgun", + "postmark", + "ses", + "resend" + ] + }, + "host": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "fromName": { + "type": "string" + }, + "fromEmail": { + "type": "string", + "format": "email" + }, + "replyTo": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ], + "description": "Where replies go, when not the from address. `\"\"` clears it." + }, + "maxSendRate": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": 0, + "maximum": 10000, + "nullable": true, + "description": "Messages per second to send through this ESP, across all workers. `null` uses the provider default (Amazon SES 14, Resend 2, others unlimited)." + }, + "dailySendLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true, + "description": "Most marketing emails (broadcasts and sequences) to send through this ESP per UTC day, e.g. while warming up a new domain. Emails over the cap wait for the next day. `null` removes the cap; transactional email is never capped." + }, + "warmupTargetDailyVolume": { + "type": "integer", + "minimum": 100, + "maximum": 10000000, + "nullable": true, + "description": "Turns on automatic warm-up up to this many marketing emails a day: the daily limit starts at 50 and grows each UTC day until it reaches the target, with the most engaged contacts sent to first. Setting it starts the warm-up from day 1 (changing the target of a running warm-up keeps its progress). `null` turns it off." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + }, + "required": [ + "provider", + "host", + "port", + "secure", + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a user-managed ESP configuration", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update a user-managed ESP configuration", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "provider": { + "type": "string", + "enum": [ + "smtp", + "sendgrid", + "mailgun", + "postmark", + "ses", + "resend" + ] + }, + "host": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "fromName": { + "type": "string" + }, + "fromEmail": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "replyTo": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "maxSendRate": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": 0, + "maximum": 10000, + "nullable": true, + "description": "Messages per second to send through this ESP, across all workers. `null` uses the provider default (Amazon SES 14, Resend 2, others unlimited)." + }, + "dailySendLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true, + "description": "Most marketing emails (broadcasts and sequences) to send through this ESP per UTC day, e.g. while warming up a new domain. Emails over the cap wait for the next day. `null` removes the cap; transactional email is never capped." + }, + "warmupTargetDailyVolume": { + "type": "integer", + "minimum": 100, + "maximum": 10000000, + "nullable": true, + "description": "Turns on automatic warm-up up to this many marketing emails a day: the daily limit starts at 50 and grows each UTC day until it reaches the target, with the most engaged contacts sent to first. Setting it starts the warm-up from day 1 (changing the target of a running warm-up keeps its progress). `null` turns it off." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Deletes an ESP that has never carried delivery history; delivery history is never deleted. Draft, paused, and completed sequences are detached first. 409 explains a blocked delete: `esp_is_default` (the team's live default; pick another default or retire it first), `delivery_source_in_use` (a sending sequence or broadcast, or queued email, needs it), or `esp_has_delivery_history` (transactional email, delivery feedback, or grants point at it; retire it instead).\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Remove a user-managed ESP configuration", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/domain-authentication": { + "get": { + "description": "Looks up the DNS records of the ESP's From address domain and returns each check's status with the records to add. Returns 422 `from_email_missing` when the ESP has no From address.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Check SPF, DKIM and DMARC for an ESP's sending domain", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "dkimSelector", + "in": "query", + "description": "DKIM selector to check (for Amazon SES, one Easy DKIM token). A selector that passes is saved on the ESP and used by later checks that omit it.", + "schema": { + "type": "string", + "pattern": "^[A-Za-z0-9._-]{1,63}$" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "provider": { + "type": "string", + "enum": [ + "smtp", + "sendgrid", + "mailgun", + "postmark", + "ses", + "resend" + ] + }, + "spf": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pass", + "warn", + "fail" + ] + }, + "message": { + "type": "string" + }, + "record": { + "type": "string", + "nullable": true + }, + "host": { + "type": "string" + } + }, + "required": [ + "status", + "message", + "record", + "host" + ] + }, + "dkim": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pass", + "warn", + "fail" + ] + }, + "message": { + "type": "string" + }, + "record": { + "type": "string", + "nullable": true + }, + "selector": { + "type": "string", + "nullable": true + } + }, + "required": [ + "status", + "message", + "record", + "selector" + ] + }, + "dmarc": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pass", + "warn", + "fail" + ] + }, + "message": { + "type": "string" + }, + "record": { + "type": "string", + "nullable": true + }, + "policy": { + "type": "string", + "nullable": true + } + }, + "required": [ + "status", + "message", + "record", + "policy" + ] + }, + "records": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "TXT", + "CNAME", + "MX" + ] + }, + "name": { + "type": "string" + }, + "value": { + "type": "string", + "nullable": true + }, + "purpose": { + "type": "string" + }, + "present": { + "type": "boolean" + } + }, + "required": [ + "type", + "name", + "value", + "purpose", + "present" + ] + } + }, + "providerConsoleUrl": { + "type": "string", + "nullable": true + }, + "checkedAt": { + "type": "string" + } + }, + "required": [ + "domain", + "provider", + "spf", + "dkim", + "dmarc", + "records", + "providerConsoleUrl", + "checkedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/test": { + "post": { + "description": "Requires a non-empty mailing address in the team's general settings.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Send a test email through a user-managed ESP", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "format": "email" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/activate": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Activate a verified team ESP", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/suspend": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Suspend a team ESP", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/resume": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Resume a team ESP", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/retire": { + "post": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Drain or cancel a team ESP", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "discriminator": { + "propertyName": "transition" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "transition": { + "type": "string", + "enum": [ + "drain" + ] + }, + "drainUntil": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "transition" + ] + }, + { + "type": "object", + "properties": { + "transition": { + "type": "string", + "enum": [ + "cancel" + ] + } + }, + "required": [ + "transition" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/general": { + "get": { + "description": "Returns defaults (all-null fields) when nothing has been saved yet.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get the team's general settings", + "tags": [ + "Settings" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "mailingAddress": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "mailingAddress" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "put": { + "description": "Omitted fields are left unchanged; send an empty string to clear a field.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update the team's general settings", + "tags": [ + "Settings" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "mailingAddress": { + "type": "string" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "mailingAddress": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "mailingAddress" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/tracking-domain": { + "get": { + "description": "Open, click, unsubscribe and \"view in browser\" links use the domain once it is verified; until then, and after it stops working, they use SendLit's own host.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get the team's custom tracking domain", + "tags": [ + "Settings" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "verified", + "failed" + ], + "nullable": true + }, + "cnameTarget": { + "type": "string" + }, + "error": { + "type": "string", + "nullable": true + }, + "verifiedAt": { + "type": "string", + "nullable": true + }, + "checkedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "domain", + "status", + "cnameTarget", + "error", + "verifiedAt", + "checkedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "put": { + "description": "Use a subdomain (for example `track.example.com`) with a CNAME record pointing to `cnameTarget`. A new domain is `pending` until it is verified; SendLit rechecks pending domains every hour. `null` removes it. Links in emails already sent on a previous domain keep working. 400 `tracking_domain_invalid`, 409 `tracking_domain_taken`.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Set or remove the team's custom tracking domain", + "tags": [ + "Settings" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "maxLength": 253, + "nullable": true + } + }, + "required": [ + "domain" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "verified", + "failed" + ], + "nullable": true + }, + "cnameTarget": { + "type": "string" + }, + "error": { + "type": "string", + "nullable": true + }, + "verifiedAt": { + "type": "string", + "nullable": true + }, + "checkedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "domain", + "status", + "cnameTarget", + "error", + "verifiedAt", + "checkedAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/tracking-domain/verify": { + "post": { + "description": "Checks the CNAME record and that SendLit answers over HTTPS on the domain. 400 `tracking_domain_not_set`.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Check the team's custom tracking domain now", + "tags": [ + "Settings" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "verified", + "failed" + ], + "nullable": true + }, + "cnameTarget": { + "type": "string" + }, + "error": { + "type": "string", + "nullable": true + }, + "verifiedAt": { + "type": "string", + "nullable": true + }, + "checkedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "domain", + "status", + "cnameTarget", + "error", + "verifiedAt", + "checkedAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/web": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get the team's web archive settings", + "tags": [ + "Web publishing" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "handle": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "signupFormId": { + "type": "string", + "nullable": true + }, + "archiveUrl": { + "type": "string", + "nullable": true + }, + "rssUrl": { + "type": "string", + "nullable": true + } + }, + "required": [ + "enabled", + "handle", + "title", + "description", + "signupFormId", + "archiveUrl", + "rssUrl" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "Turning hosted pages on picks a handle from the team name and creates a signup form for the archive when none is set. 409 `handle_taken` when another team uses the handle.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update the team's web archive settings", + "tags": [ + "Web publishing" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Host the archive and issue pages on SendLit; off when a platform renders them" + }, + "handle": { + "type": "string", + "minLength": 2, + "maxLength": 40, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "description": "Archive URL: /w/" + }, + "title": { + "type": "string", + "maxLength": 200, + "nullable": true + }, + "description": { + "type": "string", + "maxLength": 1000, + "nullable": true + }, + "signupFormId": { + "type": "string", + "nullable": true + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "handle": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "description": { + "type": "string", + "nullable": true + }, + "signupFormId": { + "type": "string", + "nullable": true + }, + "archiveUrl": { + "type": "string", + "nullable": true + }, + "rssUrl": { + "type": "string", + "nullable": true + } + }, + "required": [ + "enabled", + "handle", + "title", + "description", + "signupFormId", + "archiveUrl", + "rssUrl" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/teams": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "List the teams the current user belongs to", + "tags": [ + "Teams" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Create a new team", + "tags": [ + "Teams" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/teams/{teamId}": { + "patch": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Rename a team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Archive a team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/teams/{teamId}/keys": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "List a team's API keys", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "keyId": { + "type": "string" + }, + "keyPrefix": { + "type": "string" + }, + "name": { + "type": "string" + }, + "expiresAt": { + "type": "string", + "nullable": true + }, + "lastUsedAt": { + "type": "string", + "nullable": true + }, + "revokedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "keyId", + "keyPrefix", + "name" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "The response's `key` field is the full secret and is only ever returned once — store it securely. Subsequent listings only expose the key's prefix.\n\n**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Create a new API key for a team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "keyId": { + "type": "string" + }, + "keyPrefix": { + "type": "string" + }, + "name": { + "type": "string" + }, + "expiresAt": { + "type": "string", + "nullable": true + }, + "lastUsedAt": { + "type": "string", + "nullable": true + }, + "revokedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "key": { + "type": "string" + } + }, + "required": [ + "keyId", + "keyPrefix", + "name", + "key" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/teams/{teamId}/keys/{keyId}": { + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Revoke an API key by its id", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "keyId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "List organizations for the current user", + "tags": [ + "Organizations" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + } + }, + "required": [ + "organizationId", + "name", + "status", + "createdAt", + "updatedAt", + "role" + ] + } + }, + "ownsFreeOrganization": { + "type": "boolean" + } + }, + "required": [ + "items", + "ownsFreeOrganization" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Create an organization", + "tags": [ + "Organizations" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "organizationId", + "name", + "status", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get an organization", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "organizationId", + "name", + "status", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Update an organization", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "organizationId", + "name", + "status", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "Owner only, from a dashboard session. Fails with `409 active_subscription_exists` while a subscription is active or paid through a future date (cancel it in billing first), and with `409 billing_checkout_pending` while a checkout is open. Deleting closes the organization, archives its teams, revokes organization and team API keys and sending domains, retires every ESP config with its credentials wiped, and deletes its teams' MediaLit files. Billing history, audit events, delivery reporting and suppressions are kept.\n\n**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Delete an organization", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/abandon": { + "post": { + "description": "**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Hide a pending-payment organization after a cancelled checkout", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/members": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "List organization members", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + }, + "image": { + "type": "string", + "nullable": true + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "userId", + "email", + "name", + "image", + "role", + "createdAt", + "updatedAt" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Add an existing user to an organization", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + } + }, + "required": [ + "email", + "role" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + }, + "image": { + "type": "string", + "nullable": true + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "userId", + "email", + "name", + "image", + "role", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/members/{userId}": { + "patch": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Change an organization member role", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + } + }, + "required": [ + "role" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "userId": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "name": { + "type": "string" + }, + "image": { + "type": "string", + "nullable": true + }, + "role": { + "type": "string", + "enum": [ + "owner", + "admin", + "member" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "userId", + "email", + "name", + "image", + "role", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Remove an organization member", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/teams": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "List teams in an organization", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Create a human-managed team in an organization", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/teams/{teamId}": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get organization team metadata", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Rename an organization team", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "organizationId": { + "type": "string" + }, + "organizationName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + }, + "viewerIsMember": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "name", + "status" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Archive an organization team", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/keys": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "List organization API keys", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "keyId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "keyPrefix": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "organization:read", + "delivery:read", + "delivery:manage", + "teams:provision", + "teams:read", + "teams:manage", + "teams:keys", + "esps:read", + "esps:manage", + "grants:manage", + "usage:read" + ] + } + }, + "expiresAt": { + "type": "string", + "nullable": true + }, + "lastUsedAt": { + "type": "string", + "nullable": true + }, + "revokedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "keyId", + "name", + "keyPrefix", + "scopes", + "expiresAt", + "lastUsedAt", + "revokedAt", + "createdAt" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Create an organization API key", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "scopes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "organization:read", + "delivery:read", + "delivery:manage", + "teams:provision", + "teams:read", + "teams:manage", + "teams:keys", + "esps:read", + "esps:manage", + "grants:manage", + "usage:read" + ] + }, + "minItems": 1 + }, + "expiresAt": { + "type": "string", + "format": "date-time", + "nullable": true + } + }, + "required": [ + "name", + "scopes" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "keyId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "keyPrefix": { + "type": "string" + }, + "scopes": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "organization:read", + "delivery:read", + "delivery:manage", + "teams:provision", + "teams:read", + "teams:manage", + "teams:keys", + "esps:read", + "esps:manage", + "grants:manage", + "usage:read" + ] + } + }, + "expiresAt": { + "type": "string", + "nullable": true + }, + "lastUsedAt": { + "type": "string", + "nullable": true + }, + "revokedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "key": { + "type": "string" + } + }, + "required": [ + "keyId", + "name", + "keyPrefix", + "scopes", + "expiresAt", + "lastUsedAt", + "revokedAt", + "createdAt", + "key" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/keys/{keyId}": { + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Revoke an organization API key", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "keyId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/esps": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "List organization-owned ESP configurations", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Create an organization-owned ESP configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": [ + "smtp", + "sendgrid", + "mailgun", + "postmark", + "ses", + "resend" + ] + }, + "host": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "fromName": { + "type": "string" + }, + "fromEmail": { + "type": "string", + "format": "email" + }, + "replyTo": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ], + "description": "Where replies go, when not the from address. `\"\"` clears it." + }, + "maxSendRate": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": 0, + "maximum": 10000, + "nullable": true, + "description": "Messages per second to send through this ESP, across all workers. `null` uses the provider default (Amazon SES 14, Resend 2, others unlimited)." + }, + "dailySendLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true, + "description": "Most marketing emails (broadcasts and sequences) to send through this ESP per UTC day, e.g. while warming up a new domain. Emails over the cap wait for the next day. `null` removes the cap; transactional email is never capped." + }, + "warmupTargetDailyVolume": { + "type": "integer", + "minimum": 100, + "maximum": 10000000, + "nullable": true, + "description": "Turns on automatic warm-up up to this many marketing emails a day: the daily limit starts at 50 and grows each UTC day until it reaches the target, with the most engaged contacts sent to first. Setting it starts the warm-up from day 1 (changing the target of a running warm-up keeps its progress). `null` turns it off." + }, + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + } + }, + "required": [ + "provider", + "host", + "port", + "secure", + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get an organization-owned ESP configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Update an organization-owned ESP configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 100 + }, + "provider": { + "type": "string", + "enum": [ + "smtp", + "sendgrid", + "mailgun", + "postmark", + "ses", + "resend" + ] + }, + "host": { + "type": "string", + "minLength": 1 + }, + "port": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string" + }, + "password": { + "type": "string" + }, + "fromName": { + "type": "string" + }, + "fromEmail": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "replyTo": { + "oneOf": [ + { + "type": "string", + "format": "email" + }, + { + "type": "string", + "enum": [ + "" + ] + } + ] + }, + "maxSendRate": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": 0, + "maximum": 10000, + "nullable": true, + "description": "Messages per second to send through this ESP, across all workers. `null` uses the provider default (Amazon SES 14, Resend 2, others unlimited)." + }, + "dailySendLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true, + "description": "Most marketing emails (broadcasts and sequences) to send through this ESP per UTC day, e.g. while warming up a new domain. Emails over the cap wait for the next day. `null` removes the cap; transactional email is never capped." + }, + "warmupTargetDailyVolume": { + "type": "integer", + "minimum": 100, + "maximum": 10000000, + "nullable": true, + "description": "Turns on automatic warm-up up to this many marketing emails a day: the daily limit starts at 50 and grows each UTC day until it reaches the target, with the most engaged contacts sent to first. Setting it starts the warm-up from day 1 (changing the target of a running warm-up keeps its progress). `null` turns it off." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "delete": { + "description": "Only a never-activated draft with no history is deleted. 409 explains a blocked delete: `esp_retire_instead` (it has been activated; retire it instead), `esp_has_delivery_history`, or `delivery_source_in_use`.\n\n**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Delete an eligible never-activated organization ESP", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/test": { + "post": { + "description": "Organization API keys may test an ESP with an explicit `to` address; they have no signed-in user email to use as a fallback. Requires `esps:manage` and organization binding.\n\n**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Test an organization-owned ESP configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "to": { + "type": "string", + "format": "email" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "502": { + "description": "502", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/activate": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Activate a verified organization ESP", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/suspend": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Suspend an organization ESP", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/resume": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Resume an organization ESP", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/retire": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Drain or cancel an organization ESP", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "discriminator": { + "propertyName": "transition" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "transition": { + "type": "string", + "enum": [ + "drain" + ] + }, + "drainUntil": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "transition" + ] + }, + { + "type": "object", + "properties": { + "transition": { + "type": "string", + "enum": [ + "cancel" + ] + } + }, + "required": [ + "transition" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "host": { + "type": "string" + }, + "port": { + "type": "number" + }, + "secure": { + "type": "boolean" + }, + "username": { + "type": "string", + "nullable": true + }, + "hasPassword": { + "type": "boolean" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "maxSendRate": { + "type": "number", + "nullable": true + }, + "dailySendLimit": { + "type": "integer", + "nullable": true + }, + "sentToday": { + "type": "integer", + "nullable": true + }, + "warmup": { + "type": "object", + "properties": { + "targetDailyVolume": { + "type": "integer" + }, + "startedAt": { + "type": "string" + }, + "day": { + "type": "integer" + }, + "totalDays": { + "type": "integer" + }, + "todayLimit": { + "type": "integer" + }, + "complete": { + "type": "boolean" + } + }, + "required": [ + "targetDailyVolume", + "startedAt", + "day", + "totalDays", + "todayLimit", + "complete" + ], + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "draft", + "active", + "suspended", + "draining", + "retired" + ] + }, + "secretVersion": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "lastTestedAt": { + "type": "string", + "nullable": true + }, + "lastTestStatus": { + "type": "string", + "enum": [ + "success", + "failed" + ], + "nullable": true + }, + "lastTestError": { + "type": "string", + "nullable": true + }, + "activatedAt": { + "type": "string", + "nullable": true + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "retiredAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "espId", + "name", + "provider", + "host", + "port", + "secure", + "hasPassword", + "status", + "secretVersion" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/feedback": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get organization ESP feedback configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "hasCredential": { + "type": "boolean" + }, + "expectedTopicArn": { + "type": "string", + "nullable": true + }, + "subscriptionConfirmedAt": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "stale", + "error", + "retiring", + "disabled" + ] + }, + "lastReceivedAt": { + "type": "string", + "nullable": true + }, + "lastVerifiedAt": { + "type": "string", + "nullable": true + }, + "lastErrorCode": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "connectionId", + "espId", + "provider", + "webhookUrl", + "hasCredential", + "status" + ], + "nullable": true + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "put": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Configure organization ESP feedback", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "credential": { + "type": "string", + "minLength": 1 + }, + "expectedTopicArn": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "hasCredential": { + "type": "boolean" + }, + "expectedTopicArn": { + "type": "string", + "nullable": true + }, + "subscriptionConfirmedAt": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "stale", + "error", + "retiring", + "disabled" + ] + }, + "lastReceivedAt": { + "type": "string", + "nullable": true + }, + "lastVerifiedAt": { + "type": "string", + "nullable": true + }, + "lastErrorCode": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "connectionId", + "espId", + "provider", + "webhookUrl", + "hasCredential", + "status" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Disable organization ESP feedback configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/feedback/rotate": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Rotate organization ESP feedback credentials", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "credential": { + "type": "string", + "minLength": 1 + }, + "expectedTopicArn": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "hasCredential": { + "type": "boolean" + }, + "expectedTopicArn": { + "type": "string", + "nullable": true + }, + "subscriptionConfirmedAt": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "stale", + "error", + "retiring", + "disabled" + ] + }, + "lastReceivedAt": { + "type": "string", + "nullable": true + }, + "lastVerifiedAt": { + "type": "string", + "nullable": true + }, + "lastErrorCode": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "connectionId", + "espId", + "provider", + "webhookUrl", + "hasCredential", + "status" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/esps/{espId}/feedback/test": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Verify organization ESP feedback configuration", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/delivery-policy": { + "get": { + "description": "Organization API keys require the delivery:read scope and must be bound to the path organization. Owner/admin user sessions remain supported.\n\n**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get organization delivery policy", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "defaultEspId": { + "type": "string", + "nullable": true + }, + "autoGrantDefaultEsp": { + "type": "boolean" + }, + "defaultDailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "defaultMonthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "aggregateDailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "aggregateMonthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "teamEspEnabledByDefault": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "defaultEspId", + "autoGrantDefaultEsp", + "defaultDailyLimit", + "defaultMonthlyLimit", + "aggregateDailyLimit", + "aggregateMonthlyLimit", + "teamEspEnabledByDefault", + "teamCanChangeDefault", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "put": { + "description": "Organization API keys require the delivery:manage scope and must be bound to the path organization. This scope authorizes every field in the delivery-policy schema, including shared quotas and team delivery controls. Owner/admin user sessions remain supported.\n\n**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Update organization delivery policy", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "defaultEspId": { + "type": "string", + "nullable": true + }, + "autoGrantDefaultEsp": { + "type": "boolean" + }, + "defaultDailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "defaultMonthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "aggregateDailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "aggregateMonthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "teamEspEnabledByDefault": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "defaultEspId": { + "type": "string", + "nullable": true + }, + "autoGrantDefaultEsp": { + "type": "boolean" + }, + "defaultDailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "defaultMonthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "aggregateDailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "aggregateMonthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "teamEspEnabledByDefault": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "defaultEspId", + "autoGrantDefaultEsp", + "defaultDailyLimit", + "defaultMonthlyLimit", + "aggregateDailyLimit", + "aggregateMonthlyLimit", + "teamEspEnabledByDefault", + "teamCanChangeDefault", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/usage": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get aggregate organization delivery quota usage", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "day": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "nullable": true + }, + "accepted": { + "type": "integer", + "minimum": 0 + }, + "reserved": { + "type": "integer", + "minimum": 0 + }, + "remaining": { + "type": "integer", + "nullable": true + }, + "resetsAt": { + "type": "string" + } + }, + "required": [ + "limit", + "accepted", + "reserved", + "remaining", + "resetsAt" + ] + }, + "month": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "nullable": true + }, + "accepted": { + "type": "integer", + "minimum": 0 + }, + "reserved": { + "type": "integer", + "minimum": 0 + }, + "remaining": { + "type": "integer", + "nullable": true + }, + "resetsAt": { + "type": "string" + } + }, + "required": [ + "limit", + "accepted", + "reserved", + "remaining", + "resetsAt" + ] + } + }, + "required": [ + "day", + "month" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/mail-activity": { + "get": { + "description": "Returns per-team and organization-total transactional mail counts (`sent`, `queued`, `failed`, `bounced`) for a window of 1, 3, 7, or 30 days (default 7). Includes teams with zero activity. Does not include campaigns, broadcasts, sequences, recipients, subjects, or email bodies. Shared-delivery quota remains on `/usage`.\n\n**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get organization transactional mail activity", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "rangeDays", + "in": "query", + "schema": { + "type": "integer", + "nullable": true + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "rangeDays": { + "type": "number", + "enum": [ + 1, + 3, + 7, + 30 + ] + }, + "totals": { + "type": "object", + "properties": { + "sent": { + "type": "integer", + "minimum": 0 + }, + "queued": { + "type": "integer", + "minimum": 0 + }, + "failed": { + "type": "integer", + "minimum": 0 + }, + "bounced": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "sent", + "queued", + "failed", + "bounced" + ] + }, + "teams": { + "type": "array", + "items": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "externalId": { + "type": "string", + "nullable": true + }, + "mail": { + "type": "object", + "properties": { + "sent": { + "type": "integer", + "minimum": 0 + }, + "queued": { + "type": "integer", + "minimum": 0 + }, + "failed": { + "type": "integer", + "minimum": 0 + }, + "bounced": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "sent", + "queued", + "failed", + "bounced" + ] + } + }, + "required": [ + "teamId", + "name", + "status", + "externalId", + "mail" + ] + } + } + }, + "required": [ + "rangeDays", + "totals", + "teams" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/teams/{teamId}/enter": { + "post": { + "description": "Grants the calling human organization owner or administrator a permanent team `admin` membership, recorded as organization audit action `team.entered`. Organization API keys cannot enter. Archived teams cannot be entered. Idempotent if membership already exists.\n\n**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Enter an organization team as a human admin", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {} + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "role": { + "type": "string", + "enum": [ + "admin", + "member" + ] + }, + "created": { + "type": "boolean" + } + }, + "required": [ + "teamId", + "role", + "created" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/audit-events": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "List recent organization audit events", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "action": { + "type": "string" + }, + "actorType": { + "type": "string", + "enum": [ + "user", + "organization_key", + "team_key", + "system" + ] + }, + "teamId": { + "type": "string", + "nullable": true + }, + "espId": { + "type": "string", + "nullable": true + }, + "grantId": { + "type": "string", + "nullable": true + }, + "metadata": { + "type": "object", + "additionalProperties": { + "nullable": true + } + }, + "createdAt": { + "type": "string" + } + }, + "required": [ + "action", + "actorType", + "teamId", + "espId", + "grantId", + "metadata", + "createdAt" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/teams/{teamId}/esp-grant": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Get a team's organization ESP grant", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "grantId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "draining", + "suspended", + "revoked" + ] + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "fromName": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "dailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "monthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "grantId", + "teamId", + "espId", + "status", + "drainUntil", + "fromName", + "replyTo", + "dailyLimit", + "monthlyLimit", + "createdAt", + "updatedAt" + ], + "nullable": true + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + }, + "put": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Create or update a team's organization ESP grant", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "espId": { + "type": "string" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "format": "email", + "nullable": true + }, + "dailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "monthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "makeDefault": { + "type": "boolean" + } + }, + "required": [ + "espId" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "grantId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "draining", + "suspended", + "revoked" + ] + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "fromName": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "dailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "monthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "grantId", + "teamId", + "espId", + "status", + "drainUntil", + "fromName", + "replyTo", + "dailyLimit", + "monthlyLimit", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/teams/{teamId}/esp-grant/transition": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or dashboard session, or a scoped organization API key. The caller must also have the required organization role or key scope.", + "summary": "Transition a team's organization ESP grant; revoking detaches safe campaigns", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "discriminator": { + "propertyName": "action" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "suspend" + ] + } + }, + "required": [ + "action" + ] + }, + { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "resume" + ] + } + }, + "required": [ + "action" + ] + }, + { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "drain" + ] + }, + "drainUntil": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "action" + ] + }, + { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "cancel" + ] + } + }, + "required": [ + "action" + ] + } + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "grantId": { + "type": "string" + }, + "teamId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "draining", + "suspended", + "revoked" + ] + }, + "drainUntil": { + "type": "string", + "nullable": true + }, + "fromName": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "dailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "monthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "grantId", + "teamId", + "espId", + "status", + "drainUntil", + "fromName", + "replyTo", + "dailyLimit", + "monthlyLimit", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + }, + { + "organizationApiKey": [] + } + ] + } + }, + "/organizations/{organizationId}/sending-domains": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "List verified sending domains", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domainId": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "verified", + "revoked", + "failed" + ] + }, + "verifiedAt": { + "type": "string", + "nullable": true + }, + "lastCheckedAt": { + "type": "string", + "nullable": true + }, + "nextCheckAt": { + "type": "string", + "nullable": true + }, + "challengeToken": { + "type": "string", + "nullable": true + }, + "challengeRecordName": { + "type": "string", + "nullable": true + }, + "challengeRecordValue": { + "type": "string", + "nullable": true + } + }, + "required": [ + "domainId", + "domain", + "status", + "verifiedAt", + "lastCheckedAt", + "nextCheckAt", + "challengeToken", + "challengeRecordName", + "challengeRecordValue" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Create a DNS verification challenge for a sending domain", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domain": { + "type": "string", + "minLength": 1, + "maxLength": 253 + } + }, + "required": [ + "domain" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domainId": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "verified", + "revoked", + "failed" + ] + }, + "verifiedAt": { + "type": "string", + "nullable": true + }, + "lastCheckedAt": { + "type": "string", + "nullable": true + }, + "nextCheckAt": { + "type": "string", + "nullable": true + }, + "challengeToken": { + "type": "string", + "nullable": true + }, + "challengeRecordName": { + "type": "string", + "nullable": true + }, + "challengeRecordValue": { + "type": "string", + "nullable": true + } + }, + "required": [ + "domainId", + "domain", + "status", + "verifiedAt", + "lastCheckedAt", + "nextCheckAt", + "challengeToken", + "challengeRecordName", + "challengeRecordValue" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/sending-domains/{domainId}/verify": { + "post": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Refresh DNS verification for a sending domain", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "domainId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "domainId": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "verified", + "revoked", + "failed" + ] + }, + "verifiedAt": { + "type": "string", + "nullable": true + }, + "lastCheckedAt": { + "type": "string", + "nullable": true + }, + "nextCheckAt": { + "type": "string", + "nullable": true + }, + "challengeToken": { + "type": "string", + "nullable": true + }, + "challengeRecordName": { + "type": "string", + "nullable": true + }, + "challengeRecordValue": { + "type": "string", + "nullable": true + } + }, + "required": [ + "domainId", + "domain", + "status", + "verifiedAt", + "lastCheckedAt", + "nextCheckAt", + "challengeToken", + "challengeRecordName", + "challengeRecordValue" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/sending-domains/{domainId}": { + "delete": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Revoke a sending domain", + "tags": [ + "Organizations" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "domainId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/sending-options": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List sanitized sending options for the active team", + "tags": [ + "Delivery" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + }, + "name": { + "type": "string" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "replyTo": { + "type": "string", + "nullable": true + }, + "isDefault": { + "type": "boolean" + }, + "available": { + "type": "boolean" + }, + "countsAgainstQuota": { + "type": "boolean", + "enum": [ + true + ] + } + }, + "required": [ + "type", + "name", + "fromName", + "fromEmail", + "replyTo", + "isDefault", + "available", + "countsAgainstQuota" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "fromName": { + "type": "string", + "nullable": true + }, + "fromEmail": { + "type": "string", + "nullable": true + }, + "isDefault": { + "type": "boolean" + }, + "available": { + "type": "boolean" + }, + "countsAgainstQuota": { + "type": "boolean", + "enum": [ + false + ] + } + }, + "required": [ + "type", + "espId", + "name", + "fromName", + "fromEmail", + "isDefault", + "available", + "countsAgainstQuota" + ] + } + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/delivery": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get team delivery settings", + "tags": [ + "Delivery" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "defaultSource": { + "type": "string", + "enum": [ + "organization", + "team" + ], + "nullable": true + }, + "defaultTeamEspId": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "teamEspEnabled", + "teamCanChangeDefault", + "defaultSource", + "defaultTeamEspId", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Choose the team's default delivery source", + "tags": [ + "Delivery" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "deliverySource": { + "discriminator": { + "propertyName": "type" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "team" + ] + }, + "espId": { + "type": "string" + } + }, + "required": [ + "type", + "espId" + ] + } + ] + } + }, + "required": [ + "deliverySource" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "defaultSource": { + "type": "string", + "enum": [ + "organization", + "team" + ], + "nullable": true + }, + "defaultTeamEspId": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "teamEspEnabled", + "teamCanChangeDefault", + "defaultSource", + "defaultTeamEspId", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/provisioning/organization": { + "get": { + "description": "Returns only the organization associated with the authenticated organization API key. Requires the organization:read scope; the organization cannot be selected in the request.\n\n**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Get the organization bound to the calling API key", + "tags": [ + "Teams" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "organizationId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending_payment", + "active", + "suspended", + "abandoned", + "closed" + ] + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "organizationId", + "name", + "status", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/provisioning/teams": { + "post": { + "description": "Server-to-server endpoint for multi-tenant consumers (e.g. CourseLit) to provision one SendLit team per tenant. Requires a scoped organization key as a Bearer token.\n\n**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Find-or-create a team for a consumer-supplied tenant id", + "tags": [ + "Teams" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "externalId": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string", + "minLength": 1 + }, + "sender": { + "type": "object", + "properties": { + "fromName": { + "type": "string", + "minLength": 1 + }, + "replyTo": { + "type": "string", + "format": "email" + } + } + }, + "mailingAddress": { + "type": "string", + "minLength": 1 + }, + "delivery": { + "type": "object", + "properties": { + "useOrganizationDefault": { + "type": "boolean" + }, + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + } + } + }, + "quota": { + "type": "object", + "properties": { + "dailyLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "monthlyLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + } + } + } + }, + "required": [ + "externalId", + "name" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "discriminator": { + "propertyName": "created" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "externalId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "deliverySource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + "created": { + "type": "boolean", + "enum": [ + true + ] + }, + "apiKey": { + "type": "string" + } + }, + "required": [ + "teamId", + "externalId", + "name", + "deliverySource", + "created", + "apiKey" + ] + }, + { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "externalId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "deliverySource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + "created": { + "type": "boolean", + "enum": [ + false + ] + }, + "apiKey": { + "type": "string", + "format": "null", + "nullable": true + } + }, + "required": [ + "teamId", + "externalId", + "name", + "deliverySource", + "created", + "apiKey" + ] + } + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "500": { + "description": "500", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/provisioning/teams/{teamId}": { + "get": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Get a provisioned team within the authenticated organization", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "externalId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "deliverySource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "mailingAddress": { + "type": "string", + "nullable": true + }, + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "teamId", + "externalId", + "name", + "deliverySource", + "status", + "mailingAddress", + "teamEspEnabled", + "teamCanChangeDefault", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + }, + "patch": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Update a provisioned team's sender, limits, or settings", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + }, + "sender": { + "type": "object", + "properties": { + "fromName": { + "type": "string", + "minLength": 1, + "nullable": true + }, + "replyTo": { + "type": "string", + "format": "email", + "nullable": true + } + } + }, + "mailingAddress": { + "type": "string", + "minLength": 1, + "nullable": true + }, + "delivery": { + "type": "object", + "properties": { + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + } + } + }, + "quota": { + "type": "object", + "properties": { + "dailyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + }, + "monthlyLimit": { + "type": "integer", + "minimum": 0, + "nullable": true + } + } + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "externalId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "deliverySource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "mailingAddress": { + "type": "string", + "nullable": true + }, + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "teamId", + "externalId", + "name", + "deliverySource", + "status", + "mailingAddress", + "teamEspEnabled", + "teamCanChangeDefault", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Archive a provisioned team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/provisioning/teams/{teamId}/keys": { + "post": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Replace a provisioned team's integration key", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "keyId": { + "type": "string" + }, + "keyPrefix": { + "type": "string" + }, + "name": { + "type": "string" + }, + "expiresAt": { + "type": "string", + "nullable": true + }, + "lastUsedAt": { + "type": "string", + "nullable": true + }, + "revokedAt": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "key": { + "type": "string" + } + }, + "required": [ + "keyId", + "keyPrefix", + "name", + "key" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/provisioning/teams/{teamId}/suspend": { + "post": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Suspend new sends for a provisioned team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "externalId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "deliverySource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "mailingAddress": { + "type": "string", + "nullable": true + }, + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "teamId", + "externalId", + "name", + "deliverySource", + "status", + "mailingAddress", + "teamEspEnabled", + "teamCanChangeDefault", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/provisioning/teams/{teamId}/resume": { + "post": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Resume sends for a provisioned team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "teamId": { + "type": "string" + }, + "externalId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "deliverySource": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "organization" + ] + } + }, + "required": [ + "type" + ] + }, + "status": { + "type": "string", + "enum": [ + "active", + "sending_suspended", + "archived" + ] + }, + "mailingAddress": { + "type": "string", + "nullable": true + }, + "teamEspEnabled": { + "type": "boolean" + }, + "teamCanChangeDefault": { + "type": "boolean" + }, + "createdAt": { + "type": "string", + "nullable": true + }, + "updatedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "teamId", + "externalId", + "name", + "deliverySource", + "status", + "mailingAddress", + "teamEspEnabled", + "teamCanChangeDefault", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/provisioning/teams/{teamId}/usage": { + "get": { + "description": "**Authentication:** Requires a scoped organization API key sent as a Bearer token.", + "summary": "Get organization-delivery quota usage for a provisioned team", + "tags": [ + "Teams" + ], + "parameters": [ + { + "name": "teamId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "day": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "nullable": true + }, + "accepted": { + "type": "integer", + "minimum": 0 + }, + "reserved": { + "type": "integer", + "minimum": 0 + }, + "remaining": { + "type": "integer", + "nullable": true + }, + "resetsAt": { + "type": "string" + } + }, + "required": [ + "limit", + "accepted", + "reserved", + "remaining", + "resetsAt" + ] + }, + "month": { + "type": "object", + "properties": { + "limit": { + "type": "integer", + "nullable": true + }, + "accepted": { + "type": "integer", + "minimum": 0 + }, + "reserved": { + "type": "integer", + "minimum": 0 + }, + "remaining": { + "type": "integer", + "nullable": true + }, + "resetsAt": { + "type": "string" + } + }, + "required": [ + "limit", + "accepted", + "reserved", + "remaining", + "resetsAt" + ] + } + }, + "required": [ + "day", + "month" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "organizationApiKey": [] + } + ] + } + }, + "/overview": { + "get": { + "description": "Use `rangeDays` (1, 3, 7, or 30; default 7) to filter transactional delivery activity and scheduled broadcasts.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get team overview metrics for a delivery activity window", + "tags": [ + "Overview" + ], + "parameters": [ + { + "name": "rangeDays", + "in": "query", + "schema": { + "type": "integer", + "nullable": true + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "activeSequences": { + "type": "number" + }, + "ongoingContacts": { + "type": "number" + }, + "scheduledBroadcasts": { + "type": "number" + }, + "mail": { + "type": "object", + "properties": { + "sent": { + "type": "number" + }, + "queued": { + "type": "number" + }, + "failed": { + "type": "number" + }, + "bounced": { + "type": "number" + } + }, + "required": [ + "sent", + "queued", + "failed", + "bounced" + ] + }, + "quota": { + "type": "object", + "properties": { + "dailyUsed": { + "type": "number" + }, + "dailyLimit": { + "type": "number" + }, + "monthlyUsed": { + "type": "number" + }, + "monthlyLimit": { + "type": "number" + } + }, + "required": [ + "dailyUsed", + "dailyLimit", + "monthlyUsed", + "monthlyLimit" + ] + } + }, + "required": [ + "activeSequences", + "ongoingContacts", + "scheduledBroadcasts", + "mail", + "quota" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/feedback": { + "get": { + "description": "Returns null when feedback hasn't been configured for this ESP yet.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a user ESP's delivery-feedback connection", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "hasCredential": { + "type": "boolean" + }, + "expectedTopicArn": { + "type": "string", + "nullable": true + }, + "subscriptionConfirmedAt": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "stale", + "error", + "retiring", + "disabled" + ] + }, + "lastReceivedAt": { + "type": "string", + "nullable": true + }, + "lastVerifiedAt": { + "type": "string", + "nullable": true + }, + "lastErrorCode": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "connectionId", + "espId", + "provider", + "webhookUrl", + "hasCredential", + "status" + ], + "nullable": true + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "put": { + "description": "Creates the connection (and its stable webhook URL) on first call; a later call rotates the credential without changing the URL. The provider is always the ESP's current provider, not client-writable. Amazon SES takes `expectedTopicArn` (the SNS topic subscribed to the webhook URL) instead of `credential`; SendLit confirms the SNS subscription automatically. Fails with 400 `feedback_credential_required`, `feedback_topic_arn_required`, or `feedback_topic_arn_invalid` when the body doesn't fit the provider.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create or rotate a user ESP's delivery-feedback connection", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "credential": { + "type": "string", + "minLength": 1 + }, + "expectedTopicArn": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "hasCredential": { + "type": "boolean" + }, + "expectedTopicArn": { + "type": "string", + "nullable": true + }, + "subscriptionConfirmedAt": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "stale", + "error", + "retiring", + "disabled" + ] + }, + "lastReceivedAt": { + "type": "string", + "nullable": true + }, + "lastVerifiedAt": { + "type": "string", + "nullable": true + }, + "lastErrorCode": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "connectionId", + "espId", + "provider", + "webhookUrl", + "hasCredential", + "status" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Disable a user ESP's delivery-feedback connection", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/feedback/rotate": { + "post": { + "description": "Same effect as PUT, but fails with 404 if no connection exists yet. The previous credential remains valid for 24h so in-flight provider retries aren't lost.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Rotate an existing feedback connection's credential", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "credential": { + "type": "string", + "minLength": 1 + }, + "expectedTopicArn": { + "type": "string", + "minLength": 1 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connectionId": { + "type": "string" + }, + "espId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "webhookUrl": { + "type": "string" + }, + "hasCredential": { + "type": "boolean" + }, + "expectedTopicArn": { + "type": "string", + "nullable": true + }, + "subscriptionConfirmedAt": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "stale", + "error", + "retiring", + "disabled" + ] + }, + "lastReceivedAt": { + "type": "string", + "nullable": true + }, + "lastVerifiedAt": { + "type": "string", + "nullable": true + }, + "lastErrorCode": { + "type": "string", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "connectionId", + "espId", + "provider", + "webhookUrl", + "hasCredential", + "status" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/settings/esps/{espId}/feedback/test": { + "post": { + "description": "A smoke test that does not call the provider. Fails with `feedback_invalid_credentials` when the stored credential can't be read, or for Amazon SES with `feedback_subscription_unconfirmed` until SNS has confirmed the subscription.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Verify a feedback connection's stored credential", + "tags": [ + "Settings" + ], + "parameters": [ + { + "name": "espId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean" + }, + "error": { + "type": "string" + } + }, + "required": [ + "success" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/delivery-events": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List normalized delivery events for the team", + "tags": [ + "Delivery" + ], + "parameters": [ + { + "name": "espId", + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "deliverySourceType", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "organization", + "team" + ] + } + }, + { + "name": "eventType", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "accepted", + "delivered", + "delayed", + "soft_bounce", + "hard_bounce", + "failed", + "complaint", + "suppressed", + "rejected", + "unknown" + ] + } + }, + { + "name": "createdAfter", + "in": "query", + "schema": { + "type": "integer", + "nullable": true + } + }, + { + "name": "createdBefore", + "in": "query", + "schema": { + "type": "integer", + "nullable": true + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "itemsPerPage", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "eventId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "espId": { + "type": "string", + "nullable": true + }, + "deliverySourceType": { + "type": "string", + "enum": [ + "organization", + "team" + ], + "nullable": true + }, + "messageId": { + "type": "string", + "nullable": true + }, + "recipientEmail": { + "type": "string", + "nullable": true + }, + "eventType": { + "type": "string", + "enum": [ + "accepted", + "delivered", + "delayed", + "soft_bounce", + "hard_bounce", + "failed", + "complaint", + "suppressed", + "rejected", + "unknown" + ] + }, + "bounceClass": { + "type": "string", + "enum": [ + "permanent", + "transient", + "undetermined" + ], + "nullable": true + }, + "smtpCode": { + "type": "number", + "nullable": true + }, + "enhancedStatusCode": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "nullable": true + }, + "occurredAt": { + "type": "string" + }, + "receivedAt": { + "type": "string" + } + }, + "required": [ + "eventId", + "provider", + "espId", + "deliverySourceType", + "messageId", + "recipientEmail", + "eventType", + "occurredAt", + "receivedAt" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/delivery-events/{eventId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a single normalized delivery event", + "tags": [ + "Delivery" + ], + "parameters": [ + { + "name": "eventId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "eventId": { + "type": "string" + }, + "provider": { + "type": "string" + }, + "espId": { + "type": "string", + "nullable": true + }, + "deliverySourceType": { + "type": "string", + "enum": [ + "organization", + "team" + ], + "nullable": true + }, + "messageId": { + "type": "string", + "nullable": true + }, + "recipientEmail": { + "type": "string", + "nullable": true + }, + "eventType": { + "type": "string", + "enum": [ + "accepted", + "delivered", + "delayed", + "soft_bounce", + "hard_bounce", + "failed", + "complaint", + "suppressed", + "rejected", + "unknown" + ] + }, + "bounceClass": { + "type": "string", + "enum": [ + "permanent", + "transient", + "undetermined" + ], + "nullable": true + }, + "smtpCode": { + "type": "number", + "nullable": true + }, + "enhancedStatusCode": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "nullable": true + }, + "occurredAt": { + "type": "string" + }, + "receivedAt": { + "type": "string" + } + }, + "required": [ + "eventId", + "provider", + "espId", + "deliverySourceType", + "messageId", + "recipientEmail", + "eventType", + "occurredAt", + "receivedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/suppressions": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List the team's suppressed recipients", + "tags": [ + "Delivery" + ], + "parameters": [ + { + "name": "active", + "in": "query", + "schema": { + "type": "boolean", + "nullable": true + } + }, + { + "name": "reason", + "in": "query", + "schema": { + "type": "string", + "enum": [ + "hard_bounce", + "complaint", + "repeated_soft_bounce", + "provider_suppression", + "manual" + ] + } + }, + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "itemsPerPage", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "suppressionId": { + "type": "string" + }, + "recipientEmail": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "enum": [ + "hard_bounce", + "complaint", + "repeated_soft_bounce", + "provider_suppression", + "manual" + ] + }, + "active": { + "type": "boolean" + }, + "firstSuppressedAt": { + "type": "string" + }, + "lastSuppressedAt": { + "type": "string" + }, + "releasedAt": { + "type": "string", + "nullable": true + }, + "releaseReason": { + "type": "string", + "nullable": true + } + }, + "required": [ + "suppressionId", + "recipientEmail", + "reason", + "active", + "firstSuppressedAt", + "lastSuppressedAt" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "Adds a manual suppression: SendLit never mails the address again from this team (broadcasts, sequences or transactional) until the suppression is released. Use it for role accounts, legal requests or known spam traps. An existing stronger suppression (for example a complaint) is kept.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Suppress an address", + "tags": [ + "Delivery" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email" + }, + "note": { + "type": "string", + "maxLength": 2000, + "description": "Why the address is blocked, e.g. a legal request; kept in the audit trail" + } + }, + "required": [ + "email" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "suppressionId": { + "type": "string" + }, + "recipientEmail": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "enum": [ + "hard_bounce", + "complaint", + "repeated_soft_bounce", + "provider_suppression", + "manual" + ] + }, + "active": { + "type": "boolean" + }, + "firstSuppressedAt": { + "type": "string" + }, + "lastSuppressedAt": { + "type": "string" + }, + "releasedAt": { + "type": "string", + "nullable": true + }, + "releaseReason": { + "type": "string", + "nullable": true + } + }, + "required": [ + "suppressionId", + "recipientEmail", + "reason", + "active", + "firstSuppressedAt", + "lastSuppressedAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/suppressions/{suppressionId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a single suppression", + "tags": [ + "Delivery" + ], + "parameters": [ + { + "name": "suppressionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "suppressionId": { + "type": "string" + }, + "recipientEmail": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "enum": [ + "hard_bounce", + "complaint", + "repeated_soft_bounce", + "provider_suppression", + "manual" + ] + }, + "active": { + "type": "boolean" + }, + "firstSuppressedAt": { + "type": "string" + }, + "lastSuppressedAt": { + "type": "string" + }, + "releasedAt": { + "type": "string", + "nullable": true + }, + "releaseReason": { + "type": "string", + "nullable": true + } + }, + "required": [ + "suppressionId", + "recipientEmail", + "reason", + "active", + "firstSuppressedAt", + "lastSuppressedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/suppressions/{suppressionId}/release": { + "post": { + "description": "A workspace owner may release hard_bounce, repeated_soft_bounce, and manual suppressions only. Complaint suppressions cannot be released through this endpoint (409 suppression_not_releasable) — see the PRD's reactivation policy.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Release an active suppression", + "tags": [ + "Delivery" + ], + "parameters": [ + { + "name": "suppressionId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "explanation": { + "type": "string", + "minLength": 1, + "maxLength": 2000 + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "suppressionId": { + "type": "string" + }, + "recipientEmail": { + "type": "string", + "nullable": true + }, + "reason": { + "type": "string", + "enum": [ + "hard_bounce", + "complaint", + "repeated_soft_bounce", + "provider_suppression", + "manual" + ] + }, + "active": { + "type": "boolean" + }, + "firstSuppressedAt": { + "type": "string" + }, + "lastSuppressedAt": { + "type": "string" + }, + "releasedAt": { + "type": "string", + "nullable": true + }, + "releaseReason": { + "type": "string", + "nullable": true + } + }, + "required": [ + "suppressionId", + "recipientEmail", + "reason", + "active", + "firstSuppressedAt", + "lastSuppressedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/forms": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List signup forms", + "tags": [ + "Contacts" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "formId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "doubleOptIn": { + "type": "boolean" + }, + "redirectUrl": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string" + }, + "sendingIssue": { + "type": "string", + "description": "Why this double opt-in form can't email confirmation links right now (e.g. the team has no sending mailbox); null when it can. Submissions fail until it's fixed.", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "formId", + "name", + "tags", + "doubleOptIn", + "redirectUrl", + "url", + "sendingIssue", + "createdAt", + "updatedAt" + ] + } + } + }, + "required": [ + "items" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "post": { + "description": "A public signup form for the team. People subscribe on its hosted page (`url`) or through an HTML form on your site that posts `email` (and optionally `name`) to the same URL, as a form post or JSON. Double opt-in (the default) emails a confirmation link through the team's delivery source and subscribes only once it is clicked, so it needs a sending mailbox: without one, creating a double opt-in form (or switching a form to it) fails with 422 `confirmation_sender_unavailable`, and an existing form's `sendingIssue` says why submissions are failing; single opt-in never resubscribes someone who unsubscribed. Submissions are rate limited per IP and per form, and suppressed or disposable addresses are dropped. A new subscriber triggers `subscriber:added` sequences.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Create a signup form", + "tags": [ + "Contacts" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "maxItems": 20, + "description": "Added to everyone who subscribes through the form" + }, + "doubleOptIn": { + "type": "boolean", + "description": "Send a confirmation email and subscribe only once it's clicked (default true)" + }, + "redirectUrl": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Where to send people after they submit the hosted or embedded form" + } + }, + "required": [ + "name" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "formId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "doubleOptIn": { + "type": "boolean" + }, + "redirectUrl": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string" + }, + "sendingIssue": { + "type": "string", + "description": "Why this double opt-in form can't email confirmation links right now (e.g. the team has no sending mailbox); null when it can. Submissions fail until it's fixed.", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "formId", + "name", + "tags", + "doubleOptIn", + "redirectUrl", + "url", + "sendingIssue", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "confirmation_sender_unavailable" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "error", + "message" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/forms/{formId}": { + "get": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a signup form", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "formId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "formId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "doubleOptIn": { + "type": "boolean" + }, + "redirectUrl": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string" + }, + "sendingIssue": { + "type": "string", + "description": "Why this double opt-in form can't email confirmation links right now (e.g. the team has no sending mailbox); null when it can. Submissions fail until it's fixed.", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "formId", + "name", + "tags", + "doubleOptIn", + "redirectUrl", + "url", + "sendingIssue", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "patch": { + "description": "Turning double opt-in on fails with 422 `confirmation_sender_unavailable` when the team can't send the confirmation email.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Update a signup form", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "formId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200 + }, + "tags": { + "type": "array", + "items": { + "type": "string", + "minLength": 1 + }, + "maxItems": 20, + "description": "Added to everyone who subscribes through the form" + }, + "doubleOptIn": { + "type": "boolean", + "description": "Send a confirmation email and subscribe only once it's clicked (default true)" + }, + "redirectUrl": { + "type": "string", + "format": "uri", + "nullable": true, + "description": "Where to send people after they submit the hosted or embedded form" + } + } + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "formId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "doubleOptIn": { + "type": "boolean" + }, + "redirectUrl": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string" + }, + "sendingIssue": { + "type": "string", + "description": "Why this double opt-in form can't email confirmation links right now (e.g. the team has no sending mailbox); null when it can. Submissions fail until it's fixed.", + "nullable": true + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "formId", + "name", + "tags", + "doubleOptIn", + "redirectUrl", + "url", + "sendingIssue", + "createdAt", + "updatedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "enum": [ + "confirmation_sender_unavailable" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "error", + "message" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Delete a signup form", + "tags": [ + "Contacts" + ], + "parameters": [ + { + "name": "formId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/web/issues": { + "get": { + "description": "Broadcasts published to the web that have been sent, newest first. Works whether or not SendLit hosts the pages, so a platform can render the archive itself.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "List the team's published issues", + "tags": [ + "Web publishing" + ], + "parameters": [ + { + "name": "offset", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + { + "name": "itemsPerPage", + "in": "query", + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 50 + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "publishedAt": { + "type": "string" + }, + "excerpt": { + "type": "string" + }, + "seoTitle": { + "type": "string", + "nullable": true + }, + "seoDescription": { + "type": "string", + "nullable": true + }, + "ogImage": { + "type": "string", + "nullable": true + }, + "noindex": { + "type": "boolean" + }, + "canonicalUrl": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + } + }, + "required": [ + "slug", + "title", + "publishedAt", + "excerpt", + "seoTitle", + "seoDescription", + "ogImage", + "noindex", + "canonicalUrl", + "url" + ] + } + }, + "total": { + "type": "number" + } + }, + "required": [ + "items", + "total" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/web/issues/{slug}": { + "get": { + "description": "The HTML has no personal data, tracking or unsubscribe links. Merge tags fall back to their defaults.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get a published issue with its HTML", + "tags": [ + "Web publishing" + ], + "parameters": [ + { + "name": "slug", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "slug": { + "type": "string" + }, + "title": { + "type": "string" + }, + "publishedAt": { + "type": "string" + }, + "excerpt": { + "type": "string" + }, + "seoTitle": { + "type": "string", + "nullable": true + }, + "seoDescription": { + "type": "string", + "nullable": true + }, + "ogImage": { + "type": "string", + "nullable": true + }, + "noindex": { + "type": "boolean" + }, + "canonicalUrl": { + "type": "string", + "nullable": true + }, + "url": { + "type": "string", + "nullable": true + }, + "html": { + "type": "string" + } + }, + "required": [ + "slug", + "title", + "publishedAt", + "excerpt", + "seoTitle", + "seoDescription", + "ogImage", + "noindex", + "canonicalUrl", + "url", + "html" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/reputation": { + "get": { + "description": "Google Postmaster Tools (spam rate, SPF/DKIM/DMARC pass rates, delivery errors and compliance per sending domain) and Microsoft SNDS (filter result, complaint rate and trap hits per IP), with current alerts.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Get mailbox provider reputation data for the last 30 days", + "tags": [ + "Reputation" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "google": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "connection": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "error" + ] + }, + "accountLabel": { + "type": "string", + "nullable": true + }, + "lastSyncedAt": { + "type": "string", + "nullable": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "connectedAt": { + "type": "string" + } + }, + "required": [ + "status", + "accountLabel", + "lastSyncedAt", + "lastError", + "connectedAt" + ], + "nullable": true + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ok", + "not_in_postmaster", + "pending" + ] + }, + "compliance": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "compliant", + "needs_work", + "unknown" + ] + }, + "needsWork": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "status", + "needsWork" + ], + "nullable": true + }, + "days": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "spamRate": { + "type": "number", + "nullable": true + }, + "spfRate": { + "type": "number", + "nullable": true + }, + "dkimRate": { + "type": "number", + "nullable": true + }, + "dmarcRate": { + "type": "number", + "nullable": true + }, + "deliveryErrorRate": { + "type": "number", + "nullable": true + } + }, + "required": [ + "day", + "spamRate", + "spfRate", + "dkimRate", + "dmarcRate", + "deliveryErrorRate" + ] + } + } + }, + "required": [ + "domain", + "status", + "compliance", + "days" + ] + } + } + }, + "required": [ + "available", + "connection", + "domains" + ] + }, + "snds": { + "type": "object", + "properties": { + "connection": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "error" + ] + }, + "accountLabel": { + "type": "string", + "nullable": true + }, + "lastSyncedAt": { + "type": "string", + "nullable": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "connectedAt": { + "type": "string" + } + }, + "required": [ + "status", + "accountLabel", + "lastSyncedAt", + "lastError", + "connectedAt" + ], + "nullable": true + }, + "ips": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "days": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "filterResult": { + "type": "string", + "enum": [ + "green", + "yellow", + "red" + ], + "nullable": true + }, + "complaintRate": { + "type": "number", + "nullable": true + }, + "complaintRateText": { + "type": "string", + "nullable": true + }, + "trapHits": { + "type": "integer", + "nullable": true + }, + "recipients": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "day", + "filterResult", + "complaintRate", + "complaintRateText", + "trapHits", + "recipients" + ] + } + } + }, + "required": [ + "ip", + "days" + ] + } + } + }, + "required": [ + "connection", + "ips" + ] + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": [ + "google_postmaster", + "microsoft_snds" + ] + }, + "subject": { + "type": "string" + }, + "signal": { + "type": "string", + "enum": [ + "spam_rate", + "compliance", + "snds_filter" + ] + }, + "level": { + "type": "string", + "enum": [ + "warning", + "critical" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "provider", + "subject", + "signal", + "level", + "message" + ] + } + } + }, + "required": [ + "google", + "snds", + "alerts" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/reputation/google/connect": { + "post": { + "description": "Returns the Google sign-in URL to open in a browser. After consent, Google redirects back to SendLit and the data syncs daily. 409 `google_postmaster_not_configured` when this deployment has no Google OAuth client.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Start connecting Google Postmaster Tools", + "tags": [ + "Reputation" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "authorizationUrl": { + "type": "string" + } + }, + "required": [ + "authorizationUrl" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/reputation/google": { + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Disconnect Google Postmaster Tools and delete its data", + "tags": [ + "Reputation" + ], + "parameters": [], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/reputation/snds": { + "put": { + "description": "The link is checked by downloading the data once. 422 `snds_link_invalid` for links that aren't SNDS links or don't return SNDS data.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Connect Microsoft SNDS with its automated data access link", + "tags": [ + "Reputation" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "dataUrl": { + "type": "string", + "format": "uri", + "description": "The SNDS automated data access link (Automated Data Access page in SNDS). It expires after 30 days; paste the new one when it does." + } + }, + "required": [ + "dataUrl" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "google": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "connection": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "error" + ] + }, + "accountLabel": { + "type": "string", + "nullable": true + }, + "lastSyncedAt": { + "type": "string", + "nullable": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "connectedAt": { + "type": "string" + } + }, + "required": [ + "status", + "accountLabel", + "lastSyncedAt", + "lastError", + "connectedAt" + ], + "nullable": true + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ok", + "not_in_postmaster", + "pending" + ] + }, + "compliance": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "compliant", + "needs_work", + "unknown" + ] + }, + "needsWork": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "status", + "needsWork" + ], + "nullable": true + }, + "days": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "spamRate": { + "type": "number", + "nullable": true + }, + "spfRate": { + "type": "number", + "nullable": true + }, + "dkimRate": { + "type": "number", + "nullable": true + }, + "dmarcRate": { + "type": "number", + "nullable": true + }, + "deliveryErrorRate": { + "type": "number", + "nullable": true + } + }, + "required": [ + "day", + "spamRate", + "spfRate", + "dkimRate", + "dmarcRate", + "deliveryErrorRate" + ] + } + } + }, + "required": [ + "domain", + "status", + "compliance", + "days" + ] + } + } + }, + "required": [ + "available", + "connection", + "domains" + ] + }, + "snds": { + "type": "object", + "properties": { + "connection": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "error" + ] + }, + "accountLabel": { + "type": "string", + "nullable": true + }, + "lastSyncedAt": { + "type": "string", + "nullable": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "connectedAt": { + "type": "string" + } + }, + "required": [ + "status", + "accountLabel", + "lastSyncedAt", + "lastError", + "connectedAt" + ], + "nullable": true + }, + "ips": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "days": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "filterResult": { + "type": "string", + "enum": [ + "green", + "yellow", + "red" + ], + "nullable": true + }, + "complaintRate": { + "type": "number", + "nullable": true + }, + "complaintRateText": { + "type": "string", + "nullable": true + }, + "trapHits": { + "type": "integer", + "nullable": true + }, + "recipients": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "day", + "filterResult", + "complaintRate", + "complaintRateText", + "trapHits", + "recipients" + ] + } + } + }, + "required": [ + "ip", + "days" + ] + } + } + }, + "required": [ + "connection", + "ips" + ] + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": [ + "google_postmaster", + "microsoft_snds" + ] + }, + "subject": { + "type": "string" + }, + "signal": { + "type": "string", + "enum": [ + "spam_rate", + "compliance", + "snds_filter" + ] + }, + "level": { + "type": "string", + "enum": [ + "warning", + "critical" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "provider", + "subject", + "signal", + "level", + "message" + ] + } + } + }, + "required": [ + "google", + "snds", + "alerts" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "422": { + "description": "422", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + }, + "delete": { + "description": "**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Disconnect Microsoft SNDS and delete its data", + "tags": [ + "Reputation" + ], + "parameters": [], + "responses": { + "204": { + "description": "204" + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/reputation/sync": { + "post": { + "description": "Data also refreshes daily on its own. Limited to once every 5 minutes.\n\n**Authentication:** Requires a team API key or a user OAuth access token. Dashboard session cookies are also accepted.", + "summary": "Refresh reputation data now", + "tags": [ + "Reputation" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "google": { + "type": "object", + "properties": { + "available": { + "type": "boolean" + }, + "connection": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "error" + ] + }, + "accountLabel": { + "type": "string", + "nullable": true + }, + "lastSyncedAt": { + "type": "string", + "nullable": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "connectedAt": { + "type": "string" + } + }, + "required": [ + "status", + "accountLabel", + "lastSyncedAt", + "lastError", + "connectedAt" + ], + "nullable": true + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "ok", + "not_in_postmaster", + "pending" + ] + }, + "compliance": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "compliant", + "needs_work", + "unknown" + ] + }, + "needsWork": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "status", + "needsWork" + ], + "nullable": true + }, + "days": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "spamRate": { + "type": "number", + "nullable": true + }, + "spfRate": { + "type": "number", + "nullable": true + }, + "dkimRate": { + "type": "number", + "nullable": true + }, + "dmarcRate": { + "type": "number", + "nullable": true + }, + "deliveryErrorRate": { + "type": "number", + "nullable": true + } + }, + "required": [ + "day", + "spamRate", + "spfRate", + "dkimRate", + "dmarcRate", + "deliveryErrorRate" + ] + } + } + }, + "required": [ + "domain", + "status", + "compliance", + "days" + ] + } + } + }, + "required": [ + "available", + "connection", + "domains" + ] + }, + "snds": { + "type": "object", + "properties": { + "connection": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "active", + "error" + ] + }, + "accountLabel": { + "type": "string", + "nullable": true + }, + "lastSyncedAt": { + "type": "string", + "nullable": true + }, + "lastError": { + "type": "string", + "nullable": true + }, + "connectedAt": { + "type": "string" + } + }, + "required": [ + "status", + "accountLabel", + "lastSyncedAt", + "lastError", + "connectedAt" + ], + "nullable": true + }, + "ips": { + "type": "array", + "items": { + "type": "object", + "properties": { + "ip": { + "type": "string" + }, + "days": { + "type": "array", + "items": { + "type": "object", + "properties": { + "day": { + "type": "string" + }, + "filterResult": { + "type": "string", + "enum": [ + "green", + "yellow", + "red" + ], + "nullable": true + }, + "complaintRate": { + "type": "number", + "nullable": true + }, + "complaintRateText": { + "type": "string", + "nullable": true + }, + "trapHits": { + "type": "integer", + "nullable": true + }, + "recipients": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "day", + "filterResult", + "complaintRate", + "complaintRateText", + "trapHits", + "recipients" + ] + } + } + }, + "required": [ + "ip", + "days" + ] + } + } + }, + "required": [ + "connection", + "ips" + ] + }, + "alerts": { + "type": "array", + "items": { + "type": "object", + "properties": { + "provider": { + "type": "string", + "enum": [ + "google_postmaster", + "microsoft_snds" + ] + }, + "subject": { + "type": "string" + }, + "signal": { + "type": "string", + "enum": [ + "spam_rate", + "compliance", + "snds_filter" + ] + }, + "level": { + "type": "string", + "enum": [ + "warning", + "critical" + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "provider", + "subject", + "signal", + "level", + "message" + ] + } + } + }, + "required": [ + "google", + "snds", + "alerts" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "429": { + "description": "429", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "teamApiKeyStandardHeader": [] + }, + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/billing/action-token": { + "post": { + "description": "**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Issue a single-use token for a sensitive billing action", + "tags": [ + "Billing" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "organization_checkout", + "checkout", + "portal", + "plan_change", + "cancellation", + "organization_close", + "pending_hide" + ] + }, + "target": { + "type": "string", + "minLength": 1, + "maxLength": 300 + } + }, + "required": [ + "action", + "target" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "token": { + "type": "string", + "minLength": 32 + }, + "expiresAt": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "token", + "expiresAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/billing/organization-checkouts": { + "post": { + "description": "**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Create a paid organization and hosted checkout", + "tags": [ + "Billing" + ], + "parameters": [], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "interval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "catalogRevision": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "organizationName": { + "type": "string", + "minLength": 1, + "maxLength": 120 + }, + "teamName": { + "type": "string", + "minLength": 1, + "maxLength": 120 + } + }, + "required": [ + "plan", + "interval", + "catalogRevision", + "organizationName", + "teamName" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "checkoutUrl": { + "type": "string", + "format": "uri" + }, + "expiresAt": { + "type": "string" + }, + "organizationId": { + "type": "string" + } + }, + "required": [ + "checkoutUrl", + "expiresAt", + "organizationId" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/billing/catalog": { + "get": { + "description": "Returns environment-configured offers without provider product IDs. Amounts are integer minor units.\n\n**Authentication:** No authentication is required.", + "summary": "Read the active billing catalog", + "tags": [ + "Billing" + ], + "parameters": [], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "catalogRevision": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "currency": { + "type": "string", + "pattern": "^[A-Z]{3}$", + "nullable": true + }, + "offers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "catalogKey": { + "type": "string", + "enum": [ + "starter_month", + "starter_year", + "pro_month", + "pro_year", + "business_month", + "business_year" + ] + }, + "plan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "interval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "currency": { + "type": "string", + "pattern": "^[A-Z]{3}$" + }, + "amountMinor": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "trialDays": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "catalogKey", + "plan", + "interval", + "currency", + "amountMinor", + "trialDays" + ] + } + }, + "plans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "teamsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "monthlySendsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "marketingBranding": { + "type": "boolean" + }, + "sharedOrganizationMailbox": { + "type": "boolean" + }, + "provisioning": { + "type": "boolean" + }, + "organizationApiKeys": { + "type": "boolean" + } + }, + "required": [ + "plan", + "teamsLimit", + "monthlySendsLimit", + "marketingBranding", + "sharedOrganizationMailbox", + "provisioning", + "organizationApiKeys" + ] + } + }, + "checkoutAvailable": { + "type": "boolean" + } + }, + "required": [ + "catalogRevision", + "currency", + "offers", + "checkoutAvailable" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [] + } + }, + "/organizations/{organizationId}/billing": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Read organization plan and billing status", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "oss", + "free", + "starter", + "pro", + "business" + ] + }, + "billingInterval": { + "type": "string", + "enum": [ + "month", + "year" + ], + "nullable": true + }, + "paymentStatus": { + "type": "string", + "enum": [ + "free", + "checkout_pending", + "trialing", + "active", + "past_due", + "cancel_at_period_end", + "cancelled", + "expired" + ] + }, + "trialEndsAt": { + "type": "string", + "nullable": true + }, + "currentPeriodEndsAt": { + "type": "string", + "nullable": true + }, + "paidThroughAt": { + "type": "string", + "nullable": true + }, + "cancelAtPeriodEnd": { + "type": "boolean" + }, + "graceEndsAt": { + "type": "string", + "nullable": true + }, + "canManageBilling": { + "type": "boolean" + }, + "entitlements": { + "type": "object", + "properties": { + "teamsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "subscribedContactsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "monthlySendsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "sharedOrganizationMailbox": { + "type": "boolean" + }, + "provisioning": { + "type": "boolean" + }, + "organizationApiKeys": { + "type": "boolean" + }, + "marketingBranding": { + "type": "boolean" + } + }, + "required": [ + "teamsLimit", + "subscribedContactsLimit", + "monthlySendsLimit", + "sharedOrganizationMailbox", + "provisioning", + "organizationApiKeys", + "marketingBranding" + ] + }, + "usage": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "oss", + "free", + "starter", + "pro", + "business" + ] + }, + "paymentStatus": { + "type": "string", + "enum": [ + "free", + "checkout_pending", + "trialing", + "active", + "past_due", + "cancel_at_period_end", + "cancelled", + "expired" + ] + }, + "teams": { + "type": "integer", + "minimum": 0 + }, + "subscribedContacts": { + "type": "integer", + "minimum": 0 + }, + "monthlySends": { + "type": "integer", + "minimum": 0 + }, + "monthlySendsReserved": { + "type": "integer", + "minimum": 0 + }, + "bucketStartsAt": { + "type": "string" + }, + "bucketEndsAt": { + "type": "string" + }, + "teamsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "subscribedContactsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "monthlySendsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + } + }, + "required": [ + "plan", + "paymentStatus", + "teams", + "subscribedContacts", + "monthlySends", + "monthlySendsReserved", + "bucketStartsAt", + "bucketEndsAt", + "teamsLimit", + "subscribedContactsLimit", + "monthlySendsLimit" + ] + }, + "pendingPlanChange": { + "type": "object", + "properties": { + "changeId": { + "type": "string" + }, + "targetPlan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "targetInterval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "effectiveAt": { + "type": "string", + "enum": [ + "immediately", + "next_billing_date" + ] + } + }, + "required": [ + "changeId", + "targetPlan", + "targetInterval", + "effectiveAt" + ], + "nullable": true + } + }, + "required": [ + "plan", + "billingInterval", + "paymentStatus", + "trialEndsAt", + "currentPeriodEndsAt", + "paidThroughAt", + "cancelAtPeriodEnd", + "graceEndsAt", + "canManageBilling", + "entitlements", + "usage", + "pendingPlanChange" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/plan-usage": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Read organization plan usage", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "oss", + "free", + "starter", + "pro", + "business" + ] + }, + "paymentStatus": { + "type": "string", + "enum": [ + "free", + "checkout_pending", + "trialing", + "active", + "past_due", + "cancel_at_period_end", + "cancelled", + "expired" + ] + }, + "teams": { + "type": "integer", + "minimum": 0 + }, + "subscribedContacts": { + "type": "integer", + "minimum": 0 + }, + "monthlySends": { + "type": "integer", + "minimum": 0 + }, + "monthlySendsReserved": { + "type": "integer", + "minimum": 0 + }, + "bucketStartsAt": { + "type": "string" + }, + "bucketEndsAt": { + "type": "string" + }, + "teamsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "subscribedContactsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + }, + "monthlySendsLimit": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0, + "nullable": true + } + }, + "required": [ + "plan", + "paymentStatus", + "teams", + "subscribedContacts", + "monthlySends", + "monthlySendsReserved", + "bucketStartsAt", + "bucketEndsAt", + "teamsLimit", + "subscribedContactsLimit", + "monthlySendsLimit" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/billing/checkout": { + "post": { + "description": "**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Create a hosted organization checkout", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "interval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "catalogRevision": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + } + }, + "required": [ + "plan", + "interval", + "catalogRevision" + ] + } + } + } + }, + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "checkoutUrl": { + "type": "string", + "format": "uri" + }, + "expiresAt": { + "type": "string" + } + }, + "required": [ + "checkoutUrl", + "expiresAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/billing/portal": { + "post": { + "description": "**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Create the billing manager's hosted portal session", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "201", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "portalUrl": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "portalUrl" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/billing/cancel": { + "post": { + "description": "Requires a browser session, the CSRF headers, and a single-use `cancellation` action token in `X-SendLit-Billing-Action-Token`. Paid access continues until `paidThroughAt`. Idempotent: an already scheduled cancellation returns 200.\n\n**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Cancel the organization's subscription at the end of the paid period", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accepted": { + "type": "boolean", + "enum": [ + true + ] + }, + "subscriptionId": { + "type": "string" + } + }, + "required": [ + "accepted", + "subscriptionId" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/billing/resume": { + "post": { + "description": "Clears a cancellation scheduled for the end of the paid period. Uses a single-use `cancellation` action token. Returns 409 `operation_conflicted` (retryable) while a cancellation is still being sent to the payment provider.\n\n**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Resume a subscription that is scheduled to cancel", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "accepted": { + "type": "boolean", + "enum": [ + true + ] + }, + "subscriptionId": { + "type": "string" + } + }, + "required": [ + "accepted", + "subscriptionId" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/billing/plan-change": { + "post": { + "description": "**Authentication:** Requires a dashboard session cookie; OAuth access tokens and API keys are not accepted. Requests must pass the dashboard's same-origin and CSRF checks. Sensitive billing actions require recent authentication and a single-use billing action token; POST /billing/action-token issues that token.", + "summary": "Request a SendLit-owned organization plan change", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Body", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "plan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "interval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "catalogRevision": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": 0 + }, + "idempotencyKey": { + "type": "string", + "minLength": 1, + "maxLength": 256 + } + }, + "required": [ + "plan", + "interval", + "catalogRevision" + ] + } + } + } + }, + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "changeId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "succeeded", + "failed", + "conflicted" + ] + }, + "targetPlan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "targetInterval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "effectiveAt": { + "type": "string", + "enum": [ + "immediately", + "next_billing_date" + ] + }, + "paymentUrl": { + "type": "string", + "format": "uri", + "nullable": true + }, + "completedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "changeId", + "status", + "targetPlan", + "targetInterval", + "effectiveAt", + "paymentUrl", + "completedAt" + ] + } + } + } + }, + "202": { + "description": "202", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "changeId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "succeeded", + "failed", + "conflicted" + ] + }, + "targetPlan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "targetInterval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "effectiveAt": { + "type": "string", + "enum": [ + "immediately", + "next_billing_date" + ] + }, + "paymentUrl": { + "type": "string", + "format": "uri", + "nullable": true + }, + "completedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "changeId", + "status", + "targetPlan", + "targetInterval", + "effectiveAt", + "paymentUrl", + "completedAt" + ] + } + } + } + }, + "400": { + "description": "400\n\nOr `credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "401": { + "description": "401", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "402": { + "description": "402", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "409": { + "description": "409", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "503": { + "description": "503", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + }, + "/organizations/{organizationId}/billing/plan-changes/{changeId}": { + "get": { + "description": "**Authentication:** Requires a user OAuth access token or a dashboard session cookie.", + "summary": "Read a plan-change request status", + "tags": [ + "Billing" + ], + "parameters": [ + { + "name": "organizationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "changeId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "200", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "changeId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "succeeded", + "failed", + "conflicted" + ] + }, + "targetPlan": { + "type": "string", + "enum": [ + "starter", + "pro", + "business" + ] + }, + "targetInterval": { + "type": "string", + "enum": [ + "month", + "year" + ] + }, + "effectiveAt": { + "type": "string", + "enum": [ + "immediately", + "next_billing_date" + ] + }, + "paymentUrl": { + "type": "string", + "format": "uri", + "nullable": true + }, + "completedAt": { + "type": "string", + "nullable": true + } + }, + "required": [ + "changeId", + "status", + "targetPlan", + "targetInterval", + "effectiveAt", + "paymentUrl", + "completedAt" + ] + } + } + } + }, + "400": { + "description": "`credential_ambiguous`: the request sent more than one credential (for example an API key and a bearer token). Send exactly one." + }, + "403": { + "description": "403", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + }, + "404": { + "description": "404", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string" + } + }, + "required": [ + "error" + ] + } + } + } + } + }, + "security": [ + { + "userAccessToken": [] + }, + { + "dashboardSession": [] + }, + { + "dashboardSecureSession": [] + } + ] + } + } + }, + "info": { + "title": "SendLit API", + "description": "SendLit REST API for composing, sending and automating email. Most team-scoped routes accept a team API key or Better Auth OAuth2 bearer token. Provisioning and selected organization routes accept scoped organization API keys; some routes require user authentication.", + "version": "0.1.0" + }, + "servers": [ + { + "url": "{protocol}://{host}", + "description": "API Server", + "variables": { + "protocol": { + "default": "https", + "enum": [ + "https", + "http" + ] + }, + "host": { + "default": "api.sendlit.app" + } + } + } + ], + "tags": [ + { + "name": "Contacts", + "description": "Manage contacts (subscribers)." + }, + { + "name": "Segments", + "description": "Saved, named, reusable contact filters - build a filter once, reuse it across broadcasts and sequences." + }, + { + "name": "Tags", + "description": "Team-wide tags: list them with contact counts, see where one is used, rename or delete it." + }, + { + "name": "Media", + "description": "Authenticated MediaLit upload signatures for dashboard-owned media." + }, + { + "name": "Templates", + "description": "Reusable email templates." + }, + { + "name": "Sequences", + "description": "Broadcasts (one-off) and sequences (multi-step, event-triggered)." + }, + { + "name": "Transactional Emails", + "description": "Single API-triggered sends (receipts, password resets, ...) — no audience filter, no unsubscribe footer, delivered immediately." + }, + { + "name": "Events", + "description": "Custom events sent for a contact (`course purchased`, `trial ending`, ...). Sequences can start on them, and their properties render as {{ event.* }}." + }, + { + "name": "Settings", + "description": "Per-team settings, including email sending provider (SMTP) configuration and test sends." + }, + { + "name": "Teams", + "description": "Team management (list/create/rename/delete), per-team API keys, and server-to-server provisioning." + }, + { + "name": "Delivery", + "description": "Normalized bounce/complaint delivery events and the per-workspace suppression (do-not-send) list. See docs/bounces-and-complaints.md." + }, + { + "name": "Billing", + "description": "Organization-scoped plan, configured catalog, entitlement, and usage information." + } + ], + "components": { + "securitySchemes": { + "teamApiKey": { + "type": "apiKey", + "in": "header", + "name": "x-sendlit-apikey", + "description": "Team-scoped API key (sl_live_...) for REST requests." + }, + "teamApiKeyStandardHeader": { + "type": "apiKey", + "in": "header", + "name": "X-API-Key", + "description": "The same team API key in the standard X-API-Key header." + }, + "userAccessToken": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT", + "description": "Better Auth OAuth access token for a user. Dashboard session cookies are also accepted on user-authenticated routes." + }, + "dashboardSession": { + "type": "apiKey", + "in": "cookie", + "name": "sendlit.session_token", + "description": "Better Auth dashboard session cookie used for local HTTP connections." + }, + "dashboardSecureSession": { + "type": "apiKey", + "in": "cookie", + "name": "__Secure-sendlit.session_token", + "description": "Better Auth dashboard session cookie used over HTTPS." + }, + "organizationApiKey": { + "type": "http", + "scheme": "bearer", + "description": "Scoped organization API key (sl_org_live_...) sent as Authorization: Bearer ." + } + } + }, + "security": [ + { + "teamApiKey": [] + }, + { + "userAccessToken": [] + } + ] +} diff --git a/apps/api/package.json b/apps/api/package.json index 94b3f306..5dc94cd8 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -24,7 +24,9 @@ "test:conformance": "bun run build:deps && bun test --timeout 30000 src/platform-conformance.test.ts src/mcp/parity.test.ts", "lint": "biome lint .", "build:deps": "bun --filter @sendlit/email-editor build && bun --filter @sendlit/email-blocks build", - "disposable-domains:update": "bun scripts/update-disposable-domains.ts" + "disposable-domains:update": "bun scripts/update-disposable-domains.ts", + "openapi:generate": "bun scripts/openapi.ts", + "openapi:check": "bun scripts/openapi.ts --check" }, "dependencies": { "@anthropic-ai/sdk": "^0.135.0", diff --git a/apps/api/scripts/openapi.ts b/apps/api/scripts/openapi.ts new file mode 100644 index 00000000..e82e8fb0 --- /dev/null +++ b/apps/api/scripts/openapi.ts @@ -0,0 +1,27 @@ +import { readFile, writeFile } from "node:fs/promises"; +import { openApiDocument } from "@sendlit/api-contract/openapi"; + +/** + * Writes the OpenAPI document generated from the ts-rest contract to + * `apps/api/openapi.json`, the spec FrontLit publishes as the API reference. + * `--check` fails instead of writing when the committed file is stale; CI runs it. + * + * bun --filter @sendlit/api openapi:generate + * bun --filter @sendlit/api openapi:check + */ +const TARGET = new URL("../openapi.json", import.meta.url); +const expected = `${JSON.stringify(openApiDocument, null, 2)}\n`; + +if (process.argv.includes("--check")) { + const current = await readFile(TARGET, "utf8").catch(() => ""); + if (current !== expected) { + console.error( + "apps/api/openapi.json is out of date with the API contract. Run `bun --filter @sendlit/api openapi:generate` and commit the result.", + ); + process.exit(1); + } + console.log("apps/api/openapi.json is up to date."); +} else { + await writeFile(TARGET, expected); + console.log("Wrote apps/api/openapi.json."); +} diff --git a/biome.json b/biome.json index 193a6ba2..eeaeff5f 100644 --- a/biome.json +++ b/biome.json @@ -18,6 +18,7 @@ "!**/next-env.d.ts", "!**/*.generated.ts", "!apps/api/drizzle", + "!apps/api/openapi.json", "!.codelit-platform.json", "!.github/workflows/platform-conformance.yml" ]