> For the complete documentation index, see [llms.txt](https://boundaryai.gitbook.io/boundaryai-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://boundaryai.gitbook.io/boundaryai-docs/api-and-webhooks/invites.md).

# Invites

Trigger personalized survey-invite emails from the API using the template designed in the dashboard. Requires the `send` key permission and a verified sending domain.

## Get an invite distribution's status

> Lifecycle + counts for one API-triggered send. \`status\` is the discriminator: pending/sending are in flight; sent/partial/failed are terminal. Subscribe to the \`invites.completed\` webhook event instead of polling.\
> \
> \*\*Response envelope migration:\*\* during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's \`data\` property, without the \`{"data": ...}\` wrapper). The \`Deprecation\` and \`Sunset\` headers on every 2xx announce this envelope migration — they do \*\*not\*\* deprecate the endpoint itself. After the \`Sunset\` date, responses will be wrapped as \`{"data": ...}\` exactly as the schema declares.

```json
{"openapi":"3.1.0","info":{"title":"BAI Analytics API","version":"1.3.0"},"tags":[{"name":"Invites","description":"Trigger personalized survey-invite emails from the API using the template designed in the dashboard. Requires the `send` key permission and a verified sending domain."}],"servers":[{"url":"https://app.boundary-ai.com","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"BAI Analytics API key","description":"API key authentication: pass your key in the Authorization header as `Bearer inpk_live_...` (or `inpk_test_...` for a test-environment key). Create and manage keys in the dashboard under Integrations Hub -> API Keys. Each key carries a permission scope (push, create, send, mcp_read, or all) and a per-minute rate limit."}},"schemas":{"InviteDistributionStatus":{"description":"GET /api/input/invites/{distribution_id}.","properties":{"distribution_id":{"title":"Distribution Id","type":"integer"},"survey_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"default":null,"title":"Survey Id"},"status":{"enum":["pending","sending","sent","partial","failed","canceled"],"title":"Status","type":"string"},"total":{"default":0,"title":"Total","type":"integer"},"sent":{"default":0,"title":"Sent","type":"integer"},"failed":{"default":0,"title":"Failed","type":"integer"},"skipped":{"default":0,"title":"Skipped","type":"integer"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Created At"},"finished_at":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Finished At"}},"required":["distribution_id","status"],"title":"InviteDistributionStatus","type":"object"},"ErrorEnvelope":{"description":"Top-level error response.\n\nSchema name in the OpenAPI document: ``ErrorEnvelope``.","properties":{"error":{"$ref":"#/components/schemas/ErrorObject"},"details":{"anyOf":[{},{"type":"null"}],"default":null,"description":"Optional structured details (e.g. validation field map)","title":"Details"}},"required":["error"],"title":"ErrorEnvelope","type":"object"},"ErrorObject":{"description":"The inner ``error`` field of an error envelope.\n\nSchema name in the OpenAPI document: ``ErrorObject``.","properties":{"code":{"description":"Machine-readable error code drawn from ErrorCode enum","title":"Code","type":"string"},"message":{"description":"Human-readable error message, safe to display","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorObject","type":"object"}},"responses":{"RateLimited":{"description":"Rate limit exceeded — see Retry-After header.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/XRateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying. Emitted on 429 responses and on 503 responses from the global rate limiter. Rate-limit windows are fixed (anchored at the first request in the window): requests rejected with 429 do not extend the window, so waiting until ``X-RateLimit-Reset`` always recovers.","schema":{"type":"integer","minimum":1}},"XRateLimitLimit":{"description":"Total requests allowed in the current rate-limit window.","schema":{"type":"integer","minimum":1}},"XRateLimitRemaining":{"description":"Requests remaining in the current rate-limit window.","schema":{"type":"integer","minimum":0}},"XRateLimitReset":{"description":"Unix timestamp when the rate-limit window resets. The window is fixed from its first request — 429-rejected requests do not push this forward.","schema":{"type":"integer","minimum":0}}}},"paths":{"/api/input/invites/{distribution_id}":{"get":{"summary":"Get an invite distribution's status","description":"Lifecycle + counts for one API-triggered send. `status` is the discriminator: pending/sending are in flight; sent/partial/failed are terminal. Subscribe to the `invites.completed` webhook event instead of polling.\n\n**Response envelope migration:** during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's `data` property, without the `{\"data\": ...}` wrapper). The `Deprecation` and `Sunset` headers on every 2xx announce this envelope migration — they do **not** deprecate the endpoint itself. After the `Sunset` date, responses will be wrapped as `{\"data\": ...}` exactly as the schema declares.","tags":["Invites"],"operationId":"inputapi_get_invite_distribution","parameters":[{"name":"distribution_id","in":"path","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InviteDistributionStatus"}},"required":["data"]}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List a distribution's recipients (cursor-paginated)

> Per-recipient outcomes: send status, ESP delivery status (populated asynchronously from delivery events), and your \`external\_id\` for correlation. Pass \`next\_cursor\` back as \`cursor\` for the next page.\
> \
> \*\*Response envelope migration:\*\* during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's \`data\` property, without the \`{"data": ...}\` wrapper). The \`Deprecation\` and \`Sunset\` headers on every 2xx announce this envelope migration — they do \*\*not\*\* deprecate the endpoint itself. After the \`Sunset\` date, responses will be wrapped as \`{"data": ...}\` exactly as the schema declares.

```json
{"openapi":"3.1.0","info":{"title":"BAI Analytics API","version":"1.3.0"},"tags":[{"name":"Invites","description":"Trigger personalized survey-invite emails from the API using the template designed in the dashboard. Requires the `send` key permission and a verified sending domain."}],"servers":[{"url":"https://app.boundary-ai.com","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"BAI Analytics API key","description":"API key authentication: pass your key in the Authorization header as `Bearer inpk_live_...` (or `inpk_test_...` for a test-environment key). Create and manage keys in the dashboard under Integrations Hub -> API Keys. Each key carries a permission scope (push, create, send, mcp_read, or all) and a per-minute rate limit."}},"schemas":{"InviteRecipientsPage":{"properties":{"items":{"items":{"$ref":"#/components/schemas/InviteRecipientStatus"},"title":"Items","type":"array"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Next Cursor"},"has_more":{"default":false,"title":"Has More","type":"boolean"}},"title":"InviteRecipientsPage","type":"object"},"InviteRecipientStatus":{"properties":{"id":{"description":"Cursor-orderable row id","title":"Id","type":"integer"},"email":{"title":"Email","type":"string"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"External Id"},"status":{"enum":["pending","sent","failed","skipped"],"title":"Status","type":"string"},"delivery_status":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"description":"ESP outcome: delivered | bounced | dropped | complained. Null until an event arrives.","title":"Delivery Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Error"}},"required":["id","email","status"],"title":"InviteRecipientStatus","type":"object"},"ErrorEnvelope":{"description":"Top-level error response.\n\nSchema name in the OpenAPI document: ``ErrorEnvelope``.","properties":{"error":{"$ref":"#/components/schemas/ErrorObject"},"details":{"anyOf":[{},{"type":"null"}],"default":null,"description":"Optional structured details (e.g. validation field map)","title":"Details"}},"required":["error"],"title":"ErrorEnvelope","type":"object"},"ErrorObject":{"description":"The inner ``error`` field of an error envelope.\n\nSchema name in the OpenAPI document: ``ErrorObject``.","properties":{"code":{"description":"Machine-readable error code drawn from ErrorCode enum","title":"Code","type":"string"},"message":{"description":"Human-readable error message, safe to display","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorObject","type":"object"}},"responses":{"RateLimited":{"description":"Rate limit exceeded — see Retry-After header.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/XRateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying. Emitted on 429 responses and on 503 responses from the global rate limiter. Rate-limit windows are fixed (anchored at the first request in the window): requests rejected with 429 do not extend the window, so waiting until ``X-RateLimit-Reset`` always recovers.","schema":{"type":"integer","minimum":1}},"XRateLimitLimit":{"description":"Total requests allowed in the current rate-limit window.","schema":{"type":"integer","minimum":1}},"XRateLimitRemaining":{"description":"Requests remaining in the current rate-limit window.","schema":{"type":"integer","minimum":0}},"XRateLimitReset":{"description":"Unix timestamp when the rate-limit window resets. The window is fixed from its first request — 429-rejected requests do not push this forward.","schema":{"type":"integer","minimum":0}}}},"paths":{"/api/input/invites/{distribution_id}/recipients":{"get":{"summary":"List a distribution's recipients (cursor-paginated)","description":"Per-recipient outcomes: send status, ESP delivery status (populated asynchronously from delivery events), and your `external_id` for correlation. Pass `next_cursor` back as `cursor` for the next page.\n\n**Response envelope migration:** during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's `data` property, without the `{\"data\": ...}` wrapper). The `Deprecation` and `Sunset` headers on every 2xx announce this envelope migration — they do **not** deprecate the endpoint itself. After the `Sunset` date, responses will be wrapped as `{\"data\": ...}` exactly as the schema declares.","tags":["Invites"],"operationId":"inputapi_get_invite_recipients","parameters":[{"name":"distribution_id","in":"path","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50}}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InviteRecipientsPage"}},"required":["data"]}}}},"400":{"description":"Bad Request — validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Inspect the source's invite template + sending readiness

> What an integration needs before calling the send endpoint: whether a template is configured in the platform, which variable keys it can personalize with, whether the org's sender domain is verified (required for API sends), and the current daily quota usage.\
> \
> \*\*Response envelope migration:\*\* during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's \`data\` property, without the \`{"data": ...}\` wrapper). The \`Deprecation\` and \`Sunset\` headers on every 2xx announce this envelope migration — they do \*\*not\*\* deprecate the endpoint itself. After the \`Sunset\` date, responses will be wrapped as \`{"data": ...}\` exactly as the schema declares.

```json
{"openapi":"3.1.0","info":{"title":"BAI Analytics API","version":"1.3.0"},"tags":[{"name":"Invites","description":"Trigger personalized survey-invite emails from the API using the template designed in the dashboard. Requires the `send` key permission and a verified sending domain."}],"servers":[{"url":"https://app.boundary-ai.com","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"BAI Analytics API key","description":"API key authentication: pass your key in the Authorization header as `Bearer inpk_live_...` (or `inpk_test_...` for a test-environment key). Create and manage keys in the dashboard under Integrations Hub -> API Keys. Each key carries a permission scope (push, create, send, mcp_read, or all) and a per-minute rate limit."}},"schemas":{"InviteTemplateInfo":{"description":"GET /api/input/sources/{id}/invite-template — what an integration\nneeds to know before calling the send endpoint.","properties":{"configured":{"title":"Configured","type":"boolean"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Subject"},"variables":{"description":"Column keys available as {{tokens}} in the template","items":{"type":"string"},"title":"Variables","type":"array"},"sender_domain_verified":{"default":false,"title":"Sender Domain Verified","type":"boolean"},"sending_enabled":{"description":"True when template + verified domain + published survey are all in place","title":"Sending Enabled","type":"boolean"},"daily_quota":{"default":0,"title":"Daily Quota","type":"integer"},"daily_quota_used":{"default":0,"title":"Daily Quota Used","type":"integer"}},"required":["configured","sending_enabled"],"title":"InviteTemplateInfo","type":"object"},"ErrorEnvelope":{"description":"Top-level error response.\n\nSchema name in the OpenAPI document: ``ErrorEnvelope``.","properties":{"error":{"$ref":"#/components/schemas/ErrorObject"},"details":{"anyOf":[{},{"type":"null"}],"default":null,"description":"Optional structured details (e.g. validation field map)","title":"Details"}},"required":["error"],"title":"ErrorEnvelope","type":"object"},"ErrorObject":{"description":"The inner ``error`` field of an error envelope.\n\nSchema name in the OpenAPI document: ``ErrorObject``.","properties":{"code":{"description":"Machine-readable error code drawn from ErrorCode enum","title":"Code","type":"string"},"message":{"description":"Human-readable error message, safe to display","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorObject","type":"object"}},"responses":{"RateLimited":{"description":"Rate limit exceeded — see Retry-After header.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"},"X-RateLimit-Limit":{"$ref":"#/components/headers/XRateLimitLimit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/XRateLimitRemaining"},"X-RateLimit-Reset":{"$ref":"#/components/headers/XRateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying. Emitted on 429 responses and on 503 responses from the global rate limiter. Rate-limit windows are fixed (anchored at the first request in the window): requests rejected with 429 do not extend the window, so waiting until ``X-RateLimit-Reset`` always recovers.","schema":{"type":"integer","minimum":1}},"XRateLimitLimit":{"description":"Total requests allowed in the current rate-limit window.","schema":{"type":"integer","minimum":1}},"XRateLimitRemaining":{"description":"Requests remaining in the current rate-limit window.","schema":{"type":"integer","minimum":0}},"XRateLimitReset":{"description":"Unix timestamp when the rate-limit window resets. The window is fixed from its first request — 429-rejected requests do not push this forward.","schema":{"type":"integer","minimum":0}}}},"paths":{"/api/input/sources/{survey_id}/invite-template":{"get":{"summary":"Inspect the source's invite template + sending readiness","description":"What an integration needs before calling the send endpoint: whether a template is configured in the platform, which variable keys it can personalize with, whether the org's sender domain is verified (required for API sends), and the current daily quota usage.\n\n**Response envelope migration:** during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's `data` property, without the `{\"data\": ...}` wrapper). The `Deprecation` and `Sunset` headers on every 2xx announce this envelope migration — they do **not** deprecate the endpoint itself. After the `Sunset` date, responses will be wrapped as `{\"data\": ...}` exactly as the schema declares.","tags":["Invites"],"operationId":"inputapi_get_source_invite_template","parameters":[{"name":"survey_id","in":"path","required":true,"schema":{"type":"integer","format":"int64"},"description":"The source id"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InviteTemplateInfo"}},"required":["data"]}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden — insufficient permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Send survey invites by email (transactional)

> Emails the survey to the supplied recipients using the invite template designed in the platform. Recipients are appended to the source's respondent list (each gets a personalized survey link, so responses join back to the contact). Addresses that already received this survey are skipped unless \`resend\` is true; suppressed addresses (unsubscribed / bounced) are never emailed. Requires a \`send\`-scoped API key AND the org's own verified sending domain — the shared BAI Analytics sender is reserved for dashboard sends. Set \`dry\_run\` to preview the rendered emails without sending. Returns 202 + a \`distribution\_id\` to poll; subscribe to \`invites.completed\` and \`invite.bounced\` webhook events for the outcomes.\
> \
> \*\*Requires permission:\*\* \`send\` or \`all\`.\
> \
> \*\*Response envelope migration:\*\* during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's \`data\` property, without the \`{"data": ...}\` wrapper). The \`Deprecation\` and \`Sunset\` headers on every 2xx announce this envelope migration — they do \*\*not\*\* deprecate the endpoint itself. After the \`Sunset\` date, responses will be wrapped as \`{"data": ...}\` exactly as the schema declares.

```json
{"openapi":"3.1.0","info":{"title":"BAI Analytics API","version":"1.3.0"},"tags":[{"name":"Invites","description":"Trigger personalized survey-invite emails from the API using the template designed in the dashboard. Requires the `send` key permission and a verified sending domain."}],"servers":[{"url":"https://app.boundary-ai.com","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"BAI Analytics API key","description":"API key authentication: pass your key in the Authorization header as `Bearer inpk_live_...` (or `inpk_test_...` for a test-environment key). Create and manage keys in the dashboard under Integrations Hub -> API Keys. Each key carries a permission scope (push, create, send, mcp_read, or all) and a per-minute rate limit."}},"schemas":{"SendInvitesRequest":{"properties":{"recipients":{"items":{"$ref":"#/components/schemas/InviteRecipient"},"maxItems":1000,"minItems":1,"title":"Recipients","type":"array"},"resend":{"default":false,"description":"Re-invite addresses that already received this survey (default: they are skipped).","title":"Resend","type":"boolean"},"dry_run":{"default":false,"description":"Render previews for the first recipients and send nothing.","title":"Dry Run","type":"boolean"}},"required":["recipients"],"title":"SendInvitesRequest","type":"object"},"InviteRecipient":{"properties":{"email":{"maxLength":320,"title":"Email","type":"string"},"external_id":{"anyOf":[{"maxLength":255,"type":"string"},{"type":"null"}],"default":null,"description":"Your correlation id for this contact — echoed on the recipients listing.","title":"External Id"},"variables":{"additionalProperties":true,"description":"Values for the template's {{column_key}} tokens. Keys must match the respondent list's columns configured in the platform; unknown keys are rejected with the available column list.","title":"Variables","type":"object"}},"required":["email"],"title":"InviteRecipient","type":"object"},"SendInvitesDryRun":{"description":"200 body of a dry_run call — nothing was sent or persisted.","properties":{"status":{"const":"dry_run","title":"Status","type":"string"},"previews":{"items":{"$ref":"#/components/schemas/InvitePreview"},"title":"Previews","type":"array"},"would_accept":{"minimum":0,"title":"Would Accept","type":"integer"},"skipped_already_invited":{"items":{"type":"string"},"title":"Skipped Already Invited","type":"array"},"invalid":{"items":{"type":"string"},"title":"Invalid","type":"array"}},"required":["status","would_accept"],"title":"SendInvitesDryRun","type":"object"},"InvitePreview":{"properties":{"email":{"title":"Email","type":"string"},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Subject"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Body Html"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"default":null,"title":"Error"}},"required":["email"],"title":"InvitePreview","type":"object"},"SendInvitesAccepted":{"description":"202 body — the distribution was queued.","properties":{"status":{"const":"accepted","title":"Status","type":"string"},"distribution_id":{"title":"Distribution Id","type":"integer"},"task_id":{"title":"Task Id","type":"string"},"accepted":{"description":"Recipients queued for sending","minimum":0,"title":"Accepted","type":"integer"},"skipped_already_invited":{"items":{"type":"string"},"title":"Skipped Already Invited","type":"array"},"invalid":{"items":{"type":"string"},"title":"Invalid","type":"array"},"status_url":{"title":"Status Url","type":"string"}},"required":["status","distribution_id","task_id","accepted","status_url"],"title":"SendInvitesAccepted","type":"object"},"ErrorEnvelope":{"description":"Top-level error response.\n\nSchema name in the OpenAPI document: ``ErrorEnvelope``.","properties":{"error":{"$ref":"#/components/schemas/ErrorObject"},"details":{"anyOf":[{},{"type":"null"}],"default":null,"description":"Optional structured details (e.g. validation field map)","title":"Details"}},"required":["error"],"title":"ErrorEnvelope","type":"object"},"ErrorObject":{"description":"The inner ``error`` field of an error envelope.\n\nSchema name in the OpenAPI document: ``ErrorObject``.","properties":{"code":{"description":"Machine-readable error code drawn from ErrorCode enum","title":"Code","type":"string"},"message":{"description":"Human-readable error message, safe to display","title":"Message","type":"string"}},"required":["code","message"],"title":"ErrorObject","type":"object"}}},"paths":{"/api/input/sources/{survey_id}/invites":{"post":{"summary":"Send survey invites by email (transactional)","description":"Emails the survey to the supplied recipients using the invite template designed in the platform. Recipients are appended to the source's respondent list (each gets a personalized survey link, so responses join back to the contact). Addresses that already received this survey are skipped unless `resend` is true; suppressed addresses (unsubscribed / bounced) are never emailed. Requires a `send`-scoped API key AND the org's own verified sending domain — the shared BAI Analytics sender is reserved for dashboard sends. Set `dry_run` to preview the rendered emails without sending. Returns 202 + a `distribution_id` to poll; subscribe to `invites.completed` and `invite.bounced` webhook events for the outcomes.\n\n**Requires permission:** `send` or `all`.\n\n**Response envelope migration:** during the current migration window, 2xx responses carry the bare payload shown in the example (the value of the schema's `data` property, without the `{\"data\": ...}` wrapper). The `Deprecation` and `Sunset` headers on every 2xx announce this envelope migration — they do **not** deprecate the endpoint itself. After the `Sunset` date, responses will be wrapped as `{\"data\": ...}` exactly as the schema declares.","tags":["Invites"],"operationId":"inputapi_post_source_invites","parameters":[{"name":"survey_id","in":"path","required":true,"schema":{"type":"integer","format":"int64"},"description":"The source id"},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"Opaque retry key (24h window). Strongly recommended for send calls."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInvitesRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SendInvitesDryRun"}},"required":["data"]}}}},"202":{"description":"Accepted — task queued for async processing","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SendInvitesAccepted"}},"required":["data"]}}}},"400":{"description":"Bad Request — validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"401":{"description":"Unauthorized — missing or invalid credentials","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"403":{"description":"Forbidden — insufficient permission","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"409":{"description":"Conflict (idempotency key reuse or resource conflict)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}},"429":{"description":"Rate Limited — see Retry-After header","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorEnvelope"}}}}}}}}}
```
