{
  "openapi": "3.1.0",
  "info": {
    "title": "Political Comms API",
    "summary": "Direct-to-carrier political texting API for campaigns, PACs, advocacy organizations, fundraisers, and elected officials.",
    "description": "Public REST API for the Political Comms platform. Surfaces include Projects (compose, test, schedule, send), Conversations (read inbound threads and reply inside them), Contact Lists (S3 import and analysis), Media Files, Organizations and hierarchy, Brands, Campaigns, Tracking Domains, Phone Numbers, Analytics, and Billing.\n\nAuthentication is an API key passed in the `X-API-Key` header. Keys are generated from the dashboard at Admin → API and are prefixed `pc_live_`. All POST and PATCH endpoints that mutate state are designed to be safe to retry, with optional `Idempotency-Key` headers for stronger guarantees. Rate limit is 600 requests per minute per key (bursts up to 600, refilling at 10 per second, the same for every scope; the `X-RateLimit-Limit` header reports your key's exact limit, and `Retry-After` on a 429 is the seconds until your next request is allowed).\n\nWebhooks emit `message.sent`, `message.delivered`, `message.failed`, `message.replied`, and `link.clicked` events. Payloads are HMAC-signed; validate the signature before trusting any payload.\n\nA Model Context Protocol (MCP) server is available at https://docs.politicalcomms.com/mcp for AI agents that need to search the documentation programmatically. The developer hub at https://politicalcomms.com/developers/ has quickstart examples in cURL, raw HTTP, and Python.",
    "version": "1.4.1",
    "termsOfService": "https://politicalcomms.com/terms/",
    "contact": {
      "name": "Political Comms Support",
      "email": "support@politicalcomms.com",
      "url": "https://docs.politicalcomms.com"
    },
    "license": {
      "name": "Proprietary",
      "url": "https://politicalcomms.com/terms/"
    },
    "x-logo": {
      "url": "https://politicalcomms.com/images/brand/pcomms-logo-left-of-text.png",
      "altText": "Political Comms",
      "backgroundColor": "#ffffff",
      "href": "https://politicalcomms.com/"
    },
    "x-mcp": {
      "url": "https://docs.politicalcomms.com/mcp",
      "discovery_url": "https://docs.politicalcomms.com/.well-known/mcp",
      "transport": "http",
      "auth": "none",
      "tools": [
        "search_political_comms",
        "query_docs_filesystem_political_comms",
        "submit_feedback"
      ]
    }
  },
  "servers": [
    {
      "url": "https://api.politicalcomms.com/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "tags": [
    {
      "name": "Projects",
      "description": "Create, edit, test, schedule, and inspect projects."
    },
    {
      "name": "Conversations",
      "description": "Read inbound threads and reply inside them from the same number. Conversations are created by project sends; the API never starts one."
    },
    {
      "name": "Phone Numbers",
      "description": "List phone numbers across 10DLC campaigns and toll-free verifications."
    },
    {
      "name": "Toll-Free Verifications",
      "description": "List toll-free verifications (carrier registrations) across your organization hierarchy."
    },
    {
      "name": "Brands",
      "description": "List brands across your organization hierarchy."
    },
    {
      "name": "10DLC Campaigns",
      "description": "List 10DLC campaigns (carrier registrations) across your organization hierarchy."
    },
    {
      "name": "Email Campaigns",
      "description": "Create, schedule, and report on email campaigns. Pause, resume, and test sends are deliverability decisions a human makes while watching a send, and live in the dashboard."
    },
    {
      "name": "Email Templates",
      "description": "Create and read saved email templates."
    },
    {
      "name": "Email Senders",
      "description": "Read the sender identities (From addresses) on your verified domains. Identities carry the physical mailing address and paid-for-by disclaimer, so they are created and edited in the dashboard."
    },
    {
      "name": "Email Suppressions",
      "description": "Read, add, and lift suppressions in bulk. This is the primitive for keeping your own opt-out record in sync with ours."
    },
    {
      "name": "Contact Lists",
      "description": "List, import, and analyze contact lists."
    },
    {
      "name": "Email Lists",
      "description": "Create email lists, read their contacts, and bulk-upsert or import addresses. To stop mailing someone use Email Suppressions, which survives a re-import."
    },
    {
      "name": "Media Files",
      "description": "List, import, and fetch media files."
    },
    {
      "name": "Sending Domains",
      "description": "Read the sending domains in your organization and the DNS records to publish. Domains are added in the dashboard: DNS is published by hand, so creation is not part of the API."
    },
    {
      "name": "Tracking Domains",
      "description": "List active link-tracking domains. Use the returned ids as `link_tracking_domain_id` on project create/update."
    },
    {
      "name": "Organizations",
      "description": "List descendant organizations and hierarchy."
    },
    {
      "name": "Billing",
      "description": "Usage and billing data across your organization."
    },
    {
      "name": "Analytics",
      "description": "Message statistics and delivery performance."
    }
  ],
  "paths": {
    "/organizations": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "List Organizations",
        "description": "List all descendant organizations accessible to your API key.",
        "operationId": "listOrganizations",
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "display_name": {
                            "type": "string"
                          },
                          "parent_org_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Name of the parent organization; null for a root organization."
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "org_abc123",
                      "display_name": "Regional Office East",
                      "parent_org_name": "Smith Campaign 2024",
                      "status": "active",
                      "created_at": "2024-02-01T10:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/hierarchy": {
      "get": {
        "tags": [
          "Organizations"
        ],
        "summary": "Get Hierarchy",
        "description": "Get the full organization hierarchy with brands and campaigns, rooted at the requested organization (defaults to the API key's organization). Responses use camelCase field names and are cached for up to 5 minutes.",
        "operationId": "getHierarchy",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationIdCamel"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/HierarchyOrganization"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                    "name": "Smith Campaign 2024",
                    "parentId": null,
                    "status": "active",
                    "contactEmail": "ops@smithcampaign.com",
                    "contactFirstName": "Jordan",
                    "contactLastName": "Smith",
                    "createdAt": "2024-01-15T10:00:00.000Z",
                    "updatedAt": "2024-06-01T09:30:00.000Z",
                    "brands": [
                      {
                        "id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                        "name": "Smith PAC",
                        "status": "OK",
                        "organizationId": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                        "createdAt": "2024-01-15T11:00:00.000Z",
                        "updatedAt": "2024-01-20T08:00:00.000Z",
                        "campaigns": [
                          {
                            "id": "4b8fa3c1-6d25-4e9a-bf47-8c2e5d9a3f71",
                            "name": "Fall 2025 GOTV",
                            "status": "ACTIVE",
                            "createdAt": "2024-02-01T12:00:00.000Z",
                            "updatedAt": "2024-02-01T12:00:00.000Z"
                          }
                        ]
                      }
                    ],
                    "childOrganizations": [
                      {
                        "id": "9e4c7b2a-5f18-4d3e-a6b9-1c8f4e7a2d55",
                        "name": "Regional Office East",
                        "parentId": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                        "status": "active",
                        "contactEmail": null,
                        "contactFirstName": null,
                        "contactLastName": null,
                        "createdAt": "2024-02-01T10:00:00.000Z",
                        "updatedAt": "2024-02-01T10:00:00.000Z",
                        "brands": [],
                        "childOrganizations": []
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/brands": {
      "get": {
        "tags": [
          "Brands"
        ],
        "summary": "List Brands",
        "description": "List all brands. Optionally filter by organization.",
        "operationId": "listBrands",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "brand_name": {
                            "type": "string",
                            "description": "The brand's display name as registered: the DBA (doing business as) name when one was given, otherwise the legal name."
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "tcr_brand_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "TCR brand ID; null until the brand is registered with TCR."
                          },
                          "status": {
                            "type": "string"
                          },
                          "identity_status": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "notes": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "maxLength": 100,
                            "description": "Short internal note set in the dashboard or the bulk CSV import. Never sent to the registrar or to recipients."
                          },
                          "created_at": {
                            "type": "string"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "brand_abc123",
                      "brand_name": "Smith PAC",
                      "org_id": "org_123",
                      "org_name": "Smith Campaign 2024",
                      "tcr_brand_id": "BTCRMFG",
                      "status": "OK",
                      "identity_status": "VETTED_VERIFIED",
                      "notes": "Primary PAC brand, renewal due Q1",
                      "created_at": "2024-01-15T11:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/campaigns": {
      "get": {
        "tags": [
          "10DLC Campaigns"
        ],
        "summary": "List Campaigns",
        "description": "List all campaigns. Filter by organization or brand.",
        "operationId": "listCampaigns",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          },
          {
            "$ref": "#/components/parameters/BrandId"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "brand_id": {
                            "type": "string"
                          },
                          "brand_name": {
                            "type": "string",
                            "description": "The brand's display name as registered: the DBA (doing business as) name when one was given, otherwise the legal name."
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "campaign_status": {
                            "type": "string"
                          },
                          "notes": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "maxLength": 100,
                            "description": "Short internal note set in the dashboard. Never sent to the registrar or to recipients."
                          },
                          "created_at": {
                            "type": "string"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "camp_xyz789",
                      "name": "Fall 2025 GOTV",
                      "brand_id": "brand_abc123",
                      "brand_name": "Smith PAC",
                      "org_id": "org_123",
                      "org_name": "Smith Campaign 2024",
                      "campaign_status": "MNO_PROVISIONED",
                      "notes": "GOTV texting",
                      "created_at": "2024-01-20T09:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/campaigns/{id}/throughput": {
      "get": {
        "tags": [
          "10DLC Campaigns"
        ],
        "summary": "Get Campaign Throughput",
        "description": "Carrier sending limits for a campaign's brand and how much of today's T-Mobile daily cap is used. The two carrier lanes are independent: `t_mobile` is present only once a T-Mobile daily cap has synced for the campaign, and `att` whenever the campaign has an AT&T rate on file, whatever the brand type (political brands included). Use it before scheduling to see how much room is left. Returns 404 `NOT_FOUND` when the campaign is outside the key's scope. See [Carrier throughput limits](/api-reference/best-practices#carrier-throughput-limits).",
        "operationId": "getCampaignThroughput",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "campaign_id": {
                          "type": "string"
                        },
                        "brand_id": {
                          "type": "string"
                        },
                        "carrier_metered": {
                          "type": "boolean",
                          "description": "true when at least one carrier lane applies to this campaign (`t_mobile` or `att` is present). false when both are null."
                        },
                        "t_mobile": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "description": "T-Mobile per-brand daily message cap. null whenever the brand has no T-Mobile daily limit on file (political brands have none today), so when present `daily_cap` is always an integer. `used_today` and `remaining_today` are null when today's usage is temporarily unavailable; a null is never a zero.",
                          "properties": {
                            "daily_cap": {
                              "type": "integer",
                              "description": "Messages per day T-Mobile allows this brand."
                            },
                            "used_today": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "Messages counted against the cap so far in the current Pacific day."
                            },
                            "remaining_today": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "`daily_cap` minus `used_today`, never below 0."
                            },
                            "pacific_day": {
                              "type": "string",
                              "description": "The current Pacific day (YYYY-MM-DD). The cap resets at midnight Pacific."
                            }
                          }
                        },
                        "att": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "description": "AT&T per-minute limits for this campaign, counted in message parts. AT&T traffic is delivered at this rate; it never pauses a project and never slows other carriers. null only when the campaign has no AT&T rate on file. Every 10DLC campaign with a rate on file has its AT&T traffic delivered at it, whatever the brand type. The platform sends AT&T recipients right away and the carrier queue delivers them at this rate, usually within about an hour; only in the last 90 minutes of the recipients' sending window does the platform hold back AT&T recipients that could not be delivered before the window closes. Inside `att`, `sms_tpm` and `mms_tpm` are each null when that rate is not known.",
                          "properties": {
                            "sms_tpm": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "SMS message parts per minute. A text that splits into 2 or 3 parts counts 2 or 3 times."
                            },
                            "mms_tpm": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "MMS messages per minute. A picture message counts once."
                            }
                          }
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "campaign_id": "4b8fa3c1-6d25-4e9a-bf47-8c2e5d9a3f71",
                    "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                    "carrier_metered": true,
                    "t_mobile": {
                      "daily_cap": 10000,
                      "used_today": 7250,
                      "remaining_today": 2750,
                      "pacific_day": "2026-09-28"
                    },
                    "att": {
                      "sms_tpm": 4500,
                      "mms_tpm": 1200
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/tracking-domains": {
      "get": {
        "tags": [
          "Tracking Domains"
        ],
        "summary": "List Tracking Domains",
        "description": "List active tracking domains available to your organizations. Domains owned by one of your accessible organizations return source \"own\" with full metadata; domains shared down from a parent organization return source \"inherited\" with only id, domain, and source. Optional filter by organization.",
        "operationId": "listTrackingDomains",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "oneOf": [
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "domain": {
                                "type": "string"
                              },
                              "source": {
                                "type": "string",
                                "enum": [
                                  "own"
                                ]
                              },
                              "status": {
                                "type": "string",
                                "description": "Always \"active\"; only active domains are listed."
                              },
                              "verified_at": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "created_at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "notes": {
                                "type": [
                                  "string",
                                  "null"
                                ],
                                "maxLength": 100,
                                "description": "Short internal note set on the Domains page. Owned rows only; never shown to recipients."
                              }
                            },
                            "required": [
                              "id",
                              "domain",
                              "source"
                            ],
                            "additionalProperties": true,
                            "title": "Owned tracking domain"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "domain": {
                                "type": "string"
                              },
                              "source": {
                                "type": "string",
                                "enum": [
                                  "inherited"
                                ],
                                "description": "Inherited from a parent organization that shares the domain with sub-organizations."
                              }
                            },
                            "required": [
                              "id",
                              "domain",
                              "source"
                            ],
                            "additionalProperties": true,
                            "title": "Inherited tracking domain"
                          }
                        ]
                      },
                      "description": "Discriminate on the source field: \"own\" rows carry status/verified_at/created_at/notes; \"inherited\" rows do not."
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "6e2c9a4f-8b51-4d73-a9e6-1f4b8c5d2a90",
                      "domain": "link.smithpac.com",
                      "source": "own",
                      "status": "active",
                      "verified_at": "2024-02-10T14:05:00.000Z",
                      "created_at": "2024-02-10T14:00:00.000Z",
                      "notes": "Main link host"
                    },
                    {
                      "id": "2a7f4d8c-3e61-4b29-9c85-6d1a4f7e3b16",
                      "domain": "links.national-hq.com",
                      "source": "inherited"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/phone-numbers": {
      "get": {
        "tags": [
          "Phone Numbers"
        ],
        "summary": "List Phone Numbers",
        "description": "List all phone numbers across both channels: 10DLC numbers (owned via a campaign) and toll-free numbers (owned via a toll-free verification). Each row carries a `channel` and an `owner_type` indicating what it belongs to. The `campaign_id`/`campaign_name`/`brand_id`/`brand_name` fields are populated for 10DLC numbers and null for toll-free; `toll_free_verification_id`/`toll_free_registration_name`/`tf_verification_status` are populated for toll-free numbers and null for 10DLC. Filter by organization, brand, or campaign (brand/campaign filters apply to 10DLC numbers only), and optionally by `channel` or `owner_type`.\n\nNumbers a parent organization shared with one of the key's organizations are included with `shared: true` and `nickname`; on those rows every owner-side name and `brand_id` is null. Sharing keeps billing and registration with the owner; see [Sharing phone numbers with sub-organizations](/help/shared-phone-numbers).",
        "operationId": "listPhoneNumbers",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          },
          {
            "$ref": "#/components/parameters/BrandId"
          },
          {
            "$ref": "#/components/parameters/CampaignId"
          },
          {
            "name": "channel",
            "in": "query",
            "required": false,
            "description": "Filter by messaging channel",
            "schema": {
              "type": "string",
              "enum": [
                "10dlc",
                "toll-free",
                "short-code",
                "rcs"
              ]
            }
          },
          {
            "name": "owner_type",
            "in": "query",
            "required": false,
            "description": "Filter by what the number belongs to: a 10DLC campaign, a toll-free verification, or neither (purchased but not yet attached)",
            "schema": {
              "type": "string",
              "enum": [
                "campaign",
                "toll_free_registration",
                "unassigned"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "number": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string"
                          },
                          "owner_type": {
                            "type": "string",
                            "enum": [
                              "campaign",
                              "toll_free_registration",
                              "short_code",
                              "unassigned"
                            ]
                          },
                          "campaign_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "campaign_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Null on a shared row: a number shared with your organization by a parent never reveals the owner's registration."
                          },
                          "brand_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Null on a shared row."
                          },
                          "brand_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Null on a shared row."
                          },
                          "toll_free_verification_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "tf_verification_status": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "toll_free_registration_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Null on a shared row and on 10DLC numbers."
                          },
                          "toll_free_business_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Business name on the toll-free verification; null for 10DLC numbers."
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string"
                          },
                          "shared": {
                            "type": "boolean",
                            "description": "True when this row is a number an ancestor organization shared with `org_id` under a nickname. The same number can appear twice for a key that spans both organizations: once owned, once shared."
                          },
                          "nickname": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "The nickname the sharing organization gave this number; the only name the recipient sees. Null on owned rows."
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "pn_abc123",
                      "number": "+12025551234",
                      "channel": "10dlc",
                      "owner_type": "campaign",
                      "campaign_id": "camp_xyz789",
                      "campaign_name": "Fall 2025 GOTV",
                      "brand_id": "brand_abc123",
                      "brand_name": "Smith PAC",
                      "toll_free_verification_id": null,
                      "tf_verification_status": null,
                      "toll_free_registration_name": null,
                      "org_id": "org_123",
                      "org_name": "Smith Campaign 2024",
                      "status": "active",
                      "created_at": "2024-03-01T08:00:00Z",
                      "shared": false,
                      "nickname": null
                    },
                    {
                      "id": "pn_def456",
                      "number": "+18005551234",
                      "channel": "toll-free",
                      "owner_type": "toll_free_registration",
                      "campaign_id": null,
                      "campaign_name": null,
                      "brand_id": null,
                      "brand_name": null,
                      "toll_free_verification_id": "tfv_abc123",
                      "tf_verification_status": "verified",
                      "toll_free_registration_name": "Smith Campaign TFN",
                      "org_id": "org_123",
                      "org_name": "Smith Campaign 2024",
                      "status": "active",
                      "created_at": "2024-03-02T08:00:00Z",
                      "shared": false,
                      "nickname": null
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/toll-free-verifications": {
      "get": {
        "tags": [
          "Toll-Free Verifications"
        ],
        "summary": "List Toll-Free Verifications",
        "description": "List toll-free verifications (the carrier registrations behind your toll-free numbers) across your organization hierarchy. Filter by organization or by submission status.",
        "operationId": "listTollFreeVerifications",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by submission status",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "In Progress",
                "Verified",
                "Rejected"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          },
                          "verification_request_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "business_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "use_case": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "phone_number_count": {
                            "type": "integer"
                          },
                          "submitted_at": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "verified_at": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "created_at": {
                            "type": "string"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "tfv_abc123",
                      "name": "Smith Campaign TFN",
                      "status": "Verified",
                      "verification_request_id": "TFV-1234567890",
                      "org_id": "org_123",
                      "org_name": "Smith Campaign 2024",
                      "business_name": "Smith for Senate",
                      "use_case": "Political",
                      "phone_number_count": 2,
                      "submitted_at": "2024-02-20T09:00:00Z",
                      "verified_at": "2024-02-28T17:30:00Z",
                      "created_at": "2024-02-19T10:00:00Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/toll-free-verifications/{id}": {
      "get": {
        "tags": [
          "Toll-Free Verifications"
        ],
        "summary": "Get Toll-Free Verification",
        "description": "Fetch a single toll-free verification, including the phone numbers it covers and any rejection reason.",
        "operationId": "getTollFreeVerification",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "verification_request_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "org_id": {
                          "type": "string"
                        },
                        "org_name": {
                          "type": "string"
                        },
                        "business_name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "use_case": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "phone_number_count": {
                          "type": "integer"
                        },
                        "phone_number_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "rejection_reason": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "submitted_at": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "verified_at": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "created_at": {
                          "type": "string"
                        },
                        "updated_at": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": true
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "id": "tfv_abc123",
                    "name": "Smith Campaign TFN",
                    "status": "Verified",
                    "verification_request_id": "TFV-1234567890",
                    "org_id": "org_123",
                    "org_name": "Smith Campaign 2024",
                    "business_name": "Smith for Senate",
                    "use_case": "Political",
                    "phone_number_count": 2,
                    "phone_number_ids": [
                      "pn_def456",
                      "pn_ghi789"
                    ],
                    "rejection_reason": null,
                    "submitted_at": "2024-02-20T09:00:00Z",
                    "verified_at": "2024-02-28T17:30:00Z",
                    "created_at": "2024-02-19T10:00:00Z",
                    "updated_at": "2024-02-28T17:30:00Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contact-lists": {
      "get": {
        "tags": [
          "Contact Lists"
        ],
        "summary": "List Contact Lists",
        "description": "List all contact lists. Filter by organization or brand.",
        "operationId": "listContactLists",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          },
          {
            "$ref": "#/components/parameters/BrandId"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "brand_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "brand_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42",
                      "name": "Spring Outreach List",
                      "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                      "brand_name": "Smith PAC",
                      "org_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                      "org_name": "Smith Campaign 2024",
                      "created_at": "2025-03-15T10:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contact-lists/{id}": {
      "get": {
        "tags": [
          "Contact Lists"
        ],
        "summary": "Get Contact List",
        "description": "Fetch a single contact list including import progress and phone-type analysis results. `downloads` links the CSV files: `original_url` always, `analyzed_url` once analysis is complete (null before). Both are API URLs, so request them with your `X-API-Key`; they do not expire.",
        "operationId": "getContactList",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "list_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "brand_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "organization_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "imported_via_api": {
                          "type": "boolean",
                          "description": "True when the list was created through POST /contact-lists/import."
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "available_merge_tags": {
                          "type": "array",
                          "description": "The merge tags this list offers in message content, one per mapped column. When the list was imported without `merge_tags`, these are the tags assigned automatically.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "tag": {
                                "type": "string",
                                "description": "The merge tag name. Use it in message content as `{tag}`."
                              },
                              "column": {
                                "type": "string",
                                "description": "The CSV column the tag reads."
                              },
                              "standard": {
                                "type": "boolean",
                                "description": "True for a standard field such as first_name; false for a custom tag."
                              }
                            }
                          }
                        },
                        "import": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "uploading",
                                "processing",
                                "ready",
                                "failed"
                              ]
                            },
                            "progress": {
                              "type": "integer",
                              "description": "Import progress percentage (0-100). Always 100 once status is ready."
                            },
                            "total_rows": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "total_contacts": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "clean_contacts": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "duplicate_count": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "bad_number_count": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "opted_out_count": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "processing_started_at": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "processing_completed_at": {
                              "type": [
                                "string",
                                "null"
                              ]
                            }
                          },
                          "additionalProperties": true
                        },
                        "analysis": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "not_started",
                                "processing",
                                "complete",
                                "failed"
                              ]
                            },
                            "analyzed_numbers": {
                              "type": "integer"
                            },
                            "breakdown": {
                              "type": "object",
                              "properties": {
                                "mobile": {
                                  "type": "integer"
                                },
                                "landline": {
                                  "type": "integer"
                                },
                                "voip": {
                                  "type": "integer"
                                },
                                "invalid": {
                                  "type": "integer"
                                }
                              },
                              "additionalProperties": true,
                              "description": "Present only after at least one number has been analyzed."
                            }
                          },
                          "additionalProperties": true
                        },
                        "downloads": {
                          "type": "object",
                          "description": "API URLs for `GET /contact-lists/{id}/download`. Authenticate with `X-API-Key`; the URLs do not expire.",
                          "properties": {
                            "original_url": {
                              "type": "string"
                            },
                            "analyzed_url": {
                              "type": [
                                "string",
                                "null"
                              ],
                              "description": "Null until `analysis.status` is `complete`."
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "list_id": "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42",
                    "name": "Spring Outreach List",
                    "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                    "organization_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                    "imported_via_api": true,
                    "created_at": "2025-03-15T10:00:00.000Z",
                    "available_merge_tags": [
                      { "tag": "first_name", "column": "First Name", "standard": true },
                      { "tag": "survey_link", "column": "SURVEY_LINK", "standard": false }
                    ],
                    "import": {
                      "status": "ready",
                      "progress": 100,
                      "total_rows": 12500,
                      "total_contacts": 12500,
                      "clean_contacts": 11800,
                      "duplicate_count": 400,
                      "bad_number_count": 250,
                      "opted_out_count": 50,
                      "processing_started_at": "2025-03-15T10:00:05.000Z",
                      "processing_completed_at": "2025-03-15T10:04:40.000Z"
                    },
                    "analysis": {
                      "status": "complete",
                      "analyzed_numbers": 11800,
                      "breakdown": {
                        "mobile": 11200,
                        "landline": 300,
                        "voip": 250,
                        "invalid": 50
                      }
                    },
                    "downloads": {
                      "original_url": "https://api.politicalcomms.com/v1/contact-lists/3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42/download?type=original",
                      "analyzed_url": "https://api.politicalcomms.com/v1/contact-lists/3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42/download?type=analyzed"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Contact Lists"
        ],
        "summary": "Delete Contact List",
        "description": "Permanently remove a contact list from your account. The list is detached from any draft projects that reference it and no longer appears in list or read endpoints. Deletion is blocked with a 409 while a scheduled, sending, or active project depends on the list; unschedule the project or wait for the send to finish, then retry.",
        "operationId": "deleteContactList",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Contact list deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "list_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "deleted": {
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "list_id": "3f7a1c9e-5b2d-4e86-9a04-7c1d3f5e9b28",
                    "name": "Ohio Voters March",
                    "deleted": true
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The list is attached to a project that is scheduled, sending, or active. Error code CONTACT_LIST_IN_USE; `details.projects` lists the blocking projects as `{id, name, status}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contact-lists/import": {
      "post": {
        "tags": [
          "Contact Lists"
        ],
        "summary": "Import Contact List",
        "description": "Asynchronous job: import a CSV from a client-supplied presigned S3 or public HTTPS URL. Returns immediately with the created resource; poll `GET /contact-lists/{id}` until `status` is `ready`. Columns other than the phone column become merge tags automatically unless you send `merge_tags` (see that parameter).",
        "operationId": "importContactList",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_url",
                  "list_name",
                  "phone_column"
                ],
                "properties": {
                  "source_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "organization_id": {
                    "type": "string",
                    "description": "Target organization for the import. Defaults to the organization that owns the API key. Required only when targeting a sub-organization under an agency-tier parent; the API key must have access to the sub-organization (otherwise the request returns 403 ORG_ACCESS_DENIED)."
                  },
                  "brand_id": {
                    "type": "string",
                    "description": "Optional. When provided, the list is scoped to this brand and the brand's organization is used (must match `organization_id` if both are sent, otherwise returns 400 BRAND_ORG_MISMATCH)."
                  },
                  "list_name": {
                    "type": "string"
                  },
                  "phone_column": {
                    "type": "string"
                  },
                  "merge_tags": {
                    "type": "object",
                    "properties": {
                      "first_name": {
                        "type": "string",
                        "minLength": 1
                      },
                      "last_name": {
                        "type": "string",
                        "minLength": 1
                      },
                      "address": {
                        "type": "string",
                        "minLength": 1
                      },
                      "address_line_2": {
                        "type": "string",
                        "minLength": 1
                      },
                      "city": {
                        "type": "string",
                        "minLength": 1
                      },
                      "state": {
                        "type": "string",
                        "minLength": 1
                      },
                      "zip": {
                        "type": "string",
                        "minLength": 1
                      },
                      "custom_fields": {
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "csv_column": {
                              "type": "string",
                              "minLength": 1
                            },
                            "merge_tag": {
                              "type": "string",
                              "minLength": 1
                            }
                          },
                          "required": [
                            "csv_column",
                            "merge_tag"
                          ],
                          "additionalProperties": true
                        }
                      }
                    },
                    "description": "Maps CSV columns to contact merge fields. Unknown keys are rejected with a 400. When omitted, or when it maps nothing, every non-phone column gets a merge tag automatically: a header that matches a standard field (first name, last name, address, address line 2, city, state, zip, including common aliases) gets the standard tag, and any other column gets a tag named after its header in lowercase with underscores (for example `Donor Tier` becomes `{donor_tier}`). When supplied, it is used exactly as sent and unlisted columns get no tag.",
                    "additionalProperties": false
                  }
                }
              },
              "example": {
                "source_url": "https://example.com/path/to/list.csv",
                "organization_id": "01HX0000000000000000000000",
                "brand_id": "01HX0000000000000000000001",
                "list_name": "Spring Outreach List",
                "phone_column": "phone",
                "merge_tags": {
                  "first_name": "first_name",
                  "last_name": "last_name",
                  "address": "street",
                  "address_line_2": "street2",
                  "city": "city",
                  "state": "state",
                  "zip": "zip",
                  "custom_fields": [
                    {
                      "csv_column": "donor_tier",
                      "merge_tag": "tier"
                    }
                  ]
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Import accepted and processing asynchronously. Poll GET /contact-lists/{id} for progress.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "list_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "processing"
                          ]
                        },
                        "total_rows": {
                          "type": [
                            "integer",
                            "null"
                          ]
                        },
                        "imported_via_api": {
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "list_id": "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42",
                    "name": "Spring Outreach List",
                    "status": "processing",
                    "total_rows": 12500,
                    "imported_via_api": true
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ]
      }
    },
    "/contact-lists/{id}/analyze": {
      "post": {
        "tags": [
          "Contact Lists"
        ],
        "summary": "Analyze Contact List",
        "description": "Asynchronous, billable job: trigger LRN mobile/landline/VoIP/invalid analysis for an imported list. Charged per lookup to the list's organization for the numbers not yet analyzed; the wallet is checked first, and `402` `INSUFFICIENT_BALANCE` queues nothing. Send an `Idempotency-Key` so a retried call is not charged twice. Returns immediately with `202` and the `cost_cents` and `numbers_queued` for the run. If nothing is left to analyze it returns `200` with `analysis.status` `complete`, `numbers_queued` 0 and `cost_cents` 0. If analysis is already running it returns `202` with `cost_cents` 0 and charges nothing. Poll `GET /contact-lists/{id}` until `analysis.status` is `complete`, or subscribe to the `contact_list.analyzed` webhook, then fetch the results with `GET /contact-lists/{id}/download?type=analyzed`. A project cannot be created, updated with, or scheduled on a list whose analysis is in progress; the request fails with `409` `LIST_ANALYSIS_IN_PROGRESS`.",
        "operationId": "analyzeContactList",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Nothing left to analyze: `analysis.status` is `complete`, `numbers_queued` 0, `cost_cents` 0. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "list_id": {
                          "type": "string"
                        },
                        "analysis": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "processing",
                                "complete"
                              ]
                            },
                            "numbers_queued": {
                              "type": "integer"
                            },
                            "estimated_completion_seconds": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        },
                        "cost_cents": {
                          "type": "integer",
                          "description": "What this call charged the list's organization, in cents. 0 when nothing was queued or the run was already in progress."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "list_id": "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42",
                    "analysis": {
                      "status": "complete",
                      "numbers_queued": 0,
                      "estimated_completion_seconds": 0
                    },
                    "cost_cents": 0
                  }
                }
              }
            }
          },
          "202": {
            "description": "Analysis queued (charged) or already running (no charge, `cost_cents` 0). Poll GET /contact-lists/{id} or subscribe to the `contact_list.analyzed` webhook.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "list_id": {
                          "type": "string"
                        },
                        "analysis": {
                          "type": "object",
                          "properties": {
                            "status": {
                              "type": "string",
                              "enum": [
                                "processing",
                                "complete"
                              ]
                            },
                            "numbers_queued": {
                              "type": "integer"
                            },
                            "estimated_completion_seconds": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        },
                        "cost_cents": {
                          "type": "integer",
                          "description": "What this call charged the list's organization, in cents. 0 when nothing was queued or the run was already in progress."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "list_id": "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42",
                    "analysis": {
                      "status": "processing",
                      "numbers_queued": 11800,
                      "estimated_completion_seconds": 120
                    },
                    "cost_cents": 472
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/contact-lists/{id}/download": {
      "get": {
        "tags": [
          "Contact Lists"
        ],
        "summary": "Download Contact List CSV",
        "operationId": "downloadContactList",
        "description": "Stream a contact list as CSV. `type=original` returns `Phone Number`, `Original Row`, then one column per custom field. `type=analyzed` returns `Phone Number`, `Phone Type`, `Carrier Name`, `Is Mobile`, `Is Opted Out`, `City`, `State`, then the custom fields, and is available once `analysis.status` is `complete`; before that it returns `409` `ANALYSIS_NOT_COMPLETE`. A list outside the key's scope returns `404`; a missing or invalid `type` returns `400`.",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "name": "type",
            "in": "query",
            "required": true,
            "description": "Which file to download.",
            "schema": {
              "type": "string",
              "enum": [
                "original",
                "analyzed"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV stream, sent as an attachment.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "`type=analyzed` was requested before analysis completed (error code ANALYSIS_NOT_COMPLETE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/media": {
      "get": {
        "tags": [
          "Media Files"
        ],
        "summary": "List Media Files",
        "description": "List all media files. Filter by organization or brand.",
        "operationId": "listMedia",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          },
          {
            "$ref": "#/components/parameters/BrandId"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "brand_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "brand_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "url": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Permanent URL of the stored file. Populated only when `status` is `ready`, and null while the file is still `optimizing` or after a failure. Stable for the life of the file: a stored file is never re-processed or rewritten, so this URL always returns the same bytes. Treat it as opaque, since the filename can differ from `name` (video is converted to `.mp4`)."
                          },
                          "storage_key": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Internal object key. Null until `status` is `ready`."
                          },
                          "file_size_bytes": {
                            "type": "integer",
                            "description": "Size of the stored file in bytes. On rows that are not yet `ready`, this is the size as submitted rather than the stored size."
                          },
                          "content_type": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Inferred from the file extension; null when unknown."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "optimizing",
                              "ready",
                              "failed"
                            ]
                          },
                          "uploaded_via_api": {
                            "type": "boolean"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "5d2b8f4a-7c31-4e9d-b6a8-3f9c1e5d7a20",
                      "name": "rally-photo.jpg",
                      "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                      "brand_name": "Smith PAC",
                      "org_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                      "org_name": "Smith Campaign 2024",
                      "created_at": "2025-04-02T15:30:00.000Z",
                      "url": "https://media.politicalcomms.com/0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10/7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64/rally-photo_image_5d2b8f4a.jpg",
                      "storage_key": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10/7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64/rally-photo_image_5d2b8f4a.jpg",
                      "file_size_bytes": 482133,
                      "content_type": "image/jpeg",
                      "status": "ready",
                      "uploaded_via_api": true
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Media Files"
        ],
        "summary": "Import Media",
        "description": "Asynchronous job: provide an HTTPS URL to the file and the server retrieves and stores it. Returns immediately; poll `GET /media/{id}` until `status` is `ready`.",
        "operationId": "importMedia",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_url"
                ],
                "properties": {
                  "source_url": {
                    "type": "string",
                    "format": "uri"
                  },
                  "organization_id": {
                    "type": "string",
                    "description": "Target organization for the media file. Defaults to the organization that owns the API key. Required only when targeting a sub-organization under an agency-tier parent; the API key must have access to the sub-organization (otherwise the request returns 403 ORG_ACCESS_DENIED)."
                  },
                  "brand_id": {
                    "type": "string",
                    "description": "Optional. When provided, the media file is scoped to this brand and the brand's organization is used (must match `organization_id` if both are sent, otherwise returns 400 BRAND_ORG_MISMATCH)."
                  },
                  "name": {
                    "type": "string"
                  },
                  "usage": {
                    "type": "string",
                    "enum": [
                      "mms",
                      "email_asset"
                    ],
                    "default": "mms",
                    "description": "Which library the file lands in. `mms` is a texting attachment. `email_asset` is an image for the email template library, which is what Lincoln drafting and the document editor read from. Email assets are organization-scoped, so `brand_id` must be omitted when `usage` is `email_asset`."
                  }
                }
              },
              "example": {
                "source_url": "https://example.com/path/to/image.jpg",
                "organization_id": "01HX0000000000000000000000",
                "brand_id": "01HX0000000000000000000001",
                "name": "spring-outreach-hero",
                "usage": "mms"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Import accepted and optimizing asynchronously. Poll GET /media/{id} for readiness.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "media_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "optimizing"
                          ]
                        },
                        "uploaded_via_api": {
                          "type": "boolean"
                        },
                        "file_size_bytes": {
                          "type": "integer"
                        },
                        "name": {
                          "type": "string"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "media_id": "5d2b8f4a-7c31-4e9d-b6a8-3f9c1e5d7a20",
                    "status": "optimizing",
                    "uploaded_via_api": true,
                    "file_size_bytes": 482133,
                    "name": "rally-photo.jpg"
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ]
      }
    },
    "/media/{id}": {
      "get": {
        "tags": [
          "Media Files"
        ],
        "summary": "Get Media File",
        "description": "Fetch a single media file including optimization status and metadata.",
        "operationId": "getMedia",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "media_id": {
                          "type": "string"
                        },
                        "organization_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "brand_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "optimizing",
                            "ready",
                            "failed"
                          ]
                        },
                        "content_type": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Inferred from the file extension; null when unknown."
                        },
                        "file_size_bytes": {
                          "type": "integer",
                          "description": "0 until the file has been fetched and measured."
                        },
                        "storage_key": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Permanent URL of the stored file. Populated only when `status` is `ready`, and null while the file is still `optimizing` or after a failure. Stable for the life of the file: a stored file is never re-processed or rewritten, so this URL always returns the same bytes. Treat it as opaque, since the filename can differ from `name` (video is converted to `.mp4`)."
                        },
                        "uploaded_via_api": {
                          "type": "boolean"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "optimized_at": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "media_id": "5d2b8f4a-7c31-4e9d-b6a8-3f9c1e5d7a20",
                    "organization_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                    "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                    "name": "rally-photo.jpg",
                    "status": "ready",
                    "content_type": "image/jpeg",
                    "file_size_bytes": 482133,
                    "storage_key": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10/7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64/rally-photo_image_5d2b8f4a.jpg",
                    "url": "https://media.politicalcomms.com/0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10/7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64/rally-photo_image_5d2b8f4a.jpg",
                    "uploaded_via_api": true,
                    "created_at": "2025-04-02T15:30:00.000Z",
                    "optimized_at": "2025-04-02T15:30:12.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "delete": {
        "tags": [
          "Media Files"
        ],
        "summary": "Delete Media File",
        "description": "Permanently remove a media file from your account. The file is detached from any draft projects or survey questions that reference it and no longer appears in list or read endpoints. Already-sent MMS messages keep rendering their media. Deletion is blocked with a 409 while a scheduled, sending, or active project uses the file; unschedule the project or wait for the send to finish, then retry.",
        "operationId": "deleteMedia",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Media file deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "media_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "deleted": {
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "media_id": "9c2e4f8a-1d37-4b52-8e69-3a5c7d9f1e84",
                    "name": "rally-flyer.jpg",
                    "deleted": true
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The media file is used by a project that is scheduled, sending, or active. Error code MEDIA_IN_USE; `details.projects` lists the blocking projects as `{id, name, status}`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "List Projects",
        "description": "List all projects (broadcast and survey). Filter by organization, brand, campaign, or project type.",
        "operationId": "listProjects",
        "parameters": [
          {
            "$ref": "#/components/parameters/OrganizationId"
          },
          {
            "$ref": "#/components/parameters/BrandId"
          },
          {
            "$ref": "#/components/parameters/CampaignId"
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "broadcast",
                "survey"
              ]
            },
            "description": "Filter by project type."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "broadcast",
                              "survey"
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "channel": {
                            "type": "string",
                            "enum": [
                              "10dlc",
                              "toll-free"
                            ]
                          },
                          "campaign_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "null for toll-free projects."
                          },
                          "campaign_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "brand_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "null for toll-free projects."
                          },
                          "brand_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "toll_free_verification_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "null for 10DLC projects."
                          },
                          "contact_list_ids": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "org_id": {
                            "type": "string"
                          },
                          "org_name": {
                            "type": "string"
                          },
                          "message_text": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "null for survey projects."
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        },
                        "additionalProperties": true
                      }
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": [
                    {
                      "id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                      "name": "July GOTV Blast",
                      "type": "broadcast",
                      "status": "completed",
                      "channel": "10dlc",
                      "campaign_id": "4b8fa3c1-6d25-4e9a-bf47-8c2e5d9a3f71",
                      "campaign_name": "Fall 2025 GOTV",
                      "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                      "brand_name": "Smith PAC",
                      "toll_free_verification_id": null,
                      "contact_list_ids": [
                        "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42"
                      ],
                      "org_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                      "org_name": "Smith Campaign 2024",
                      "message_text": "Hi {{first_name}}, election day is coming up!",
                      "created_at": "2025-07-01T14:00:00.000Z"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Create Project",
        "description": "Create a new project. `type` selects the payload shape: `broadcast` (the default) sends one message to the audience; `survey` runs a multi-question flow with branching.\n\n**Phone numbers.** Assign one or more sending numbers via `phone_number_ids` (up to 49). The project spreads new conversations randomly across the assigned numbers, and once a recipient has been messaged from a given number every later message to that recipient comes from the same number (sticky sender). A single legacy `phone_number_id` is still accepted and is treated as a one-element `phone_number_ids`.\n\n**Channel.** `channel` defaults to `10dlc`. For broadcast projects a `10dlc` channel requires `brand_id` + `campaign_id` (and `toll_free_verification_id` must be omitted), while `toll-free` requires `toll_free_verification_id` with matching toll-free `phone_number_ids` (`brand_id`/`campaign_id` omitted). Survey projects always require `campaign_id`, on both channels.\n\n**Surveys.** `questions` defines the whole flow: an `intro` at sequence 1, then `multiple_choice`/`open_ended` questions, optionally ending with an `outro`. Multiple-choice options can branch with `next_sequence`, and `no_match_sequence` routes unmatched replies; both are forward-only. Each question can be SMS or MMS (`message_type` + `media_ids`). Question text supports merge tags and `{tracking_url}`. Surveys need at least 2 questions and 1 phone number.\n\nWhen `link_tracking_enabled` is `true`, `link_tracking_destination_url` and `link_tracking_domain_id` become required. The destination URL may embed the selected `link_tracking_param_field` anywhere via a placeholder named after it, e.g. `https://test.com?utm_content=xyzd_{linkid}` redirects as `...utm_content=xyzd_ABC123` (no separate `&linkid=` pair is appended). Without a placeholder the field is appended as its own query pair. `link_tracking_destination_url` may also be exactly `https://{<link_tracking_param_field>}`, e.g. `https://{custom_url}`; each recipient's tracking link then redirects to the URL stored in that contact field instead (`short_id` is still appended as a query param, but no `<field>` query param is). In this mode `link_tracking_fallback_url` is required, and recipients whose field is empty or not a valid URL go there instead. `phone` cannot be used as a whole-URL destination, and a `{field}` token in the host position must be the entire URL (`https://{custom_url}/path` is rejected). A placeholder that does not match the selected field, or an invalid whole-URL form, is rejected with a 400 (`INVALID_LINK_PLACEHOLDER`).\n\n**Opt-out footer.** Broadcast messages automatically carry the `STOP=END` opt-out footer. Set `opt_out_footer_enabled` to `false` to disable it (defaults to `true`); surveys never carry the footer.\n\n**Drafts.** `contact_list_ids` is optional. With at least one list the created project starts in `awaiting_test` status (send a test, then schedule). Without it the project is created as a `draft`; attach lists later via `PATCH /projects/{id}` and the project moves to `awaiting_test` automatically. Drafts cannot be tested or scheduled. The response's `completeness` block shows what is still missing.",
        "operationId": "createProject",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": [
                      "organization_id",
                      "name",
                      "protocol",
                      "message_text"
                    ],
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "broadcast"
                        ],
                        "description": "Project type. Optional for broadcast (the default)."
                      },
                      "organization_id": {
                        "type": "string"
                      },
                      "channel": {
                        "type": "string",
                        "enum": [
                          "10dlc",
                          "toll-free"
                        ],
                        "default": "10dlc",
                        "description": "Messaging channel. Defaults to 10dlc."
                      },
                      "brand_id": {
                        "type": "string",
                        "description": "Required when channel=10dlc; omit when channel=toll-free."
                      },
                      "campaign_id": {
                        "type": "string",
                        "description": "Required when channel=10dlc; omit when channel=toll-free."
                      },
                      "toll_free_verification_id": {
                        "type": "string",
                        "description": "Required when channel=toll-free; omit when channel=10dlc. Must match the verification behind the chosen phone_number_ids."
                      },
                      "phone_number_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "maxItems": 49,
                        "description": "Sending phone number IDs (1-49). At least one of phone_number_ids or phone_number_id is required. New conversations are spread randomly across the numbers; each recipient is then pinned to one number."
                      },
                      "phone_number_id": {
                        "type": "string",
                        "deprecated": true,
                        "description": "Deprecated. Use phone_number_ids. A single id is accepted and treated as a one-element phone_number_ids. Provide one of phone_number_ids or phone_number_id."
                      },
                      "name": {
                        "type": "string"
                      },
                      "protocol": {
                        "type": "string",
                        "enum": [
                          "sms",
                          "mms"
                        ]
                      },
                      "contact_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "description": "Optional. Omit to create the project as a draft with no audience; attach lists later via PATCH /projects/{id}. When present, must contain at least one list id."
                      },
                      "suppression_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "message_text": {
                        "type": "string"
                      },
                      "media_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "link_tracking_enabled": {
                        "type": "boolean"
                      },
                      "link_tracking_destination_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Where tracking links redirect. May embed the selected link parameter anywhere via a placeholder named after it, e.g. 'https://test.com?utm_content=xyzd_{linkid}' redirects as '...utm_content=xyzd_ABC123' (URL-encoded value, empty when the contact has none; the parameter is then not appended separately). A placeholder that does not match link_tracking_param_field is rejected with a 400 (INVALID_LINK_PLACEHOLDER). Without a placeholder the parameter is appended as its own query pair. May also be exactly 'https://{<link_tracking_param_field>}' to send each recipient to the URL in that contact field; then link_tracking_fallback_url is required. '{phone}' is not allowed as a whole URL."
                      },
                      "link_tracking_domain_id": {
                        "type": "string"
                      },
                      "link_tracking_param_field": {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Contact field carried on tracking-link redirects. Use 'phone', a contact custom-field name, or omit for no param (default). By default the field's name becomes an appended query-param key with the contact's value; a matching {field} placeholder in link_tracking_destination_url embeds the value there instead."
                      },
                      "link_tracking_fallback_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Redirect used when a recipient's link_tracking_param_field value is empty or not a valid URL. Required when link_tracking_destination_url is a whole-URL placeholder (e.g. 'https://{custom_url}'); rejected with a 400 (INVALID_LINK_PLACEHOLDER) otherwise."
                      },
                      "opt_out_footer_enabled": {
                        "type": "boolean",
                        "default": true,
                        "description": "Whether the 'STOP=END' opt-out footer is appended to every outbound message. Defaults to true. Broadcast projects only; surveys never carry the footer."
                      }
                    },
                    "title": "Broadcast project",
                    "description": "Unknown body properties are rejected with a 400 (strict validation)."
                  },
                  {
                    "title": "Survey project",
                    "type": "object",
                    "required": [
                      "type",
                      "organization_id",
                      "campaign_id",
                      "name",
                      "questions"
                    ],
                    "properties": {
                      "type": {
                        "type": "string",
                        "enum": [
                          "survey"
                        ],
                        "description": "Must be `survey`."
                      },
                      "organization_id": {
                        "type": "string"
                      },
                      "campaign_id": {
                        "type": "string",
                        "description": "Always required for surveys, on both channels (surveys route through a brand + campaign; there is no toll-free verification path for surveys)."
                      },
                      "name": {
                        "type": "string"
                      },
                      "channel": {
                        "type": "string",
                        "enum": [
                          "10dlc",
                          "toll-free"
                        ],
                        "default": "10dlc",
                        "description": "Messaging channel. Defaults to 10dlc."
                      },
                      "phone_number_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "maxItems": 49,
                        "description": "Sending phone number IDs (1-49). At least one of phone_number_ids or phone_number_id is required. New conversations are spread randomly across the numbers; each recipient is then pinned to one number."
                      },
                      "phone_number_id": {
                        "type": "string",
                        "deprecated": true,
                        "description": "Deprecated. Use phone_number_ids. A single id is accepted and treated as a one-element phone_number_ids."
                      },
                      "contact_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "description": "Optional. Omit to create the survey as a draft with no audience; attach lists later via PATCH /projects/{id}. When present, must contain at least one list id."
                      },
                      "suppression_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "questions": {
                        "type": "array",
                        "minItems": 2,
                        "maxItems": 32,
                        "description": "The full question flow, at least an intro plus one question (max 32 total). Only recipients who reply advance to later questions.",
                        "items": {
                          "type": "object",
                          "required": [
                            "sequence",
                            "question_type",
                            "question_text"
                          ],
                          "properties": {
                            "sequence": {
                              "type": "integer",
                              "minimum": 1,
                              "maximum": 32,
                              "description": "Position in the flow. Sequences must be exactly 1..N with no gaps; sequence 1 must be the intro."
                            },
                            "question_type": {
                              "type": "string",
                              "enum": [
                                "intro",
                                "multiple_choice",
                                "open_ended",
                                "outro"
                              ],
                              "description": "intro: the opening message (must be sequence 1). multiple_choice: 2-9 numbered response_options. open_ended: free-text answer, no response_options. outro: closing message, expects no reply."
                            },
                            "question_text": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 1600,
                              "description": "Message text. Supports merge tags (`{first_name|there}`, contact custom fields) and `{tracking_url}` when link tracking is enabled on the project."
                            },
                            "message_type": {
                              "type": "string",
                              "enum": [
                                "sms",
                                "mms"
                              ],
                              "default": "sms",
                              "description": "Per-question delivery type. MMS questions require media_ids."
                            },
                            "media_ids": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "maxItems": 10,
                              "description": "Media file IDs attached to this question (see Media Files). Required (at least one) when message_type=mms; not allowed when message_type=sms."
                            },
                            "response_options": {
                              "type": "array",
                              "minItems": 2,
                              "maxItems": 9,
                              "description": "Numbered answer options. Required for multiple_choice; not allowed for open_ended.",
                              "items": {
                                "type": "object",
                                "required": [
                                  "value",
                                  "label"
                                ],
                                "properties": {
                                  "value": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 9,
                                    "description": "The digit a recipient texts to pick this option."
                                  },
                                  "label": {
                                    "type": "string",
                                    "maxLength": 200,
                                    "description": "Descriptive label used for matching and reporting."
                                  },
                                  "next_sequence": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 32,
                                    "description": "Branch target when this option is picked. Must point to a later question (forward-only, no loops). Omit for linear progression to sequence + 1."
                                  }
                                }
                              }
                            },
                            "no_match_sequence": {
                              "type": "integer",
                              "minimum": 1,
                              "maximum": 32,
                              "description": "Branch target when a reply matches none of the response_options. Only valid on questions with response_options; forward-only. Omit to fall through to sequence + 1."
                            },
                            "is_required": {
                              "type": "boolean",
                              "default": true
                            }
                          }
                        }
                      },
                      "link_tracking_enabled": {
                        "type": "boolean"
                      },
                      "link_tracking_destination_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Where tracking links redirect. May embed the selected link_tracking_param_field via a {field} placeholder (see POST /projects). May also be exactly 'https://{<link_tracking_param_field>}' to send each recipient to the URL in that contact field; then link_tracking_fallback_url is required. '{phone}' is not allowed as a whole URL."
                      },
                      "link_tracking_domain_id": {
                        "type": "string"
                      },
                      "link_tracking_param_field": {
                        "type": "string",
                        "maxLength": 64,
                        "description": "Contact field appended as a redirect query param on tracking links. Use 'phone', a contact custom-field name, or omit for no param (default)."
                      },
                      "link_tracking_fallback_url": {
                        "type": "string",
                        "format": "uri",
                        "description": "Redirect used when a recipient's link_tracking_param_field value is empty or not a valid URL. Required when link_tracking_destination_url is a whole-URL placeholder (e.g. 'https://{custom_url}'); rejected with a 400 (INVALID_LINK_PLACEHOLDER) otherwise."
                      },
                      "ai_survey_analysis_enabled": {
                        "type": "boolean",
                        "default": false,
                        "description": "When true, replies that fail exact option matching are classified by AI toward the option they clearly express (including that option's branching). Billed per analyzed reply; exact matches stay free."
                      }
                    },
                    "description": "Unknown body properties are rejected with a 400 (strict validation)."
                  }
                ]
              },
              "examples": {
                "10dlc": {
                  "summary": "10DLC project (brand + campaign)",
                  "value": {
                    "organization_id": "01HX0000000000000000000000",
                    "channel": "10dlc",
                    "brand_id": "01HX0000000000000000000001",
                    "campaign_id": "01HX0000000000000000000002",
                    "phone_number_ids": [
                      "01HX0000000000000000000003",
                      "01HX0000000000000000000006"
                    ],
                    "name": "Spring Outreach",
                    "protocol": "sms",
                    "contact_list_ids": [
                      "01HX0000000000000000000004"
                    ],
                    "suppression_list_ids": [],
                    "message_text": "Hi {first_name}, early voting starts Monday. More info: {link}",
                    "media_ids": [],
                    "link_tracking_enabled": true,
                    "link_tracking_destination_url": "https://example.com/vote",
                    "link_tracking_domain_id": "01HX0000000000000000000005",
                    "link_tracking_param_field": "voter_id",
                    "link_tracking_fallback_url": null,
                    "opt_out_footer_enabled": true
                  }
                },
                "toll-free": {
                  "summary": "Toll-free project (verification)",
                  "value": {
                    "organization_id": "01HX0000000000000000000000",
                    "channel": "toll-free",
                    "toll_free_verification_id": "01HX00000000000000000000T0",
                    "phone_number_ids": [
                      "01HX00000000000000000000T1"
                    ],
                    "name": "Spring Outreach (TFN)",
                    "protocol": "sms",
                    "contact_list_ids": [
                      "01HX0000000000000000000004"
                    ],
                    "suppression_list_ids": [],
                    "message_text": "Hi {first_name}, early voting starts Monday.",
                    "media_ids": []
                  }
                },
                "survey": {
                  "summary": "Survey project (branching + AI analysis)",
                  "value": {
                    "type": "survey",
                    "organization_id": "01HX0000000000000000000000",
                    "campaign_id": "01HX0000000000000000000002",
                    "name": "Primary Intent Survey",
                    "channel": "10dlc",
                    "phone_number_ids": [
                      "01HX0000000000000000000003"
                    ],
                    "contact_list_ids": [
                      "01HX0000000000000000000004"
                    ],
                    "questions": [
                      {
                        "sequence": 1,
                        "question_type": "intro",
                        "question_text": "Hi {first_name|there}, it's Smith PAC. Mind answering 2 quick questions? Reply STOP to opt out."
                      },
                      {
                        "sequence": 2,
                        "question_type": "multiple_choice",
                        "question_text": "Do you plan to vote in the primary? Reply 1 for Yes, 2 for No, 3 for Undecided.",
                        "response_options": [
                          {
                            "value": 1,
                            "label": "Yes",
                            "next_sequence": 4
                          },
                          {
                            "value": 2,
                            "label": "No"
                          },
                          {
                            "value": 3,
                            "label": "Undecided"
                          }
                        ],
                        "no_match_sequence": 3
                      },
                      {
                        "sequence": 3,
                        "question_type": "open_ended",
                        "question_text": "What issue matters most to you this year?"
                      },
                      {
                        "sequence": 4,
                        "question_type": "outro",
                        "question_text": "Thanks for your time! Learn more: {tracking_url}"
                      }
                    ],
                    "link_tracking_enabled": true,
                    "link_tracking_destination_url": "https://example.com/volunteer",
                    "link_tracking_domain_id": "01HX0000000000000000000005",
                    "ai_survey_analysis_enabled": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project created. Status reflects completeness (`awaiting_test` when ready to test).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "broadcast",
                            "survey"
                          ]
                        },
                        "status": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "created_via_api": {
                          "type": "boolean"
                        },
                        "question_count": {
                          "type": "integer",
                          "description": "Survey projects only."
                        },
                        "estimated_cost_cents": {
                          "type": "integer"
                        },
                        "total_recipients": {
                          "type": "integer"
                        },
                        "completeness": {
                          "type": "object",
                          "description": "Broadcast reports has_message; surveys report has_questions.",
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  }
                },
                "examples": {
                  "broadcast": {
                    "summary": "Broadcast created",
                    "value": {
                      "success": true,
                      "data": {
                        "project_id": "01HX000000000000000000P001",
                        "name": "Spring Outreach",
                        "type": "broadcast",
                        "status": "awaiting_test",
                        "channel": "10dlc",
                        "created_via_api": true,
                        "estimated_cost_cents": 4250,
                        "total_recipients": 500,
                        "completeness": {
                          "has_list": true,
                          "has_message": true,
                          "has_phone_number": true,
                          "ready_to_test": true
                        }
                      }
                    }
                  },
                  "survey": {
                    "summary": "Survey created",
                    "value": {
                      "success": true,
                      "data": {
                        "project_id": "01HX000000000000000000P002",
                        "name": "Primary Intent Survey",
                        "type": "survey",
                        "status": "awaiting_test",
                        "channel": "10dlc",
                        "created_via_api": true,
                        "question_count": 4,
                        "estimated_cost_cents": 1276,
                        "total_recipients": 500,
                        "completeness": {
                          "has_list": true,
                          "has_questions": true,
                          "has_phone_number": true,
                          "ready_to_test": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, or the requested resource belongs to an organization outside the key's hierarchy.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "onboardingIncomplete": {
                    "summary": "Organization has not completed required onboarding (business profile or funding)",
                    "value": {
                      "error": "Complete your account setup to continue",
                      "code": "ONBOARDING_INCOMPLETE",
                      "details": {
                        "missingSteps": [
                          "profile",
                          "funding"
                        ],
                        "onboardingUrl": "/onboarding"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Sending is paused for the organization or platform-wide (error code SENDING_PAUSED), or a contact list on the project is still being analyzed (error code LIST_ANALYSIS_IN_PROGRESS; nothing is changed, retry as-is once `analysis.status` is `complete`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "listAnalysisInProgress": {
                    "summary": "A contact list on the project is still being analyzed",
                    "value": {
                      "error": "Contact list \"October donors\" is still being analyzed. Wait for analysis to complete, then try again.",
                      "code": "LIST_ANALYSIS_IN_PROGRESS",
                      "details": {
                        "lists": [
                          {
                            "id": "0195f3a2-7c1e-7d4b-9a3e-5b6c7d8e9f01",
                            "name": "October donors",
                            "status": "processing"
                          }
                        ]
                      }
                    }
                  },
                  "organizationPaused": {
                    "summary": "Sending is paused for this organization",
                    "value": {
                      "error": "Sending is currently paused for this organization",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "organization"
                      }
                    }
                  },
                  "platformPaused": {
                    "summary": "Sending is paused platform-wide",
                    "value": {
                      "error": "Sending is currently paused",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "platform"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ]
      }
    },
    "/projects/stats": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get Project Stats (all)",
        "description": "Aggregate per-project statistics for a date range (max 31 days, no older than 90 days). Results are paginated via limit/offset.",
        "operationId": "getAllProjectStats",
        "parameters": [
          {
            "$ref": "#/components/parameters/StartDate"
          },
          {
            "$ref": "#/components/parameters/EndDate"
          },
          {
            "$ref": "#/components/parameters/OrganizationIdCamel"
          },
          {
            "$ref": "#/components/parameters/BrandIdCamel"
          },
          {
            "$ref": "#/components/parameters/CampaignIdCamel"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter by status: draft, active, completed, paused, all",
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "active",
                "completed",
                "paused",
                "all"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 100
            },
            "description": "Number of projects to return (1-1000)."
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            },
            "description": "Number of projects to skip."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "projects": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              },
                              "status": {
                                "type": "string"
                              },
                              "protocol": {
                                "type": "string"
                              },
                              "type": {
                                "type": "string",
                                "enum": [
                                  "broadcast",
                                  "survey"
                                ]
                              },
                              "channel": {
                                "type": "string"
                              },
                              "organization_id": {
                                "type": "string"
                              },
                              "brand_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "campaign_id": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "total_recipients": {
                                "type": "integer"
                              },
                              "messages_sent": {
                                "type": "integer"
                              },
                              "messages_delivered": {
                                "type": "integer"
                              },
                              "messages_failed": {
                                "type": "integer"
                              },
                              "messages_opted_out": {
                                "type": "integer"
                              },
                              "replies_received": {
                                "type": "integer"
                              },
                              "survey_responses_count": {
                                "type": "integer"
                              },
                              "survey_completion_count": {
                                "type": "integer"
                              },
                              "actual_cost": {
                                "type": "string",
                                "description": "Numeric string in dollars, e.g. \"87.1926\"."
                              },
                              "total_url_clicks": {
                                "type": "integer"
                              },
                              "unique_url_clicks": {
                                "type": "integer"
                              },
                              "url_share_count": {
                                "type": "integer"
                              },
                              "created_at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "completed_at": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "updated_at": {
                                "type": "string",
                                "format": "date-time"
                              },
                              "organization_name": {
                                "type": "string"
                              },
                              "brand_name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "campaign_name": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "summary": {
                          "type": "object",
                          "properties": {
                            "totalProjects": {
                              "type": "integer"
                            },
                            "organizationCount": {
                              "type": "integer"
                            },
                            "brandCount": {
                              "type": "integer"
                            },
                            "campaignCount": {
                              "type": "integer"
                            },
                            "statusBreakdown": {
                              "type": "object",
                              "properties": {
                                "draft": {
                                  "type": "integer"
                                },
                                "active": {
                                  "type": "integer"
                                },
                                "completed": {
                                  "type": "integer"
                                },
                                "paused": {
                                  "type": "integer"
                                }
                              },
                              "additionalProperties": true
                            },
                            "messageStats": {
                              "type": "object",
                              "properties": {
                                "totalRecipients": {
                                  "type": "integer"
                                },
                                "totalSent": {
                                  "type": "integer"
                                },
                                "totalDelivered": {
                                  "type": "integer"
                                },
                                "totalFailed": {
                                  "type": "integer"
                                },
                                "totalOptedOut": {
                                  "type": "integer"
                                },
                                "totalReplies": {
                                  "type": "integer"
                                },
                                "totalActualCost": {
                                  "type": "number"
                                },
                                "totalUrlClicks": {
                                  "type": "integer"
                                },
                                "totalUniqueUrlClicks": {
                                  "type": "integer"
                                },
                                "totalUrlShares": {
                                  "type": "integer"
                                }
                              },
                              "additionalProperties": true
                            },
                            "dateRange": {
                              "type": "object",
                              "properties": {
                                "earliestProject": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                },
                                "latestProject": {
                                  "type": [
                                    "string",
                                    "null"
                                  ]
                                }
                              },
                              "additionalProperties": true
                            }
                          },
                          "additionalProperties": true
                        },
                        "pagination": {
                          "type": "object",
                          "properties": {
                            "limit": {
                              "type": "integer"
                            },
                            "offset": {
                              "type": "integer"
                            },
                            "totalCount": {
                              "type": "integer"
                            },
                            "hasMore": {
                              "type": "boolean"
                            }
                          },
                          "additionalProperties": true
                        },
                        "dateRange": {
                          "type": "object",
                          "properties": {
                            "startDate": {
                              "type": "string"
                            },
                            "endDate": {
                              "type": "string"
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "projects": [
                      {
                        "id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                        "name": "July GOTV Blast",
                        "status": "completed",
                        "protocol": "sms",
                        "type": "broadcast",
                        "channel": "10dlc",
                        "organization_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                        "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                        "campaign_id": "4b8fa3c1-6d25-4e9a-bf47-8c2e5d9a3f71",
                        "total_recipients": 11800,
                        "messages_sent": 11800,
                        "messages_delivered": 11342,
                        "messages_failed": 458,
                        "messages_opted_out": 87,
                        "replies_received": 312,
                        "survey_responses_count": 0,
                        "survey_completion_count": 0,
                        "actual_cost": "354.0000",
                        "total_url_clicks": 1893,
                        "unique_url_clicks": 1544,
                        "url_share_count": 12,
                        "created_at": "2025-07-01T14:00:00.000Z",
                        "completed_at": "2025-07-02T18:30:00.000Z",
                        "updated_at": "2025-07-02T18:30:00.000Z",
                        "organization_name": "Smith Campaign 2024",
                        "brand_name": "Smith PAC",
                        "campaign_name": "Fall 2025 GOTV"
                      }
                    ],
                    "summary": {
                      "totalProjects": 1,
                      "organizationCount": 1,
                      "brandCount": 1,
                      "campaignCount": 1,
                      "statusBreakdown": {
                        "draft": 0,
                        "active": 0,
                        "completed": 1,
                        "paused": 0
                      },
                      "messageStats": {
                        "totalRecipients": 11800,
                        "totalSent": 11800,
                        "totalDelivered": 11342,
                        "totalFailed": 458,
                        "totalOptedOut": 87,
                        "totalReplies": 312,
                        "totalActualCost": 354.0,
                        "totalUrlClicks": 1893,
                        "totalUniqueUrlClicks": 1544,
                        "totalUrlShares": 12
                      },
                      "dateRange": {
                        "earliestProject": "2025-07-01T14:00:00.000Z",
                        "latestProject": "2025-07-01T14:00:00.000Z"
                      }
                    },
                    "pagination": {
                      "limit": 100,
                      "offset": 0,
                      "totalCount": 1,
                      "hasMore": false
                    },
                    "dateRange": {
                      "startDate": "2025-07-01",
                      "endDate": "2025-07-31"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get Project",
        "description": "Fetch a single project. Survey projects additionally include the ordered `questions` array; their `message_text`, `protocol`, and `media_urls` mirror the intro question.",
        "operationId": "getProject",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "organization_id": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string",
                          "enum": [
                            "10dlc",
                            "toll-free"
                          ]
                        },
                        "brand_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for toll-free projects."
                        },
                        "campaign_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for toll-free broadcast projects."
                        },
                        "toll_free_verification_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for 10DLC projects."
                        },
                        "phone_number_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "broadcast",
                            "survey"
                          ]
                        },
                        "protocol": {
                          "type": "string",
                          "enum": [
                            "sms",
                            "mms"
                          ]
                        },
                        "message_text": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for survey projects."
                        },
                        "contact_list_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "suppression_list_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "media_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Always an empty array in the current API version; attached media is exposed via media_urls."
                        },
                        "media_urls": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "link_tracking_enabled": {
                          "type": "boolean"
                        },
                        "link_tracking_destination_url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "May be exactly 'https://{<link_tracking_param_field>}' (a whole-URL placeholder). In that mode link_tracking_fallback_url is also set, and each recipient's tracking link resolved to the URL in that recipient's contact field (or the fallback) at link-creation time."
                        },
                        "link_tracking_domain_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "link_tracking_param_field": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "link_tracking_fallback_url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Redirect used when a recipient's link_tracking_param_field value was empty or not a valid URL. Set only when link_tracking_destination_url is a whole-URL placeholder."
                        },
                        "opt_out_footer_enabled": {
                          "type": "boolean",
                          "description": "Whether the 'STOP=END' opt-out footer is appended to outbound messages (always true for surveys' stored flag, but surveys never send the footer)."
                        },
                        "ai_survey_analysis_enabled": {
                          "type": "boolean"
                        },
                        "total_recipients": {
                          "type": "integer"
                        },
                        "estimated_cost_cents": {
                          "type": "integer"
                        },
                        "scheduled_at": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scheduled_timezone": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "pause_reason": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Why a `paused` project stopped; null when not paused. Known values: `brand_daily_cap` (T-Mobile daily cap reached; schedule it for the next day's sending hours with `POST /projects/{id}/schedule` and a morning `scheduled_at`, or resume now with `daily_cap_bypass: true`), `quiet_hours` (paused when the project's sending window closes: 10 PM in the time zone of most recipients, or 10 PM Pacific when no zone holds a majority; restart manually once the window opens), `carrier_block_rate`, `unregistered_campaign`, `provider_error`, `insufficient_funds_auto_recharge_failed`, `insufficient_funds_ancestor`, `shared_phone_revoked`, `phone_released`, `organization_deleted`, and `manual` or a short free-text reason (up to 100 characters) set by a user who paused it. Other values may appear. Every automatic pause needs a manual restart via `POST /projects/{id}/schedule`."
                        },
                        "auto_paused": {
                          "type": "boolean",
                          "description": "true when the platform paused the project itself (for example at the daily cap). Start it again with `POST /projects/{id}/schedule`."
                        },
                        "daily_cap_bypass": {
                          "type": "boolean",
                          "description": "Whether the project runs through the T-Mobile daily cap. Set by the last schedule or resume call."
                        },
                        "created_via_api": {
                          "type": "boolean"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "questions": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "sequence": {
                                "type": "integer"
                              },
                              "question_type": {
                                "type": "string",
                                "enum": [
                                  "intro",
                                  "multiple_choice",
                                  "open_ended",
                                  "outro"
                                ]
                              },
                              "question_text": {
                                "type": "string"
                              },
                              "message_type": {
                                "type": "string",
                                "enum": [
                                  "sms",
                                  "mms"
                                ]
                              },
                              "media_urls": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "response_options": {
                                "type": [
                                  "array",
                                  "null"
                                ],
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "value": {
                                      "type": "integer"
                                    },
                                    "label": {
                                      "type": "string"
                                    },
                                    "next_sequence": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    }
                                  },
                                  "additionalProperties": true
                                }
                              },
                              "no_match_sequence": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "is_required": {
                                "type": "boolean"
                              }
                            },
                            "additionalProperties": true
                          },
                          "description": "Survey projects only; absent on broadcast projects."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "organization_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                    "channel": "10dlc",
                    "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                    "campaign_id": "4b8fa3c1-6d25-4e9a-bf47-8c2e5d9a3f71",
                    "toll_free_verification_id": null,
                    "phone_number_ids": [
                      "b2e8d4f6-1a37-4c95-8e62-5d9f3b7a1c48"
                    ],
                    "name": "July GOTV Blast",
                    "status": "ready",
                    "type": "broadcast",
                    "protocol": "sms",
                    "message_text": "Hi {{first_name}}, election day is coming up!",
                    "contact_list_ids": [
                      "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42"
                    ],
                    "suppression_list_ids": [],
                    "media_ids": [],
                    "media_urls": [],
                    "link_tracking_enabled": true,
                    "link_tracking_destination_url": "https://smithcampaign.com/vote",
                    "link_tracking_domain_id": "6e2c9a4f-8b51-4d73-a9e6-1f4b8c5d2a90",
                    "link_tracking_param_field": "phone",
                    "link_tracking_fallback_url": null,
                    "opt_out_footer_enabled": true,
                    "ai_survey_analysis_enabled": false,
                    "total_recipients": 11800,
                    "estimated_cost_cents": 35400,
                    "scheduled_at": null,
                    "scheduled_timezone": null,
                    "created_via_api": true,
                    "created_at": "2025-07-01T14:00:00.000Z",
                    "updated_at": "2025-07-02T09:15:00.000Z",
                    "pause_reason": null,
                    "auto_paused": false,
                    "daily_cap_bypass": false
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "patch": {
        "tags": [
          "Projects"
        ],
        "summary": "Update Project",
        "description": "Update a project while in `draft`, `awaiting_test`, or `ready`. `organization_id` / `brand_id` / `campaign_id` / `channel` / `toll_free_verification_id` / `type` are immutable after creation; including them in the body returns a 400. All other fields are partial: send only the keys you want to change.\n\nThe payload shape follows the project's `type`. For surveys, `questions` is a full replacement: sending it swaps the entire question set (same rules as create). Editing content on a `ready` project moves it back to `awaiting_test` (re-test required).",
        "operationId": "updateProject",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "message_text": {
                        "type": "string"
                      },
                      "protocol": {
                        "type": "string",
                        "enum": [
                          "sms",
                          "mms"
                        ]
                      },
                      "phone_number_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "maxItems": 49,
                        "description": "Replace the project's sending phone numbers (1-49). New conversations rotate across them; existing conversations keep their already-assigned sticky number."
                      },
                      "phone_number_id": {
                        "type": "string",
                        "deprecated": true,
                        "description": "Deprecated. Use phone_number_ids. A single id is treated as a one-element phone_number_ids."
                      },
                      "contact_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1
                      },
                      "suppression_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "media_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "link_tracking_enabled": {
                        "type": "boolean"
                      },
                      "link_tracking_destination_url": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri",
                        "description": "Where tracking links redirect. May embed the selected link parameter via a {field} placeholder named after it (see POST /projects); a placeholder that does not match the effective link_tracking_param_field is rejected with a 400 (INVALID_LINK_PLACEHOLDER). May also be exactly 'https://{<link_tracking_param_field>}' to send each recipient to the URL in that contact field; then link_tracking_fallback_url is required. '{phone}' is not allowed as a whole URL."
                      },
                      "link_tracking_domain_id": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "link_tracking_param_field": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "maxLength": 64,
                        "description": "Contact field carried on tracking-link redirects ('phone', a custom-field name, or null for none). Appended as a query pair, or embedded in place of a matching {field} placeholder in the destination URL."
                      },
                      "link_tracking_fallback_url": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri",
                        "description": "Redirect used when a recipient's link_tracking_param_field value is empty or not a valid URL. Required when link_tracking_destination_url is a whole-URL placeholder (e.g. 'https://{custom_url}'); rejected with a 400 (INVALID_LINK_PLACEHOLDER) otherwise."
                      },
                      "opt_out_footer_enabled": {
                        "type": "boolean",
                        "description": "Whether the 'STOP=END' opt-out footer is appended to every outbound message."
                      }
                    },
                    "title": "Broadcast project update"
                  },
                  {
                    "title": "Survey project update",
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "phone_number_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1,
                        "maxItems": 49
                      },
                      "phone_number_id": {
                        "type": "string",
                        "deprecated": true
                      },
                      "contact_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 1
                      },
                      "suppression_list_ids": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "questions": {
                        "type": "array",
                        "minItems": 2,
                        "maxItems": 32,
                        "description": "Full replacement of the question set (same rules as create). Omit to leave questions unchanged.",
                        "items": {
                          "type": "object",
                          "required": [
                            "sequence",
                            "question_type",
                            "question_text"
                          ],
                          "properties": {
                            "sequence": {
                              "type": "integer",
                              "minimum": 1,
                              "maximum": 32,
                              "description": "Position in the flow. Sequences must be exactly 1..N with no gaps; sequence 1 must be the intro."
                            },
                            "question_type": {
                              "type": "string",
                              "enum": [
                                "intro",
                                "multiple_choice",
                                "open_ended",
                                "outro"
                              ],
                              "description": "intro: the opening message (must be sequence 1). multiple_choice: 2-9 numbered response_options. open_ended: free-text answer, no response_options. outro: closing message, expects no reply."
                            },
                            "question_text": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 1600,
                              "description": "Message text. Supports merge tags (`{first_name|there}`, contact custom fields) and `{tracking_url}` when link tracking is enabled on the project."
                            },
                            "message_type": {
                              "type": "string",
                              "enum": [
                                "sms",
                                "mms"
                              ],
                              "default": "sms",
                              "description": "Per-question delivery type. MMS questions require media_ids."
                            },
                            "media_ids": {
                              "type": "array",
                              "items": {
                                "type": "string"
                              },
                              "maxItems": 10,
                              "description": "Media file IDs attached to this question (see Media Files). Required (at least one) when message_type=mms; not allowed when message_type=sms."
                            },
                            "response_options": {
                              "type": "array",
                              "minItems": 2,
                              "maxItems": 9,
                              "description": "Numbered answer options. Required for multiple_choice; not allowed for open_ended.",
                              "items": {
                                "type": "object",
                                "required": [
                                  "value",
                                  "label"
                                ],
                                "properties": {
                                  "value": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 9,
                                    "description": "The digit a recipient texts to pick this option."
                                  },
                                  "label": {
                                    "type": "string",
                                    "maxLength": 200,
                                    "description": "Descriptive label used for matching and reporting."
                                  },
                                  "next_sequence": {
                                    "type": "integer",
                                    "minimum": 1,
                                    "maximum": 32,
                                    "description": "Branch target when this option is picked. Must point to a later question (forward-only, no loops). Omit for linear progression to sequence + 1."
                                  }
                                }
                              }
                            },
                            "no_match_sequence": {
                              "type": "integer",
                              "minimum": 1,
                              "maximum": 32,
                              "description": "Branch target when a reply matches none of the response_options. Only valid on questions with response_options; forward-only. Omit to fall through to sequence + 1."
                            },
                            "is_required": {
                              "type": "boolean",
                              "default": true
                            }
                          }
                        }
                      },
                      "link_tracking_enabled": {
                        "type": "boolean"
                      },
                      "link_tracking_destination_url": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri",
                        "description": "May also be exactly 'https://{<link_tracking_param_field>}' to send each recipient to the URL in that contact field; then link_tracking_fallback_url is required. '{phone}' is not allowed as a whole URL."
                      },
                      "link_tracking_domain_id": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "link_tracking_param_field": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "maxLength": 64
                      },
                      "link_tracking_fallback_url": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uri",
                        "description": "Redirect used when a recipient's link_tracking_param_field value is empty or not a valid URL. Required when link_tracking_destination_url is a whole-URL placeholder; rejected with a 400 (INVALID_LINK_PLACEHOLDER) otherwise."
                      },
                      "ai_survey_analysis_enabled": {
                        "type": "boolean"
                      }
                    }
                  }
                ]
              },
              "example": {
                "name": "Spring Outreach (revised)",
                "message_text": "Hi {first_name}, early voting starts Monday. Learn more: {link}",
                "media_ids": []
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Project updated. Returns the full project object (same shape as GET /projects/{id}).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "organization_id": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string",
                          "enum": [
                            "10dlc",
                            "toll-free"
                          ]
                        },
                        "brand_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for toll-free projects."
                        },
                        "campaign_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for toll-free broadcast projects."
                        },
                        "toll_free_verification_id": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for 10DLC projects."
                        },
                        "phone_number_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "broadcast",
                            "survey"
                          ]
                        },
                        "protocol": {
                          "type": "string",
                          "enum": [
                            "sms",
                            "mms"
                          ]
                        },
                        "message_text": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "null for survey projects."
                        },
                        "contact_list_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "suppression_list_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "media_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Always an empty array in the current API version; attached media is exposed via media_urls."
                        },
                        "media_urls": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "link_tracking_enabled": {
                          "type": "boolean"
                        },
                        "link_tracking_destination_url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "May be exactly 'https://{<link_tracking_param_field>}' (a whole-URL placeholder). In that mode link_tracking_fallback_url is also set, and each recipient's tracking link resolved to the URL in that recipient's contact field (or the fallback) at link-creation time."
                        },
                        "link_tracking_domain_id": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "link_tracking_param_field": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "link_tracking_fallback_url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Redirect used when a recipient's link_tracking_param_field value was empty or not a valid URL. Set only when link_tracking_destination_url is a whole-URL placeholder."
                        },
                        "opt_out_footer_enabled": {
                          "type": "boolean",
                          "description": "Whether the 'STOP=END' opt-out footer is appended to outbound messages (always true for surveys' stored flag, but surveys never send the footer)."
                        },
                        "ai_survey_analysis_enabled": {
                          "type": "boolean"
                        },
                        "total_recipients": {
                          "type": "integer"
                        },
                        "estimated_cost_cents": {
                          "type": "integer"
                        },
                        "scheduled_at": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "scheduled_timezone": {
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "pause_reason": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Why a `paused` project stopped; null when not paused. Known values: `brand_daily_cap` (T-Mobile daily cap reached; schedule it for the next day's sending hours with `POST /projects/{id}/schedule` and a morning `scheduled_at`, or resume now with `daily_cap_bypass: true`), `quiet_hours` (paused when the project's sending window closes: 10 PM in the time zone of most recipients, or 10 PM Pacific when no zone holds a majority; restart manually once the window opens), `carrier_block_rate`, `unregistered_campaign`, `provider_error`, `insufficient_funds_auto_recharge_failed`, `insufficient_funds_ancestor`, `shared_phone_revoked`, `phone_released`, `organization_deleted`, and `manual` or a short free-text reason (up to 100 characters) set by a user who paused it. Other values may appear. Every automatic pause needs a manual restart via `POST /projects/{id}/schedule`."
                        },
                        "auto_paused": {
                          "type": "boolean",
                          "description": "true when the platform paused the project itself (for example at the daily cap). Start it again with `POST /projects/{id}/schedule`."
                        },
                        "daily_cap_bypass": {
                          "type": "boolean",
                          "description": "Whether the project runs through the T-Mobile daily cap. Set by the last schedule or resume call."
                        },
                        "created_via_api": {
                          "type": "boolean"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "updated_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "questions": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "id": {
                                "type": "string"
                              },
                              "sequence": {
                                "type": "integer"
                              },
                              "question_type": {
                                "type": "string",
                                "enum": [
                                  "intro",
                                  "multiple_choice",
                                  "open_ended",
                                  "outro"
                                ]
                              },
                              "question_text": {
                                "type": "string"
                              },
                              "message_type": {
                                "type": "string",
                                "enum": [
                                  "sms",
                                  "mms"
                                ]
                              },
                              "media_urls": {
                                "type": "array",
                                "items": {
                                  "type": "string"
                                }
                              },
                              "response_options": {
                                "type": [
                                  "array",
                                  "null"
                                ],
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "value": {
                                      "type": "integer"
                                    },
                                    "label": {
                                      "type": "string"
                                    },
                                    "next_sequence": {
                                      "type": [
                                        "integer",
                                        "null"
                                      ]
                                    }
                                  },
                                  "additionalProperties": true
                                }
                              },
                              "no_match_sequence": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "is_required": {
                                "type": "boolean"
                              }
                            },
                            "additionalProperties": true
                          },
                          "description": "Survey projects only; absent on broadcast projects."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "organization_id": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                    "channel": "10dlc",
                    "brand_id": "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64",
                    "campaign_id": "4b8fa3c1-6d25-4e9a-bf47-8c2e5d9a3f71",
                    "toll_free_verification_id": null,
                    "phone_number_ids": [
                      "b2e8d4f6-1a37-4c95-8e62-5d9f3b7a1c48"
                    ],
                    "name": "July GOTV Blast",
                    "status": "ready",
                    "type": "broadcast",
                    "protocol": "sms",
                    "message_text": "Hi {{first_name}}, election day is coming up!",
                    "contact_list_ids": [
                      "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42"
                    ],
                    "suppression_list_ids": [],
                    "media_ids": [],
                    "media_urls": [],
                    "link_tracking_enabled": true,
                    "link_tracking_destination_url": "https://smithcampaign.com/vote",
                    "link_tracking_domain_id": "6e2c9a4f-8b51-4d73-a9e6-1f4b8c5d2a90",
                    "link_tracking_param_field": "phone",
                    "link_tracking_fallback_url": null,
                    "opt_out_footer_enabled": true,
                    "ai_survey_analysis_enabled": false,
                    "total_recipients": 11800,
                    "estimated_cost_cents": 35400,
                    "scheduled_at": null,
                    "scheduled_timezone": null,
                    "created_via_api": true,
                    "created_at": "2025-07-01T14:00:00.000Z",
                    "updated_at": "2025-07-02T09:15:00.000Z",
                    "pause_reason": null,
                    "auto_paused": false,
                    "daily_cap_bypass": false
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "409": {
            "description": "Project is not in an editable state (only draft, awaiting_test, and ready projects can be updated; error code INVALID_STATE_TRANSITION), or a contact list on the project is still being analyzed (error code LIST_ANALYSIS_IN_PROGRESS; nothing is changed, retry as-is once `analysis.status` is `complete`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}/stats": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get Project Stats (single)",
        "description": "Delivery and engagement metrics for a single project. Cached for up to 5 minutes (15 seconds while the project is actively sending).",
        "operationId": "getProjectStats",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "as_of": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "metrics": {
                          "type": "object",
                          "properties": {
                            "sent": {
                              "type": "integer"
                            },
                            "delivered": {
                              "type": "integer"
                            },
                            "undeliverable": {
                              "type": "integer"
                            },
                            "clicks_total": {
                              "type": "integer"
                            },
                            "clicks_unique": {
                              "type": "integer"
                            },
                            "replies": {
                              "type": "integer"
                            },
                            "opt_outs": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        },
                        "test": {
                          "type": "object",
                          "description": "Test-send activity, tracked separately. The `metrics` object excludes test sends and test clicks. Unlike `metrics`, these counters form a pipeline: `sent` holds only tests still awaiting a delivery outcome and moves into `delivered` or `failed` once the carrier reports back.",
                          "properties": {
                            "sent": {
                              "type": "integer"
                            },
                            "delivered": {
                              "type": "integer"
                            },
                            "failed": {
                              "type": "integer"
                            },
                            "replies": {
                              "type": "integer"
                            },
                            "clicks_total": {
                              "type": "integer"
                            },
                            "clicks_unique": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "status": "completed",
                    "as_of": "2025-07-03T12:00:00.000Z",
                    "metrics": {
                      "sent": 11800,
                      "delivered": 11342,
                      "undeliverable": 458,
                      "clicks_total": 1893,
                      "clicks_unique": 1544,
                      "replies": 312,
                      "opt_outs": 87
                    },
                    "test": {
                      "sent": 0,
                      "delivered": 3,
                      "failed": 0,
                      "replies": 1,
                      "clicks_total": 5,
                      "clicks_unique": 2
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/throughput": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get Project Throughput",
        "description": "Pre-flight estimate of how carrier limits will affect this project: recipients expected on T-Mobile and AT&T, whether the project will pause at the T-Mobile daily cap, and how many days or minutes the send needs. `t_mobile` is null when the brand has no T-Mobile daily limit on file (political brands today), and `att` is null only when the campaign has no AT&T rate on file. When the project has `daily_cap_bypass` on, `will_pause` is false and `estimated_send_days` is 1. Results are cached for up to 60 seconds, so numbers can lag by that much. Returns 503 `CARRIER_ESTIMATE_TIMEOUT` if the estimate query times out; retry later. See [Carrier throughput limits](/api-reference/best-practices#carrier-throughput-limits).",
        "operationId": "getProjectThroughput",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "carrier_metered": {
                          "type": "boolean",
                          "description": "true when at least one carrier lane applies to this project's campaign. A political (Campaign Verify) brand with an AT&T rate on file returns true with `t_mobile` null and `att` populated. false only when neither lane applies; `t_mobile` and `att` are then null and `recipients` and `carrier_coverage` are omitted."
                        },
                        "t_mobile": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "properties": {
                            "daily_cap": {
                              "type": [
                                "integer",
                                "null"
                              ]
                            },
                            "used_today": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "Messages counted against the cap so far in the current Pacific day. null (not 0) when usage is temporarily unavailable."
                            },
                            "remaining_today": {
                              "type": [
                                "integer",
                                "null"
                              ],
                              "description": "`daily_cap` minus `used_today`, never below 0. null when `used_today` is null."
                            },
                            "estimated_recipients": {
                              "type": "integer",
                              "description": "Estimated recipients on T-Mobile. Recipients with an unknown carrier are counted using the platform-wide carrier split."
                            },
                            "will_pause": {
                              "type": "boolean",
                              "description": "true when the project will use up the remaining daily cap and auto-pause (`pause_reason` `brand_daily_cap`). Start it again with `POST /projects/{id}/schedule`. Always false when the project has `daily_cap_bypass` on, or when `used_today` is null."
                            },
                            "estimated_send_days": {
                              "type": "integer",
                              "description": "Pacific days needed to finish the T-Mobile recipients at the current cap. 1 when the project will not pause, and always 1 when the project has `daily_cap_bypass` on."
                            }
                          }
                        },
                        "att": {
                          "type": [
                            "object",
                            "null"
                          ],
                          "properties": {
                            "tpm": {
                              "type": "integer",
                              "description": "The campaign's AT&T limit in message parts per minute for this project's protocol."
                            },
                            "estimated_recipients": {
                              "type": "integer"
                            },
                            "estimated_minutes": {
                              "type": "integer",
                              "description": "Minutes AT&T needs at `tpm` to deliver what the campaign already has waiting plus this project's AT&T recipients, accounting for the number of parts in the project's text. In practice AT&T recipients usually receive the message within about an hour of sending. AT&T never pauses a project."
                            }
                          }
                        },
                        "recipients": {
                          "type": "integer",
                          "description": "Total recipients. Present only when `carrier_metered` is true."
                        },
                        "carrier_coverage": {
                          "type": "number",
                          "minimum": 0,
                          "maximum": 1,
                          "description": "Share of recipients whose carrier is known (0 to 1). When low, the per-carrier estimates use the platform-wide carrier split instead. Present only when `carrier_metered` is true."
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "carrier_metered": true,
                    "t_mobile": {
                      "daily_cap": 10000,
                      "used_today": 7250,
                      "remaining_today": 2750,
                      "estimated_recipients": 6400,
                      "will_pause": true,
                      "estimated_send_days": 2
                    },
                    "att": {
                      "tpm": 4500,
                      "estimated_recipients": 4100,
                      "estimated_minutes": 1
                    },
                    "recipients": 11800,
                    "carrier_coverage": 0.93
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "description": "`CARRIER_ESTIMATE_TIMEOUT`: the estimate query timed out and had no effect. Retry later.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}/survey-results": {
      "get": {
        "tags": [
          "Projects"
        ],
        "summary": "Get Survey Results",
        "description": "Topline results for a survey project: per-question option counts and percentages, the unmatched/open-ended \"other\" bucket, and overall completion/dropoff rates. The numbers match the survey results page in the dashboard.\n\nReturns `409` (`NOT_A_SURVEY`) when the project is not a survey.",
        "operationId": "getSurveyResults",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Topline survey results",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "data"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "total_recipients": {
                          "type": "integer"
                        },
                        "completion_rate": {
                          "type": "number",
                          "description": "Percentage of responding conversations that reached the final question."
                        },
                        "dropoff_rate": {
                          "type": "number"
                        },
                        "results": {
                          "type": "array",
                          "description": "One entry per question, ordered by sequence.",
                          "items": {
                            "type": "object",
                            "properties": {
                              "question_id": {
                                "type": "string"
                              },
                              "sequence": {
                                "type": "integer"
                              },
                              "question_text": {
                                "type": "string"
                              },
                              "question_type": {
                                "type": "string"
                              },
                              "total_responses": {
                                "type": "integer"
                              },
                              "option_results": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "value": {
                                      "type": "integer"
                                    },
                                    "label": {
                                      "type": "string"
                                    },
                                    "count": {
                                      "type": "integer"
                                    },
                                    "percentage": {
                                      "type": "integer"
                                    }
                                  }
                                }
                              },
                              "other_count": {
                                "type": "integer",
                                "description": "Replies that matched no option (or all replies, for open-ended questions)."
                              },
                              "other_percentage": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "01HX000000000000000000P002",
                    "name": "Primary Intent Survey",
                    "status": "completed",
                    "total_recipients": 5000,
                    "completion_rate": 42.1,
                    "dropoff_rate": 57.9,
                    "results": [
                      {
                        "question_id": "01HX000000000000000000Q002",
                        "sequence": 2,
                        "question_text": "Do you plan to vote in the primary? Reply 1 for Yes, 2 for No, 3 for Undecided.",
                        "question_type": "multiple_choice",
                        "total_responses": 812,
                        "option_results": [
                          {
                            "value": 1,
                            "label": "Yes",
                            "count": 500,
                            "percentage": 62
                          },
                          {
                            "value": 2,
                            "label": "No",
                            "count": 150,
                            "percentage": 18
                          },
                          {
                            "value": 3,
                            "label": "Undecided",
                            "count": 100,
                            "percentage": 12
                          }
                        ],
                        "other_count": 62,
                        "other_percentage": 8
                      },
                      {
                        "question_id": "01HX000000000000000000Q003",
                        "sequence": 3,
                        "question_text": "What issue matters most to you this year?",
                        "question_type": "open_ended",
                        "total_responses": 145,
                        "option_results": [],
                        "other_count": 145,
                        "other_percentage": 100
                      }
                    ]
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Project exists but is not a survey",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "success": false,
                  "error": "Project is not a survey",
                  "code": "NOT_A_SURVEY",
                  "statusCode": 409
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/test": {
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Test Project",
        "description": "Send test message(s). Pass `test_contacts` as an array of 1-50 objects. By default, merge fields in the message template are rendered from a sample contact in the project's contact list, not from the request; for a survey, every question renders from that same contact. With no list, tags resolve as they would for a recipient with an empty field (inline fallback, then the organization default, then the standard platform default). Any contact in `test_contacts` can instead supply `merge_values`: when present, no contact is sampled from the project's lists for that entry, every merge tag renders from the supplied values, and a tag not supplied (or supplied empty) falls back the same way an empty field would. This also drives survey follow-up question rendering, and works whether or not the project has a contact list attached.",
        "operationId": "testProject",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "test_contacts"
                ],
                "properties": {
                  "test_contacts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "object",
                      "required": [
                        "phone"
                      ],
                      "properties": {
                        "phone": {
                          "type": "string",
                          "pattern": "^\\+1[2-9]\\d{9}$",
                          "description": "US/Canada number in E.164 format, e.g. +15555550100."
                        },
                        "merge_values": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string",
                            "maxLength": 1000
                          },
                          "maxProperties": 50,
                          "propertyNames": {
                            "pattern": "^[a-zA-Z0-9_]{1,64}$"
                          },
                          "description": "Optional, keyed by merge-tag name (case-sensitive). When present, this contact is not sampled from the project's lists: every merge tag renders from these values instead, and a tag left out (or given an empty value) falls back the same way an empty field would for a real recipient (inline fallback, then the organization default, then the standard platform default). The link-tracking merge field also resolves from these values; a supplied `tracking_url` is ignored since it is a system placeholder. For a survey project, every follow-up question renders from these same values."
                        }
                      }
                    }
                  }
                }
              },
              "example": {
                "test_contacts": [
                  {
                    "phone": "+15555550100"
                  },
                  {
                    "phone": "+12074323729",
                    "merge_values": {
                      "first_name": "hunter",
                      "voter_id": "123456"
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Test messages accepted and queued for delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "test_messages": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "test_message_id": {
                                "type": "string"
                              },
                              "sent_to": {
                                "type": "string",
                                "description": "Masked recipient number, e.g. +1555****100."
                              }
                            },
                            "additionalProperties": true
                          }
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "status": "awaiting_test",
                    "test_messages": [
                      {
                        "test_message_id": "01J1F8B2ZC4D5E6F7G8H9J0K1M",
                        "sent_to": "+1555****100"
                      }
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/projects/{id}/schedule": {
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Schedule Project",
        "description": "Transition a project to `scheduled` status. The project must be in `ready` or `paused` status; scheduling a `paused` project resumes it. When a project pauses with `pause_reason` `brand_daily_cap` (the brand's T-Mobile daily cap; it resets at midnight Pacific), do not resume at midnight. Schedule it for the next day during sending hours (the project's sending window: 8 AM to 10 PM in the time zone of most recipients, or 8 AM Eastern to 10 PM Pacific when no zone holds a majority) with a morning `scheduled_at`, or resume now with `daily_cap_bypass: true` (T-Mobile messages over the cap may fail and are still billed). At the cap, recipients on known other carriers keep sending; T-Mobile recipients and recipients with an unknown carrier wait, and the project pauses when only those remain. Running list analysis first means only T-Mobile recipients wait. See [Carrier throughput limits](/api-reference/best-practices#carrier-throughput-limits). A project cannot be created, updated with, or scheduled on a list whose analysis is in progress; the request fails with `409` `LIST_ANALYSIS_IN_PROGRESS`.",
        "operationId": "scheduleProject",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scheduled_at",
                  "scheduled_timezone"
                ],
                "properties": {
                  "scheduled_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "ISO 8601 date-time including a timezone offset (e.g. 2025-07-10T09:00:00-04:00). May be now or in the past - the project starts sending as soon as audience compilation finishes (no minimum lead time)."
                  },
                  "scheduled_timezone": {
                    "type": "string",
                    "description": "US time zone the scheduled time is displayed in. Must be one of the six supported IANA zones.",
                    "enum": [
                      "America/New_York",
                      "America/Chicago",
                      "America/Denver",
                      "America/Los_Angeles",
                      "America/Anchorage",
                      "Pacific/Honolulu"
                    ],
                    "example": "America/New_York"
                  },
                  "daily_cap_bypass": {
                    "type": "boolean",
                    "default": false,
                    "description": "Run the whole project past the brand daily carrier limit instead of pausing at it. Only applies to brands that have a T-Mobile daily limit on file (political brands do not today); such a project otherwise pauses at the cap each Pacific day and must be started again to continue. Ignored for brands with no cap. Setting this accepts that messages to T-Mobile recipients over the limit may fail and are still billed: carrier is not reliably known before sending, so only those recipients cannot be skipped. Holds until the next schedule or resume call, which rewrites it, so send it on every schedule or resume call you want it to apply to."
                  }
                }
              },
              "example": {
                "scheduled_at": "2026-05-01T13:00:00-04:00",
                "scheduled_timezone": "America/New_York",
                "daily_cap_bypass": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Project scheduled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "scheduled_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "scheduled_timezone": {
                          "type": "string"
                        },
                        "daily_cap_bypass": {
                          "type": "boolean"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "status": "scheduled",
                    "scheduled_at": "2025-07-10T09:00:00-04:00",
                    "scheduled_timezone": "America/New_York",
                    "daily_cap_bypass": false
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "409": {
            "description": "Project is not in a schedulable state (must be `ready` or `paused`; error code INVALID_STATE_TRANSITION), sending is paused for the organization or platform-wide (error code SENDING_PAUSED), or a contact list on the project is still being analyzed (error code LIST_ANALYSIS_IN_PROGRESS; nothing is changed, retry as-is once `analysis.status` is `complete`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "listAnalysisInProgress": {
                    "summary": "A contact list on the project is still being analyzed",
                    "value": {
                      "error": "Contact list \"October donors\" is still being analyzed. Wait for analysis to complete, then try again.",
                      "code": "LIST_ANALYSIS_IN_PROGRESS",
                      "details": {
                        "lists": [
                          {
                            "id": "0195f3a2-7c1e-7d4b-9a3e-5b6c7d8e9f01",
                            "name": "October donors",
                            "status": "processing"
                          }
                        ]
                      }
                    }
                  },
                  "organizationPaused": {
                    "summary": "Sending is paused for this organization",
                    "value": {
                      "error": "Sending is currently paused for this organization",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "organization"
                      }
                    }
                  },
                  "platformPaused": {
                    "summary": "Sending is paused platform-wide",
                    "value": {
                      "error": "Sending is currently paused",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "platform"
                      }
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "Schedule rejected: the wallet balance cannot cover the send (INSUFFICIENT_BALANCE).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}/unschedule": {
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Unschedule Project",
        "description": "Transition a scheduled project back to `ready`.",
        "operationId": "unscheduleProject",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "200": {
            "description": "Project unscheduled and returned to ready.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "unscheduled_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "8a3f5c7e-2d91-4b64-ae08-6f1c9d3e7b52",
                    "status": "ready",
                    "unscheduled_at": "2025-07-08T16:45:00.000Z"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "409": {
            "description": "Project is not currently scheduled. Error code INVALID_STATE_TRANSITION.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/projects/{id}/copy": {
      "post": {
        "tags": [
          "Projects"
        ],
        "summary": "Copy Project",
        "description": "Duplicate a project. The copy carries the message content, media, phone numbers, link tracking settings, and survey questions, and gets a versioned name (`Fall GOTV` becomes `Fall GOTV_v2`). Contact lists, suppression lists, the schedule, and all delivery stats are not carried over: the copy starts as a `draft`. Attach contact lists via `PATCH /projects/{id}`, then test and schedule as usual.",
        "operationId": "copyProject",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object"
              },
              "example": {}
            }
          }
        },
        "responses": {
          "201": {
            "description": "Project copied. The response describes the new project, including a `completeness` block showing what still needs to be attached before it can be tested.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "project_id": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string"
                        },
                        "status": {
                          "type": "string"
                        },
                        "channel": {
                          "type": "string"
                        },
                        "created_via_api": {
                          "type": "boolean"
                        },
                        "estimated_cost_cents": {
                          "type": "integer"
                        },
                        "total_recipients": {
                          "type": "integer"
                        },
                        "completeness": {
                          "type": "object",
                          "properties": {
                            "has_list": {
                              "type": "boolean"
                            },
                            "has_message": {
                              "type": "boolean"
                            },
                            "has_phone_number": {
                              "type": "boolean"
                            },
                            "ready_to_test": {
                              "type": "boolean"
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "project_id": "6b1d8f3a-9c25-4e70-b148-2f5a7c3d9e61",
                    "name": "Fall GOTV_v2",
                    "type": "broadcast",
                    "status": "draft",
                    "channel": "10dlc",
                    "created_via_api": false,
                    "estimated_cost_cents": 0,
                    "total_recipients": 0,
                    "completeness": {
                      "has_list": false,
                      "has_message": true,
                      "has_phone_number": true,
                      "ready_to_test": false
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, or the requested resource belongs to an organization outside the key's hierarchy.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "onboardingIncomplete": {
                    "summary": "Organization has not completed required onboarding (business profile or funding)",
                    "value": {
                      "error": "Complete your account setup to continue",
                      "code": "ONBOARDING_INCOMPLETE",
                      "details": {
                        "missingSteps": [
                          "profile",
                          "funding"
                        ],
                        "onboardingUrl": "/onboarding"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/conversations": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List Conversations",
        "operationId": "listConversations",
        "description": "Conversations with at least one inbound message, across every organization the key can access, newest inbound first. Built for one job: recovering the inbound messages you missed while your `message.replied` webhook endpoint was down. Advance `updated_since` to the newest `last_inbound_at` you have processed and poll no more than once a minute; the webhook remains the real-time path.\n\nKeyset paginated: page until `next_cursor` is null. Seed and test conversations are excluded (pass `include_test=true` to include test threads). There is no `status` filter; every row carries `status`.",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Only conversations on this project."
          },
          {
            "name": "updated_since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only conversations whose `last_inbound_at` is at or after this instant. Defaults to 7 days ago. More than 90 days ago is rejected with `400`."
          },
          {
            "name": "include_test",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ],
              "default": "false"
            },
            "description": "Include test conversations."
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of conversations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Conversation"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/conversations/{conversation_id}": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "Get Conversation",
        "operationId": "getConversation",
        "description": "Fetch one conversation. Returns `404 CONVERSATION_NOT_FOUND` both when the id does not exist and when it belongs to an organization this key cannot access.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The conversation.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/Conversation"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/conversations/{conversation_id}/messages": {
      "get": {
        "tags": [
          "Conversations"
        ],
        "summary": "List Conversation Messages",
        "operationId": "listConversationMessages",
        "description": "The thread, newest first, both directions. Reading it has no side effects: it does not mark the thread read in the dashboard. Keyset paginated: page until `next_cursor` is null.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationIdPath"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of messages.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/ConversationMessage"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Conversations"
        ],
        "summary": "Reply to Conversation",
        "operationId": "replyToConversation",
        "description": "Send a text reply inside an existing conversation: same number, same contact, same project. Typical use: answer a `message.replied` webhook with your own content.\n\nThe reply is accepted (`202`) and queued at high priority; its final state arrives on the `message.sent`, `message.delivered`, or `message.failed` webhook carrying the returned `message_id`. Replies are SMS only (up to 1600 characters, billed per segment at the number's outbound rate) and are not subject to quiet hours. Every rejection happens before any charge.\n\nSend an `Idempotency-Key` on every call you might retry. A `503 SEND_ENQUEUE_FAILED` means nothing was sent and nothing was charged: retry the same call. On any other 5xx, read `GET /conversations/{conversation_id}/messages` and look for your text before retrying.",
        "parameters": [
          {
            "$ref": "#/components/parameters/ConversationIdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 1600,
                    "description": "The reply body. Unknown properties are rejected with `400`; media is not accepted."
                  }
                },
                "additionalProperties": false
              },
              "example": {
                "text": "Thanks for reaching out. Polls are open until 7pm at Lincoln Elementary."
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Reply accepted and queued for delivery.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/ConversationReplyResult"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "409": {
            "description": "The conversation cannot take a reply: `CONTACT_OPTED_OUT` (the contact replied STOP), `CONVERSATION_NOT_SENDABLE` (the thread has no sending number), `PROJECT_DELETED`, `PHONE_NUMBER_UNAVAILABLE` (the thread's number was released or is inactive), or `SENDING_PAUSED` (sending is paused for the organization or platform-wide). Nothing was charged for CONTACT_OPTED_OUT, CONVERSATION_NOT_SENDABLE, PROJECT_DELETED, PHONE_NUMBER_UNAVAILABLE, or SENDING_PAUSED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "conversationNotSendable": {
                    "summary": "The conversation cannot take a reply right now",
                    "value": {
                      "success": false,
                      "error": "Contact has opted out of this conversation",
                      "code": "CONTACT_OPTED_OUT",
                      "statusCode": 409
                    }
                  },
                  "organizationPaused": {
                    "summary": "Sending is paused for this organization",
                    "value": {
                      "error": "Sending is currently paused for this organization",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "organization"
                      }
                    }
                  },
                  "platformPaused": {
                    "summary": "Sending is paused platform-wide",
                    "value": {
                      "error": "Sending is currently paused",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "platform"
                      }
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/messages/stats": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get Message Stats",
        "description": "Message delivery statistics for a date range. The range must be 31 days or less and may not start more than 180 days in the past or end in the future.",
        "operationId": "getMessageStats",
        "parameters": [
          {
            "$ref": "#/components/parameters/StartDate"
          },
          {
            "$ref": "#/components/parameters/EndDate"
          },
          {
            "$ref": "#/components/parameters/OrganizationIdCamel"
          },
          {
            "$ref": "#/components/parameters/BrandIdCamel"
          },
          {
            "$ref": "#/components/parameters/CampaignIdCamel"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "totals": {
                          "type": "object",
                          "description": "Message counts and cost keyed by delivery status (unsent, queued, sending, sent, delivered, failed, received, on_hold). Only statuses present in the range appear.",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "count": {
                                "type": "integer"
                              },
                              "totalCost": {
                                "type": "number"
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "summary": {
                          "type": "object",
                          "properties": {
                            "totalMessages": {
                              "type": "integer"
                            },
                            "statusBreakdown": {
                              "type": "object",
                              "properties": {
                                "unsent": {
                                  "type": "integer"
                                },
                                "queued": {
                                  "type": "integer"
                                },
                                "sending": {
                                  "type": "integer"
                                },
                                "sent": {
                                  "type": "integer"
                                },
                                "delivered": {
                                  "type": "integer"
                                },
                                "failed": {
                                  "type": "integer"
                                },
                                "received": {
                                  "type": "integer"
                                },
                                "onHold": {
                                  "type": "integer"
                                }
                              },
                              "additionalProperties": true
                            },
                            "totalCost": {
                              "type": "number"
                            },
                            "projectCount": {
                              "type": "integer"
                            },
                            "organizationCount": {
                              "type": "integer"
                            }
                          },
                          "additionalProperties": true
                        },
                        "deliveryRates": {
                          "type": "object",
                          "properties": {
                            "totalMessages": {
                              "type": "integer"
                            },
                            "deliveredCount": {
                              "type": "integer"
                            },
                            "failedCount": {
                              "type": "integer"
                            },
                            "successfulCount": {
                              "type": "integer"
                            },
                            "deliveryRate": {
                              "type": "number",
                              "description": "Percentage 0-100."
                            },
                            "failureRate": {
                              "type": "number",
                              "description": "Percentage 0-100."
                            },
                            "successRate": {
                              "type": "number",
                              "description": "Percentage 0-100."
                            }
                          },
                          "additionalProperties": true
                        },
                        "byDay": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "date": {
                                "type": "string"
                              },
                              "status": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              },
                              "totalCost": {
                                "type": "number"
                              }
                            },
                            "additionalProperties": true
                          },
                          "description": "One row per date + status combination, newest date first."
                        },
                        "dateRange": {
                          "type": "object",
                          "properties": {
                            "startDate": {
                              "type": "string"
                            },
                            "endDate": {
                              "type": "string"
                            }
                          },
                          "additionalProperties": true
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "totals": {
                      "delivered": {
                        "count": 11342,
                        "totalCost": 340.26
                      },
                      "failed": {
                        "count": 458,
                        "totalCost": 13.74
                      },
                      "received": {
                        "count": 312,
                        "totalCost": 0
                      }
                    },
                    "summary": {
                      "totalMessages": 12112,
                      "statusBreakdown": {
                        "unsent": 0,
                        "queued": 0,
                        "sending": 0,
                        "sent": 0,
                        "delivered": 11342,
                        "failed": 458,
                        "received": 312,
                        "onHold": 0
                      },
                      "totalCost": 354.0,
                      "projectCount": 1,
                      "organizationCount": 1
                    },
                    "deliveryRates": {
                      "totalMessages": 12112,
                      "deliveredCount": 11342,
                      "failedCount": 458,
                      "successfulCount": 11800,
                      "deliveryRate": 93.64,
                      "failureRate": 3.78,
                      "successRate": 97.42
                    },
                    "byDay": [
                      {
                        "date": "2025-07-02T00:00:00.000Z",
                        "status": "delivered",
                        "count": 11342,
                        "totalCost": 340.26
                      },
                      {
                        "date": "2025-07-02T00:00:00.000Z",
                        "status": "failed",
                        "count": 458,
                        "totalCost": 13.74
                      }
                    ],
                    "dateRange": {
                      "startDate": "2025-07-01",
                      "endDate": "2025-07-31"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/ledger/usage": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get Usage",
        "description": "Ledger usage aggregated per organization for a date range. The range must be 31 days or less and may not start more than 180 days in the past or end in the future. Charge amounts are signed: usage charges are negative.",
        "operationId": "getLedgerUsage",
        "parameters": [
          {
            "$ref": "#/components/parameters/StartDate"
          },
          {
            "$ref": "#/components/parameters/EndDate"
          },
          {
            "$ref": "#/components/parameters/OrganizationIdCamel"
          },
          {
            "$ref": "#/components/parameters/BrandIdCamel"
          },
          {
            "$ref": "#/components/parameters/CampaignIdCamel"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "startDate": {
                          "type": "string"
                        },
                        "endDate": {
                          "type": "string"
                        },
                        "organizations": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "organizationId": {
                                "type": "string"
                              },
                              "organizationName": {
                                "type": "string"
                              },
                              "stripeCustomerId": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "accountRep": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "tier": {
                                "type": [
                                  "integer",
                                  "null"
                                ]
                              },
                              "usage": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "productCode": {
                                      "type": "string"
                                    },
                                    "eventType": {
                                      "type": "string",
                                      "enum": [
                                        "charge_success",
                                        "charge_failure",
                                        "admin_adjustment"
                                      ]
                                    },
                                    "quantity": {
                                      "type": "integer",
                                      "description": "Billable segments, not messages: an SMS over 160 characters bills as more than one segment, so a message count is always less than or equal to this."
                                    },
                                    "unitPrice": {
                                      "type": "number",
                                      "description": "Positive unit rate; direction lives on totalAmount."
                                    },
                                    "totalAmount": {
                                      "type": "number",
                                      "description": "Signed amount in dollars; charges are negative."
                                    },
                                    "transactionCount": {
                                      "type": "integer"
                                    }
                                  },
                                  "additionalProperties": true
                                }
                              },
                              "payments": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {},
                                  "additionalProperties": true
                                },
                                "description": "Always empty on this endpoint."
                              },
                              "adjustments": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {},
                                  "additionalProperties": true
                                },
                                "description": "Always empty on this endpoint."
                              },
                              "totalUsageAmount": {
                                "type": "number",
                                "description": "Signed sum of usage charges."
                              },
                              "totalPaymentAmount": {
                                "type": "number",
                                "description": "Always 0 on this endpoint."
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "totalUsageAmount": {
                          "type": "number"
                        },
                        "totalPaymentAmount": {
                          "type": "number",
                          "description": "Always 0 on this endpoint."
                        },
                        "totalQuantity": {
                          "type": "integer",
                          "description": "Total billable segments across the response."
                        },
                        "serviceTotals": {
                          "type": "object",
                          "description": "Total billable segments per product code across all organizations.",
                          "additionalProperties": {
                            "type": "integer"
                          }
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "startDate": "2025-07-01",
                    "endDate": "2025-07-31",
                    "organizations": [
                      {
                        "organizationId": "0d9f6a2e-1b34-4c8e-9f21-3e7a8b5c4d10",
                        "organizationName": "Smith Campaign 2024",
                        "stripeCustomerId": "cus_QWERty123456",
                        "accountRep": null,
                        "tier": 1,
                        "usage": [
                          {
                            "productCode": "sms_outbound",
                            "eventType": "charge_success",
                            "quantity": 11800,
                            "unitPrice": 0.03,
                            "totalAmount": -354.0,
                            "transactionCount": 11800
                          }
                        ],
                        "payments": [],
                        "adjustments": [],
                        "totalUsageAmount": -354.0,
                        "totalPaymentAmount": 0
                      }
                    ],
                    "totalUsageAmount": -354.0,
                    "totalPaymentAmount": 0,
                    "totalQuantity": 11800,
                    "serviceTotals": {
                      "sms_outbound": 11800
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/ledger/usage/by-initiator": {
      "get": {
        "tags": [
          "Billing"
        ],
        "summary": "Get Usage by Initiator",
        "description": "Ledger usage grouped by the organization that initiated each charge, for a date range (max 31 days, no older than 180 days). Amounts are signed: charges are negative. Initiators are sorted by totalAmount descending.",
        "operationId": "getLedgerUsageByInitiator",
        "parameters": [
          {
            "$ref": "#/components/parameters/StartDate"
          },
          {
            "$ref": "#/components/parameters/EndDate"
          },
          {
            "$ref": "#/components/parameters/OrganizationIdCamel"
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "startDate": {
                          "type": "string"
                        },
                        "endDate": {
                          "type": "string"
                        },
                        "initiators": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "initiatingOrganizationId": {
                                "type": "string"
                              },
                              "initiatingOrganizationName": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "usage": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "date": {
                                      "type": "string"
                                    },
                                    "productCode": {
                                      "type": "string"
                                    },
                                    "eventType": {
                                      "type": "string"
                                    },
                                    "quantity": {
                                      "type": "integer",
                                      "description": "Billable segments, not messages: an SMS over 160 characters bills as more than one segment, so a message count is always less than or equal to this."
                                    },
                                    "totalAmount": {
                                      "type": "number",
                                      "description": "Signed amount in dollars; charges are negative."
                                    },
                                    "transactionCount": {
                                      "type": "integer"
                                    }
                                  },
                                  "additionalProperties": true
                                }
                              },
                              "totalAmount": {
                                "type": "number"
                              },
                              "totalQuantity": {
                                "type": "integer"
                              },
                              "transactionCount": {
                                "type": "integer"
                              }
                            },
                            "additionalProperties": true
                          }
                        },
                        "totalAmount": {
                          "type": "number"
                        },
                        "totalQuantity": {
                          "type": "integer",
                          "description": "Total billable segments across the response."
                        },
                        "totalTransactionCount": {
                          "type": "integer"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                },
                "example": {
                  "success": true,
                  "data": {
                    "startDate": "2025-07-01",
                    "endDate": "2025-07-31",
                    "initiators": [
                      {
                        "initiatingOrganizationId": "9e4c7b2a-5f18-4d3e-a6b9-1c8f4e7a2d55",
                        "initiatingOrganizationName": "Regional Office East",
                        "usage": [
                          {
                            "date": "2025-07-02T00:00:00.000Z",
                            "productCode": "sms_outbound",
                            "eventType": "charge_success",
                            "quantity": 4200,
                            "totalAmount": -126.0,
                            "transactionCount": 4200
                          }
                        ],
                        "totalAmount": -126.0,
                        "totalQuantity": 4200,
                        "transactionCount": 4200
                      }
                    ],
                    "totalAmount": -126.0,
                    "totalQuantity": 4200,
                    "totalTransactionCount": 4200
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/domains": {
      "get": {
        "tags": [
          "Sending Domains"
        ],
        "summary": "List Sending Domains",
        "description": "List the organization's email sending domains, newest first.",
        "operationId": "listEmailDomains",
        "parameters": [
          {
            "$ref": "#/components/parameters/EmailLimit"
          },
          {
            "$ref": "#/components/parameters/EmailCursor"
          },
          {
            "$ref": "#/components/parameters/EmailSearch"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of sending domains.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailDomain"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/domains/{id}": {
      "get": {
        "tags": [
          "Sending Domains"
        ],
        "summary": "Get Sending Domain",
        "description": "Fetch one sending domain, including the DNS records to publish and the per-signal verification state.",
        "operationId": "getEmailDomain",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The sending domain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailDomain"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/senders": {
      "get": {
        "tags": [
          "Email Senders"
        ],
        "summary": "List Sender Identities",
        "description": "List every sender identity in the organization. This endpoint is not paginated: `has_more` is always `false`.",
        "operationId": "listEmailSenders",
        "responses": {
          "200": {
            "description": "Sender identities.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailSenderIdentity"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/senders/{id}": {
      "get": {
        "tags": [
          "Email Senders"
        ],
        "summary": "Get Sender Identity",
        "description": "Fetch one sender identity.",
        "operationId": "getEmailSender",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The sender identity.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailSenderIdentity"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/lists": {
      "get": {
        "tags": [
          "Email Lists"
        ],
        "summary": "List Email Lists",
        "description": "List the organization's email lists.",
        "operationId": "listEmailLists",
        "parameters": [
          {
            "$ref": "#/components/parameters/EmailLimit"
          },
          {
            "$ref": "#/components/parameters/EmailCursor"
          },
          {
            "name": "source_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "uploaded",
                "segmented",
                "winred",
                "anedot"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/EmailSearch"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of email lists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailList"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Email Lists"
        ],
        "summary": "Create Email List",
        "description": "Create an email list. `consent_attestation` is required: you are recording how the people on this list agreed to hear from you.",
        "operationId": "createEmailList",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "email_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Scopes the list to one sending domain. Omit for an organization-wide list any campaign can use."
                  },
                  "acquired": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Set this when the list came from somewhere other than your own sign-up flow. Acquired lists ramp on half the normal warm-up steps."
                  },
                  "sunset_enabled": {
                    "type": "boolean"
                  },
                  "consent_attestation": {
                    "type": "object",
                    "properties": {
                      "source": {
                        "type": "string",
                        "maxLength": 255
                      },
                      "note": {
                        "type": "string",
                        "maxLength": 2000
                      }
                    },
                    "required": [
                      "source"
                    ],
                    "description": "How the people on this list consented to hear from you.",
                    "additionalProperties": true
                  }
                },
                "required": [
                  "name",
                  "consent_attestation"
                ],
                "additionalProperties": true
              },
              "example": {
                "name": "August donors",
                "consent_attestation": {
                  "source": "webform",
                  "note": "Donate page opt-in checkbox"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "List created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailList"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/lists/{id}": {
      "get": {
        "tags": [
          "Email Lists"
        ],
        "summary": "Get Email List",
        "description": "Fetch one email list with its contact counts.",
        "operationId": "getEmailList",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The email list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailList"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/lists/{id}/contacts": {
      "get": {
        "tags": [
          "Email Lists"
        ],
        "summary": "List Contacts",
        "description": "List the contacts on an email list.",
        "operationId": "listEmailListContacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/EmailLimit"
          },
          {
            "$ref": "#/components/parameters/EmailCursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "subscribed",
                "unsubscribed",
                "bounced",
                "complained",
                "invalid",
                "sunset"
              ]
            }
          },
          {
            "name": "search",
            "in": "query",
            "required": false,
            "description": "Case-insensitive substring match on the address.",
            "schema": {
              "type": "string",
              "maxLength": 320
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of contacts.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailListContact"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Email Lists"
        ],
        "summary": "Add Contacts",
        "description": "Upsert up to 1,000 contacts in one call. Invalid rows do not fail the request: every row comes back with its own outcome so you can fix just the rejected ones.",
        "operationId": "addEmailListContacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "contacts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 1000,
                    "items": {
                      "type": "object",
                      "properties": {
                        "email": {
                          "type": "string",
                          "maxLength": 320
                        },
                        "fields": {
                          "type": "object",
                          "properties": {},
                          "description": "Merge fields.",
                          "additionalProperties": true
                        },
                        "consent_source": {
                          "type": "string",
                          "maxLength": 255
                        },
                        "consent_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "email"
                      ],
                      "additionalProperties": true
                    }
                  }
                },
                "required": [
                  "contacts"
                ],
                "additionalProperties": true
              },
              "example": {
                "contacts": [
                  {
                    "email": "voter@example.com",
                    "fields": {
                      "first_name": "Ada"
                    },
                    "consent_source": "webform"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Per-row upsert results.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/BulkUpsertResult"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/lists/import": {
      "post": {
        "tags": [
          "Email Lists"
        ],
        "summary": "Import Contacts From A URL",
        "description": "Import contacts into a list from a CSV you host.\n\nThe file is fetched over HTTPS through the same SSRF-guarded fetcher `POST /contact-lists/import` uses: private and link-local addresses are refused and re-checked on every redirect, and the 50 MB cap is enforced while streaming rather than trusted from `Content-Length`. Fetching and staging happen before this call returns; the rows are written afterwards, which is why the status is `202`. Poll `GET /email/lists/imports/{id}`.\n\nRole addresses such as info@ and admin@ are always removed at import; there is no option to keep them. Addresses currently held out of sending by send-time screening are imported but held back, and counted as `screened` in the import summary.\n\n`mapping` is optional. Omit it and the platform uses the mapping it recognizes from the export's own headers, which is what a caller exporting from a common ESP wants. When neither your mapping nor the recognizer finds an email column the request returns `400 VALIDATION_ERROR` with `details.headers` listing the headers that were read, so the retry can name the right column instead of guessing.",
        "operationId": "importEmailListContacts",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "source_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "HTTPS URL of the CSV. Maximum 50 MB."
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Name for the list this file becomes. Defaults to the file name. The uploaded file IS the list: an import creates one rather than adding to an existing list."
                  },
                  "email_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Scopes the list to one sending domain. Omit for an organization-wide list any campaign can use."
                  },
                  "acquired": {
                    "type": "boolean",
                    "description": "The addresses were purchased or rented. Acquired lists are not blocked from sending, but they ramp on half the normal warm-up steps."
                  },
                  "mapping": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 64
                    },
                    "description": "CSV header to contact field, for example `{\"Email Address\": \"email\", \"First\": \"first_name\"}`. Exactly one header must map to `email`."
                  },
                  "consent": {
                    "type": "object",
                    "properties": {
                      "source": {
                        "type": "string",
                        "enum": [
                          "donation_form",
                          "petition",
                          "signup_form",
                          "event",
                          "purchased",
                          "rented",
                          "other"
                        ],
                        "description": "How these people agreed to hear from you. Answer honestly: `purchased` and `rented` change how the list is treated, and an undeclared acquired list is the usual cause of a mid-send deliverability pause."
                      },
                      "note": {
                        "type": "string",
                        "maxLength": 1000
                      }
                    },
                    "required": [
                      "source"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "source_url",
                  "consent"
                ],
                "additionalProperties": false
              },
              "example": {
                "source_url": "https://files.example.com/exports/august-donors.csv",
                "name": "August donors",
                "consent": {
                  "source": "donation_form",
                  "note": "Donate page opt-in checkbox"
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Import accepted. Poll the import for progress.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailListImport"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/suppressions": {
      "get": {
        "tags": [
          "Email Suppressions"
        ],
        "summary": "List Suppressions",
        "description": "List suppressed addresses.",
        "operationId": "listEmailSuppressions",
        "parameters": [
          {
            "$ref": "#/components/parameters/EmailLimit"
          },
          {
            "$ref": "#/components/parameters/EmailCursor"
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "org",
                "identity",
                "domain"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A page of suppressions.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailSuppression"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Email Suppressions"
        ],
        "summary": "Add Suppressions",
        "description": "Suppress up to 5,000 addresses in one call. Malformed addresses are reported in `invalid` rather than failing the batch.",
        "operationId": "addEmailSuppressions",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "org",
                      "identity",
                      "domain"
                    ]
                  },
                  "sender_identity_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Required when `scope` is `identity`."
                  },
                  "emails": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5000,
                    "items": {
                      "type": "string",
                      "maxLength": 320
                    }
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "email_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Required when `scope` is `domain`."
                  }
                },
                "required": [
                  "scope",
                  "emails"
                ],
                "additionalProperties": true
              },
              "example": {
                "scope": "org",
                "emails": [
                  "voter@example.com"
                ],
                "reason": "CRM sync"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Addresses suppressed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "added": {
                          "type": "integer"
                        },
                        "submitted": {
                          "type": "integer"
                        },
                        "invalid": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      },
      "delete": {
        "tags": [
          "Email Suppressions"
        ],
        "summary": "Remove Suppressions",
        "description": "Lift suppressions on up to 5,000 addresses. Removing an address that was not suppressed is not an error.",
        "operationId": "removeEmailSuppressions",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "org",
                      "identity",
                      "domain"
                    ]
                  },
                  "sender_identity_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Required when `scope` is `identity`."
                  },
                  "emails": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5000,
                    "items": {
                      "type": "string",
                      "maxLength": 320
                    }
                  },
                  "email_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "Required when `scope` is `domain`."
                  }
                },
                "required": [
                  "scope",
                  "emails"
                ],
                "additionalProperties": true
              },
              "example": {
                "scope": "org",
                "emails": [
                  "voter@example.com"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Suppressions lifted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "removed": {
                          "type": "integer"
                        },
                        "submitted": {
                          "type": "integer"
                        }
                      },
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/campaigns": {
      "get": {
        "tags": [
          "Email Campaigns"
        ],
        "summary": "List Campaigns",
        "description": "List email campaigns.",
        "operationId": "listEmailCampaigns",
        "parameters": [
          {
            "$ref": "#/components/parameters/EmailLimit"
          },
          {
            "$ref": "#/components/parameters/EmailCursor"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "draft",
                "awaiting_test",
                "awaiting_approval",
                "ready",
                "scheduled",
                "compiling",
                "sending",
                "paused",
                "completed",
                "deleted"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/EmailSearch"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of campaigns.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailCampaign"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Email Campaigns"
        ],
        "summary": "Create Campaign",
        "description": "Create a draft campaign.",
        "operationId": "createEmailCampaign",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "sender_identity_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "list_ids": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "suppression_list_ids": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  },
                  "template_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "subject": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "preheader": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "html": {
                    "type": "string",
                    "maxLength": 1000000
                  },
                  "source_code": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "refcode": {
                    "type": "string",
                    "maxLength": 255
                  },
                  "append_utm": {
                    "type": "boolean",
                    "description": "Append UTM parameters to outbound links for attribution."
                  },
                  "is_repermission": {
                    "type": "boolean",
                    "description": "Mark the send as a re-permission (re-consent) message."
                  },
                  "tracking_domain_id": {
                    "type": "string",
                    "format": "uuid",
                    "nullable": true,
                    "description": "Tracking domain serving this campaign's tracked links, open pixel, unsubscribe page and browser view, so recipients see your own `links.` host instead of the platform one. Must be an active tracking domain your organization owns or inherits from a parent. Omit to let the platform pick the obvious default (the tracking domain matching your sending domain's root, or your only one); send `null` to force the platform link host."
                  },
                  "require_approval": {
                    "type": "boolean",
                    "description": "Require an approval after the test send before this campaign can schedule or send. Off by default; every campaign still requires a successful test send regardless of this setting."
                  }
                },
                "required": [
                  "name",
                  "sender_identity_id",
                  "list_ids"
                ],
                "additionalProperties": true
              },
              "example": {
                "name": "August appeal",
                "sender_identity_id": "3f7a9c1e-8b24-4d6f-a2e5-9c7b3f1a8d42",
                "list_ids": [
                  "7c1de5f0-9a42-4b6d-8e33-2f5a9c7b1e64"
                ],
                "subject": "Can you help before Friday?",
                "append_utm": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Campaign created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, the organization has not completed required onboarding, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "onboardingIncomplete": {
                    "summary": "Organization has not completed required onboarding (business profile or funding)",
                    "value": {
                      "error": "Complete your account setup to continue",
                      "code": "ONBOARDING_INCOMPLETE",
                      "details": {
                        "missingSteps": [
                          "profile",
                          "funding"
                        ],
                        "onboardingUrl": "/onboarding"
                      }
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/campaigns/{id}": {
      "get": {
        "tags": [
          "Email Campaigns"
        ],
        "summary": "Get Campaign",
        "description": "Fetch one campaign. This read additionally returns `blocked`: the machine-readable list of reasons the campaign will not schedule yet. Check it before calling schedule.",
        "operationId": "getEmailCampaign",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/campaigns/{id}/schedule": {
      "post": {
        "tags": [
          "Email Campaigns"
        ],
        "summary": "Schedule Campaign",
        "description": "Schedule the campaign, or omit `scheduled_at` to send now. A date in the past returns `400 VALIDATION_ERROR`. Scheduling runs the full checklist; if it refuses, read `blocked` on the campaign to see why.",
        "operationId": "scheduleEmailCampaign",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scheduled_at": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Omit to send immediately."
                  }
                },
                "additionalProperties": true
              },
              "example": {
                "scheduled_at": "2026-09-05T15:00:00Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Campaign scheduled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "409": {
            "description": "Sending is paused for the organization or platform-wide. Error code SENDING_PAUSED.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "organizationPaused": {
                    "summary": "Sending is paused for this organization",
                    "value": {
                      "error": "Sending is currently paused for this organization",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "organization"
                      }
                    }
                  },
                  "platformPaused": {
                    "summary": "Sending is paused platform-wide",
                    "value": {
                      "error": "Sending is currently paused",
                      "code": "SENDING_PAUSED",
                      "details": {
                        "scope": "platform"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/campaigns/{id}/unschedule": {
      "post": {
        "tags": [
          "Email Campaigns"
        ],
        "summary": "Unschedule Campaign",
        "description": "Return a scheduled campaign to a draft state.",
        "operationId": "unscheduleEmailCampaign",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign unscheduled.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailCampaign"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/campaigns/{id}/stats": {
      "get": {
        "tags": [
          "Email Campaigns"
        ],
        "summary": "Get Campaign Stats",
        "description": "Report tiles, fundraising totals, and per-link click stats for a campaign. Cached for 60 seconds. Test and seed sends are excluded from every figure.",
        "operationId": "getEmailCampaignStats",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign stats.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailCampaignStats"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/email/templates": {
      "get": {
        "tags": [
          "Email Templates"
        ],
        "summary": "List Email Templates",
        "description": "List the organization's email templates, newest first.",
        "operationId": "listEmailTemplates",
        "parameters": [
          {
            "$ref": "#/components/parameters/EmailLimit"
          },
          {
            "$ref": "#/components/parameters/EmailCursor"
          },
          {
            "$ref": "#/components/parameters/EmailSearch"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of templates.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/EmailTemplate"
                          }
                        },
                        "has_more": {
                          "type": "boolean"
                        },
                        "next_cursor": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      },
                      "required": [
                        "data",
                        "has_more",
                        "next_cursor"
                      ],
                      "additionalProperties": true
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "tags": [
          "Email Templates"
        ],
        "summary": "Create Email Template",
        "description": "Create a template from raw HTML. The response carries a `lint` object beside the template: a template with lint errors saves, but a campaign using it will not schedule.",
        "operationId": "createEmailTemplate",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 255
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 2000
                  },
                  "content": {
                    "type": "object",
                    "properties": {
                      "subject": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 900
                      },
                      "preheader": {
                        "type": "string",
                        "maxLength": 255
                      },
                      "html": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2097152
                      },
                      "text": {
                        "type": "string",
                        "description": "Plain-text alternative. Generated from the HTML when omitted.",
                        "maxLength": 512000
                      }
                    },
                    "required": [
                      "subject",
                      "html"
                    ],
                    "description": "The full body. On update this object replaces the stored content rather than merging into it.",
                    "additionalProperties": false
                  }
                },
                "required": [
                  "name",
                  "content"
                ],
                "additionalProperties": false
              },
              "example": {
                "name": "Fall fundraising",
                "description": "Evergreen ask, brand colors",
                "content": {
                  "subject": "Can you chip in before Friday?",
                  "preheader": "Every gift is matched through midnight.",
                  "html": "<html><body><h1>Hi {first_name|Friend}</h1><p>Chip in today.</p></body></html>"
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Template created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/EmailTemplate"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "lint": {
                              "$ref": "#/components/schemas/EmailTemplateLint"
                            }
                          }
                        }
                      ]
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The API key lacks the required permission scope, the requested resource belongs to an organization outside the key's hierarchy, or the organization is not entitled to this feature.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/AuthErrorResponse"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                },
                "examples": {
                  "missingScope": {
                    "summary": "Key lacks the required permission scope",
                    "value": {
                      "error": "Insufficient permissions",
                      "message": "This API key does not have the required permissions: public:read"
                    }
                  },
                  "orgAccessDenied": {
                    "summary": "Resource outside the key's organization hierarchy",
                    "value": {
                      "error": "Access denied: You do not have permission to access this organization's data",
                      "code": "ORG_ACCESS_DENIED",
                      "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                    }
                  },
                  "entitlementRequired": {
                    "summary": "Organization is not entitled to this feature",
                    "value": {
                      "error": "This feature requires enablement by Political Comms",
                      "code": "ENTITLEMENT_REQUIRED",
                      "details": {
                        "entitlement": "email"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/email/templates/{id}": {
      "get": {
        "tags": [
          "Email Templates"
        ],
        "summary": "Get Email Template",
        "description": "Get one template, including its HTML.",
        "operationId": "getEmailTemplate",
        "parameters": [
          {
            "$ref": "#/components/parameters/IdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "The template.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": {
                      "type": "boolean"
                    },
                    "data": {
                      "$ref": "#/components/schemas/EmailTemplate"
                    }
                  },
                  "required": [
                    "success",
                    "data"
                  ],
                  "additionalProperties": true
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key",
        "description": "Authenticate every request by passing your API key in the X-API-Key header. Keys are scoped to your organization hierarchy."
      }
    },
    "parameters": {
      "IdPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "Resource ID",
        "schema": {
          "type": "string"
        }
      },
      "OrganizationId": {
        "name": "organization_id",
        "in": "query",
        "required": false,
        "description": "Filter to a specific descendant organization",
        "schema": {
          "type": "string"
        }
      },
      "BrandId": {
        "name": "brand_id",
        "in": "query",
        "required": false,
        "description": "Filter to a specific brand",
        "schema": {
          "type": "string"
        }
      },
      "CampaignId": {
        "name": "campaign_id",
        "in": "query",
        "required": false,
        "description": "Filter to a specific campaign",
        "schema": {
          "type": "string"
        }
      },
      "OrganizationIdCamel": {
        "name": "organizationId",
        "in": "query",
        "required": false,
        "description": "Filter to a specific descendant organization",
        "schema": {
          "type": "string"
        }
      },
      "BrandIdCamel": {
        "name": "brandId",
        "in": "query",
        "required": false,
        "description": "Filter to a specific brand",
        "schema": {
          "type": "string"
        }
      },
      "CampaignIdCamel": {
        "name": "campaignId",
        "in": "query",
        "required": false,
        "description": "Filter to a specific campaign",
        "schema": {
          "type": "string"
        }
      },
      "StartDate": {
        "name": "startDate",
        "in": "query",
        "required": true,
        "description": "Start date in YYYY-MM-DD format",
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2026-01-01"
        }
      },
      "EndDate": {
        "name": "endDate",
        "in": "query",
        "required": true,
        "description": "End date in YYYY-MM-DD format",
        "schema": {
          "type": "string",
          "format": "date",
          "example": "2026-01-31"
        }
      },
      "IdempotencyKeyHeader": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "schema": {
          "type": "string",
          "minLength": 16,
          "maxLength": 200
        },
        "description": "Optional idempotency key: a unique string of 16-200 printable ASCII characters (a UUID is recommended). Retrying the write with the same key within 24 hours returns the stored response of the first call with an `X-Idempotent-Replayed: true` response header instead of executing it again. Reusing a key with a different request body returns `422` (`IDEMPOTENCY_MISMATCH`); a duplicate sent while the first call is still running returns `409` with a `Retry-After` header. Keys are scoped per endpoint and organization."
      },
      "EmailLimit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Rows per page, 1-200.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "EmailCursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque cursor from the previous page's `next_cursor`.",
        "schema": {
          "type": "string",
          "maxLength": 4096
        }
      },
      "EmailSearch": {
        "name": "search",
        "in": "query",
        "required": false,
        "description": "Case-insensitive substring match.",
        "schema": {
          "type": "string",
          "maxLength": 255
        }
      },
      "ConversationIdPath": {
        "name": "conversation_id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Conversation ID, as delivered on the `message.replied` webhook (`conversation_id`) or returned by `GET /conversations`."
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Rows per page, 1-200.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque cursor from the previous page's `next_cursor`. Never parse or construct one; a cursor from one list is rejected by another.",
        "schema": {
          "type": "string",
          "maxLength": 4096
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed, revoked, or expired API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/AuthErrorResponse"
            },
            "example": {
              "error": "API key required",
              "message": "Please provide an API key in the X-API-Key header"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request (validation failure, malformed parameters, or a request the resource state does not allow).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Validation failed",
              "code": "VALIDATION_ERROR",
              "correlationId": "030bf857-922f-4b82-8fca-c5b0769c1590",
              "details": [
                {
                  "field": "organization_id",
                  "message": "Invalid UUID",
                  "code": "invalid_format"
                }
              ]
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found or not visible to this API key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Project not found",
              "code": "PROJECT_NOT_FOUND",
              "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded. Check the Retry-After header.",
        "headers": {
          "Retry-After": {
            "description": "Seconds until the window resets.",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/RateLimitErrorResponse"
            },
            "example": {
              "success": false,
              "error": "Rate limit exceeded",
              "message": "Maximum 600 requests per 60 seconds",
              "code": "RATE_LIMIT_EXCEEDED",
              "statusCode": 429,
              "retryAfter": 3
            }
          }
        }
      },
      "Forbidden": {
        "description": "The API key lacks the required permission scope, or the requested resource belongs to an organization outside the key's hierarchy.",
        "content": {
          "application/json": {
            "schema": {
              "oneOf": [
                {
                  "$ref": "#/components/schemas/AuthErrorResponse"
                },
                {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              ]
            },
            "examples": {
              "missingScope": {
                "summary": "Key lacks the required permission scope",
                "value": {
                  "error": "Insufficient permissions",
                  "message": "This API key does not have the required permissions: public:read"
                }
              },
              "orgAccessDenied": {
                "summary": "Resource outside the key's organization hierarchy",
                "value": {
                  "error": "Access denied: You do not have permission to access this organization's data",
                  "code": "ORG_ACCESS_DENIED",
                  "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
                }
              }
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "An unexpected error occurred. Please try again later.",
              "code": "INTERNAL_ERROR",
              "correlationId": "de038205-7c9c-4d4a-99bc-d7275a52f07e"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "Wallet balance cannot cover the operation (`INSUFFICIENT_BALANCE`). Add funds and retry.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "The request could not be completed right now and had no effect. For `SEND_ENQUEUE_FAILED` nothing was sent and nothing was charged: retry the same call.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    },
    "schemas": {
      "ErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable error message."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable error code, e.g. VALIDATION_ERROR, ORG_ACCESS_DENIED, PROJECT_NOT_FOUND."
          },
          "correlationId": {
            "type": "string",
            "description": "Request correlation ID; include it in support requests."
          },
          "details": {
            "type": [
              "array",
              "object"
            ],
            "description": "Present on some errors. For VALIDATION_ERROR, an array of { field, message, code } with one entry per rejected field (field is the dotted path); an object for other codes (e.g. insufficient-balance shortfall)."
          }
        },
        "required": [
          "error",
          "code"
        ],
        "additionalProperties": true,
        "description": "Standard error body produced by the API error handler for 4xx/5xx responses."
      },
      "HierarchyOrganization": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "parentId": {
            "type": [
              "string",
              "null"
            ],
            "description": "null for the root organization of the returned tree."
          },
          "status": {
            "type": "string"
          },
          "contactEmail": {
            "type": [
              "string",
              "null"
            ]
          },
          "contactFirstName": {
            "type": [
              "string",
              "null"
            ]
          },
          "contactLastName": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "brands": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "organizationId": {
                  "type": "string"
                },
                "createdAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "updatedAt": {
                  "type": "string",
                  "format": "date-time"
                },
                "campaigns": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "status": {
                        "type": "string"
                      },
                      "createdAt": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "updatedAt": {
                        "type": "string",
                        "format": "date-time"
                      }
                    },
                    "additionalProperties": true
                  }
                }
              },
              "additionalProperties": true
            }
          },
          "childOrganizations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HierarchyOrganization"
            },
            "description": "Child organization nodes, recursively."
          }
        },
        "additionalProperties": true,
        "description": "Organization node in the hierarchy tree (recursive)."
      },
      "AuthErrorResponse": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string",
            "description": "Short error summary."
          },
          "message": {
            "type": "string",
            "description": "Human-readable detail."
          }
        },
        "required": [
          "error",
          "message"
        ],
        "additionalProperties": true,
        "description": "Error body returned by the API-key authentication layer (401 and permission 403s)."
      },
      "RateLimitErrorResponse": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "code": {
            "type": "string",
            "enum": [
              "RATE_LIMIT_EXCEEDED"
            ]
          },
          "statusCode": {
            "type": "integer",
            "enum": [
              429
            ]
          },
          "retryAfter": {
            "type": "integer",
            "description": "Seconds until the rate-limit window resets. Also sent as the Retry-After header."
          }
        },
        "required": [
          "success",
          "error",
          "code",
          "statusCode",
          "retryAfter"
        ],
        "additionalProperties": true
      },
      "DnsRecord": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "CNAME",
              "TXT",
              "MX"
            ]
          },
          "name": {
            "type": "string",
            "description": "Host name to create, as published in your zone."
          },
          "value": {
            "type": "string",
            "description": "Record value."
          },
          "purpose": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the record proves: `dkim`, `mail_from`, or `dmarc`."
          }
        },
        "required": [
          "type",
          "name",
          "value"
        ],
        "description": "One DNS record to publish. We never write DNS on your behalf: you publish these in your own zone.",
        "additionalProperties": true
      },
      "EmailDomain": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "domain": {
            "type": "string",
            "example": "mail.example.org"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "failed"
            ],
            "description": "`pending` until every record is observed, then `active`. `failed` means verification was abandoned; delete and re-add to retry."
          },
          "dns_records": {
            "description": "DNS records to publish. Besides the DKIM, bounce, and DMARC records, a new domain carries a per-organization domain verification TXT record (`key` `ownership_txt`, host `_sender-verification.<domain>`). Publish it even if the other records already show verified; the domain stays pending until it is seen. Domains added before this record existed, and the platform organization's own domain, do not have one.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DnsRecord"
            }
          },
          "verification": {
            "type": "object",
            "properties": {
              "dkim": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "mail_from": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "dmarc": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "description": "Per-signal verification state.",
            "additionalProperties": true
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "domain",
          "status"
        ],
        "additionalProperties": true
      },
      "EmailSenderIdentity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email_domain_id": {
            "type": "string",
            "format": "uuid"
          },
          "from_address": {
            "type": "string",
            "example": "team@mail.example.org"
          },
          "from_local_part": {
            "type": "string",
            "example": "team"
          },
          "from_name": {
            "type": "string",
            "example": "Example Team"
          },
          "reply_to": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "active",
              "paused"
            ],
            "description": "An identity missing a required physical address or disclaimer is paused rather than deleted."
          },
          "physical_address": {
            "type": [
              "string",
              "null"
            ],
            "description": "Postal address included in the footer of every message."
          },
          "disclaimer": {
            "type": [
              "string",
              "null"
            ],
            "description": "Paid-for-by disclaimer text."
          },
          "disclaimer_required": {
            "type": "boolean"
          },
          "authorized_by_candidate": {
            "type": "boolean"
          },
          "gmail_verified_sender": {
            "type": [
              "object",
              "null"
            ],
            "description": "Read-only. Status of this address in Google's Gmail Verified Sender Program, submitted through Campaign Verify. Null when the identity has never been eligible.",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "not_eligible",
                  "eligible",
                  "ready_to_submit",
                  "submitted",
                  "verified",
                  "suspended",
                  "rejected",
                  "expired"
                ]
              },
              "submitted_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "verified_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "Set when status is verified, else null."
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "email_domain_id",
          "from_address",
          "status"
        ],
        "additionalProperties": true
      },
      "EmailListCounts": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "sendable": {
            "type": "integer",
            "description": "Addresses that would receive the next send from this list."
          },
          "bounced": {
            "type": "integer"
          },
          "complained": {
            "type": "integer"
          },
          "unsubscribed": {
            "type": "integer"
          },
          "invalid": {
            "type": "integer"
          },
          "suppressed_global": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "EmailList": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_type": {
            "type": "string",
            "enum": [
              "uploaded",
              "segmented",
              "winred",
              "anedot"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "ready",
              "failed",
              "archived"
            ]
          },
          "acquired": {
            "type": [
              "string",
              "null"
            ],
            "description": "Free-text provenance for a list you did not collect yourself. Set it: acquired lists ramp on half the normal warm-up steps."
          },
          "sunset_enabled": {
            "type": "boolean",
            "description": "Whether the unengaged-contact sunset policy applies to this list."
          },
          "counts": {
            "$ref": "#/components/schemas/EmailListCounts"
          },
          "last_send_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "email_domain_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "required": [
          "id",
          "name",
          "source_type",
          "status"
        ],
        "additionalProperties": true
      },
      "EmailListContact": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "subscribed",
              "unsubscribed",
              "bounced",
              "complained",
              "invalid",
              "sunset"
            ]
          },
          "status_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "fields": {
            "type": "object",
            "properties": {},
            "description": "Merge fields for this contact.",
            "additionalProperties": true
          },
          "consent_source": {
            "type": [
              "string",
              "null"
            ]
          },
          "consent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "added_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_engaged_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "email",
          "status"
        ],
        "additionalProperties": true
      },
      "BulkUpsertResult": {
        "type": "object",
        "properties": {
          "written": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "rejected": {
            "type": "integer"
          },
          "duplicates": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer",
                  "description": "Position of the row in the request array."
                },
                "email": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "accepted",
                    "rejected",
                    "duplicate"
                  ]
                },
                "reason": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "additionalProperties": true
            }
          }
        },
        "description": "Per-row outcome of a bulk contact upsert. Invalid rows are reported, not fatal: the call still returns 200 and writes the valid rows.",
        "additionalProperties": true
      },
      "EmailSuppression": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "org",
              "identity",
              "domain"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "sender_identity_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "suppressed_at": {
            "type": "string",
            "format": "date-time"
          },
          "email_domain_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        },
        "required": [
          "email",
          "scope"
        ],
        "additionalProperties": true
      },
      "EmailCampaignCounts": {
        "type": "object",
        "properties": {
          "queued": {
            "type": "integer"
          },
          "sent": {
            "type": "integer"
          },
          "delivered": {
            "type": "integer"
          },
          "bounced": {
            "type": "integer"
          },
          "complained": {
            "type": "integer"
          },
          "unsubscribed": {
            "type": "integer"
          },
          "opened": {
            "type": "integer"
          },
          "clicked": {
            "type": "integer"
          },
          "failed": {
            "type": "integer"
          }
        },
        "additionalProperties": true
      },
      "EmailCampaign": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "awaiting_test",
              "awaiting_approval",
              "ready",
              "scheduled",
              "compiling",
              "sending",
              "paused",
              "completed",
              "deleted"
            ]
          },
          "sender_identity_id": {
            "type": "string",
            "format": "uuid"
          },
          "list_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "suppression_list_ids": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "tracking_domain_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Tracking domain serving this campaign's `/e/*` URLs. `null` means the platform link host."
          },
          "tracking_domain": {
            "type": "object",
            "nullable": true,
            "description": "The resolved tracking domain, returned on the single-campaign read. Links are branded only while `status` is `active`; any other status falls back to the platform link host for the send.",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "domain": {
                "type": "string"
              },
              "status": {
                "type": "string"
              }
            }
          },
          "require_approval": {
            "type": "boolean",
            "description": "Require an approval after the test send before this campaign can schedule or send. Off by default; every campaign still requires a successful test send regardless of this setting."
          },
          "approval_status": {
            "type": "string",
            "enum": [
              "not_required",
              "pending",
              "approved"
            ],
            "description": "`not_required` when `require_approval` is off. `pending` once a current test send exists and approval is outstanding. `approved` once granted. Editing the campaign's content, sender, lists, suppression lists, or template resets a `pending` or `approved` status back to `pending` behind a new required test."
          },
          "last_tested_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the campaign's current test send completed. Null until a test send succeeds, and cleared whenever a later edit invalidates it."
          },
          "refcode": {
            "type": [
              "string",
              "null"
            ],
            "description": "Fundraising refcode appended to donation links for attribution. Generated as `e-` plus eight characters when omitted."
          },
          "source_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "scheduled_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ]
          },
          "audience_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Deduplicated, suppression-filtered recipient count."
          },
          "counts": {
            "$ref": "#/components/schemas/EmailCampaignCounts"
          },
          "pause_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the campaign is paused. A deliverability breaker sets this automatically."
          },
          "blocked": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              },
              "additionalProperties": true
            },
            "description": "Returned by the single-campaign read only: the machine-readable reasons this campaign will not schedule yet. Empty when it is ready."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "status",
          "sender_identity_id"
        ],
        "additionalProperties": true
      },
      "EmailCampaignStats": {
        "type": "object",
        "properties": {
          "campaign_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string"
          },
          "tiles": {
            "type": "object",
            "properties": {},
            "description": "Headline report tiles: sent, delivered, bounced, openRate, clickRate, actualCost.",
            "additionalProperties": true
          },
          "links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "url": {
                  "type": "string"
                },
                "label": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "is_donation_link": {
                  "type": "boolean"
                },
                "clicks": {
                  "type": "integer"
                },
                "unique_clicks": {
                  "type": "integer"
                }
              },
              "additionalProperties": true
            }
          },
          "excludes_test_and_seed": {
            "type": "boolean",
            "description": "Always true: test sends and seed addresses are excluded from every figure here. Note that they are still billed."
          }
        },
        "description": "Cached for 60 seconds.",
        "additionalProperties": true
      },
      "CursorPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {}
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Opaque cursor for the next page, or null on the last page. Do not parse or construct cursors; a cursor from one ordering is rejected by another."
          }
        },
        "required": [
          "data",
          "has_more",
          "next_cursor"
        ],
        "description": "Keyset pagination envelope used by every email list endpoint.",
        "additionalProperties": true
      },
      "EmailTemplateContent": {
        "type": "object",
        "properties": {
          "subject": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 900
          },
          "preheader": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 255
          },
          "html": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2097152
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "description": "Plain-text alternative. Generated from the HTML when omitted.",
            "maxLength": 512000
          },
          "editor": {
            "type": "string",
            "enum": [
              "html",
              "document"
            ],
            "description": "`html` for a template imported or hand-written as HTML; `document` for one built in the dashboard's document editor. Both are returned as rendered HTML here. This surface only accepts HTML content on create. Updating the `content` of a `document` template returns `409 CONFLICT` with `details.reason` set to `TEMPLATE_IS_DOCUMENT`: edit it in the dashboard, or convert it to an HTML template first."
          }
        },
        "description": "The renderable body of a template.",
        "additionalProperties": true
      },
      "EmailTemplateLint": {
        "type": "object",
        "properties": {
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        },
        "description": "Content lint result returned alongside every template write. A template with lint errors saves, but a campaign using it will not schedule, so read this on the write rather than discovering it at schedule time. Warnings do not block.",
        "additionalProperties": true
      },
      "EmailTemplate": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 255
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          },
          "content": {
            "$ref": "#/components/schemas/EmailTemplateContent"
          },
          "thumbnail_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Preview image rendered by a background job. Null until it has run."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "name",
          "content"
        ],
        "description": "A reusable email design in the organization's template library.",
        "additionalProperties": true
      },
      "EmailListImport": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "description": "Import lifecycle state. Terminal states are `completed` and `failed`."
          },
          "email_list_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "file_name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Derived from the last path segment of `source_url`."
          },
          "file_size": {
            "type": "integer",
            "description": "Bytes fetched."
          },
          "headers": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The CSV's header row as read. Echoed back so a caller that omitted `mapping` can see what the recognizer worked from."
          },
          "mapping": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "CSV header to contact field. The mapping actually used, whether you sent it or the recognizer produced it."
          },
          "recognized_provider": {
            "type": [
              "string",
              "null"
            ],
            "description": "The ESP whose export format was recognized, when one was."
          },
          "summary": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "screened": {
                "type": "integer",
                "description": "Addresses currently held out of sending by send-time screening. They are imported but held back, and are checked again after the 90-day hold."
              }
            },
            "additionalProperties": true,
            "description": "Row counts once the import finishes, including `screened`."
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "status"
        ],
        "description": "One import of a caller-hosted CSV into an email list.",
        "additionalProperties": true
      },
      "Conversation": {
        "type": "object",
        "description": "One thread between one of the organization's sending numbers and one contact. Created by a project send (broadcast, survey, or test); never by the API.",
        "properties": {
          "conversation_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "from": {
            "type": "string",
            "description": "The organization's sending number (E.164). Every reply goes out from this number."
          },
          "to": {
            "type": "string",
            "description": "The contact's number (E.164)."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "opted_out"
            ]
          },
          "opted_out": {
            "type": "boolean",
            "description": "True once the contact replied STOP. Replies to an opted-out thread are refused with `409 CONTACT_OPTED_OUT`."
          },
          "is_test": {
            "type": "boolean"
          },
          "contact": {
            "type": "object",
            "properties": {
              "phone_number": {
                "type": "string"
              }
            },
            "required": [
              "phone_number"
            ],
            "additionalProperties": true
          },
          "inbound_messages": {
            "type": "integer"
          },
          "outbound_messages": {
            "type": "integer"
          },
          "first_message_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_inbound_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the contact last texted in. The list endpoint sorts and filters on this."
          },
          "last_outbound_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "conversation_id",
          "project_id",
          "from",
          "to",
          "status",
          "opted_out",
          "is_test",
          "contact",
          "inbound_messages",
          "outbound_messages",
          "created_at"
        ],
        "additionalProperties": true
      },
      "ConversationMessage": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid"
          },
          "direction": {
            "type": "string",
            "enum": [
              "inbound",
              "outbound"
            ]
          },
          "text": {
            "type": "string"
          },
          "media_urls": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "unsent",
              "queued",
              "sending",
              "sent",
              "delivered",
              "failed",
              "received",
              "on_hold"
            ]
          },
          "sent_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Arrival time of an inbound message; always null on outbound rows."
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ],
            "description": "Delivery error code for a failed outbound message, in the same display form as the message.failed webhook and CSV exports (e.g. \"300\" opted-out, \"012\" invalid destination; see Delivery error codes). Treat it as an opaque string. null when the message did not fail, or when the failure was a platform-side outcome rather than a delivery result."
          },
          "carrier": {
            "type": [
              "string",
              "null"
            ],
            "description": "The contact's mobile carrier: the recipient's on an outbound message, the sender's on an inbound one. The three national networks are exactly `AT&T`, `Verizon`, or `T-Mobile` (brands and subsidiaries are reported under the network they run on, e.g. Cricket under `AT&T`, Straight Talk under `Verizon`, Mint Mobile under `T-Mobile`). Any other carrier is reported under its own registered name (e.g. `CELLULAR SOUTH, INC.`); treat it as an opaque string. null when the carrier is not known, and on inbound messages received before this field was introduced."
          }
        },
        "required": [
          "message_id",
          "direction",
          "text",
          "media_urls",
          "status"
        ],
        "additionalProperties": true
      },
      "ConversationReplyResult": {
        "type": "object",
        "properties": {
          "message_id": {
            "type": "string",
            "format": "uuid",
            "description": "The queued outbound message. `message.sent` / `message.delivered` / `message.failed` webhooks for it carry this id."
          },
          "conversation_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "text": {
            "type": "string",
            "description": "The text as it will be sent (normalized for GSM encoding)."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "message_id",
          "conversation_id",
          "project_id",
          "from",
          "to",
          "text",
          "created_at"
        ],
        "additionalProperties": true
      }
    }
  }
}