/** * This file was auto-generated by openapi-typescript. * Do not make direct changes to the file. */ export interface paths { "/v1/health": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Healthy */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["HealthResponse"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Degraded */ 503: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["HealthResponse"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/version": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description API schema version */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["VersionResponse"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/api-keys": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List API keys * @description Return every non-revoked API key for the authenticated user. Includes the key prefix, scope, line allowlist, and last-used timestamp — never the plaintext (which is only emitted once at create time). */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description API key list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ApiKeyList"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; /** * Create an API key * @description Mint a new API key for the authenticated user. The plaintext key is returned ONCE in this response — store it; the server only keeps a hash. Optional `line_ids` array scopes the key to specific lines; omit or pass `[]` for account-wide access. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": { name?: string; /** * @default live * @enum {string} */ scope?: "test" | "live"; line_ids?: string[]; }; }; }; responses: { /** @description API key created */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ApiKeyCreate"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/api-keys/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; post?: never; /** * Revoke an API key * @description Mark the API key as revoked. Subsequent requests presenting this key receive 401. Revocation is irreversible — issue a fresh key via `POST /v1/api-keys` to regain access. */ delete: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description API key revoked */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ApiKeyRevoke"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List available lines (public marketplace) * @description Browse the marketplace inventory of unassigned lines. Filter by billing tier, area code, country code, status. Returns only `status: available` and never the iSnap-internal `shared_pool` tier. Unauthenticated — partners use this in prospecting / quote flows. */ get: { parameters: { query?: { billing_tier?: "rental_iphone" | "rental_android" | "byod_imessage" | "byod_android" | "shared_pool" | "shared_imessage"; area_code?: string; country_code?: string; /** @description US state filter — 2-letter code or full name. */ state?: string; /** @description Country filter — ISO alpha-2 code or country name. */ country?: string; status?: string; page?: number; limit?: number; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Available lines */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["LineList"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/my": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List the caller's own lines * @description Return every line owned by the authenticated user that's in `active` or `reserved` status. Includes a `fulfillment_status` hint for lines whose hardware is still being procured. Accepts an API key (`isnap_*`) or a dashboard JWT. */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description User lines */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["MyLines"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get line details (public marketplace) * @description Fetch a single line by its `line_` id. Available unauthenticated for the marketplace browsing flow; partner-private metadata still requires API key access via `GET /v1/lines/my`. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Line */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; /** * Update line metadata * @description Merge-patch the line's free-form `metadata` tag object. Pass `null` for a key to delete it; pass a new value to overwrite. Metadata is echoed on every `line.*` webhook so partners can attribute events back to their own customer IDs. */ patch: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["PatchLine"]; }; }; responses: { /** @description Line with merged metadata */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; trace?: never; }; "/v1/lines/assign": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Atomically reserve + activate a line * @description Find an available line matching `billing_tier` (and optional `area_code`), reserve it, and activate it in a single round-trip. Useful when the partner doesn't care which specific number they get — saves the reserve→checkout→activate dance. Scoped API keys are rejected (cannot assign lines outside their allowlist). */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["AssignLine"]; }; }; responses: { /** @description Line assigned and activated */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/shared": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Mint a virtual shared-outbound line * @description Mint a customer-owned VIRTUAL line on the shared-outbound plan (billing_tier `shared_imessage`): no dedicated hardware, no phone number of its own — sends are proxied onto the shared physical iMessage fleet, with a per-contact physical number resolved at send time. Minted directly `active` with no per-line Stripe subscription (wholesale is metered end-of-month on the active virtual-line count). Wholesale-partner only in v1; direct-customer callers get 402 until self-serve checkout ships (an admin can provision one for them in the meantime). Optional `metadata` is persisted and echoed on `line.*` webhooks. Supports `Idempotency-Key` — every mint creates a fresh billable line, so retries MUST carry the same key to avoid double-minting. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["MintSharedLine"]; }; }; responses: { /** @description Virtual shared line minted */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Payment required — no billing relationship entitles this call */ 402: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/reserve": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Reserve an available line * @description Move a specific line from `available` to `reserved` for 15 minutes. After that window a background worker auto-releases the reservation. Call `POST /v1/billing/checkout` next to convert the reservation into an active subscription. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Line reserved */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/activate": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Activate a reserved line * @description Commit a `reserved` line to `active`. Wholesale partners (no Stripe checkout surface) use this as the second phase of reserve→activate — they reserve, bill their own end-user, then activate within the 15-minute window with no per-line Stripe sub (EOM-metered), and may pick the INBOUND product via `inbound_only` (a line that cannot initiate — the contact must message first, after which the exchange is unrestricted; not receive-only). Direct callers get the test/dev mock-sub shortcut (production direct activation happens via the Stripe webhook on `checkout.session.completed`); `inbound_only` is ignored for them (theirs comes from the checkout price). */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["ActivateLine"]; }; }; responses: { /** @description Line activated */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/transfer-ownership": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Transfer a line to another iSnap user * @description Move ownership of a line to a different iSnap user. By default the paired device session stays attached so messaging continues uninterrupted; pass `preserve_device_session: false` to force the new owner to re-pair. See `/concepts/billing` for how the cutover handles the in-flight Stripe subscription. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["TransferOwnership"]; }; }; responses: { /** @description Ownership transferred */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/convert-to-test": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Convert an owned rental line into a test line * @description Flip a rental line you own into an owned test line that backs trials in your own pool (ownership → pool; test lines are free, so the deprecated monthly_price reads 0). Rejected on byod lines, lines you do not own, and lines carrying an active subscription. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Line converted to a test line */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/convert-from-test": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Revert an owned test line back to a rental line * @description Symmetric to convert-to-test: flip an owned test line back to a rental line (ownership → rental; monthly_price reads the rental tier price again, derived — nothing is stored). Rejected while the test line still hosts live trials, since reverting would strand them. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Line reverted to a rental line */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/release": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Release a line back to the marketplace * @description Cancel the line's ownership and return it to `available`. Cancels the underlying Stripe subscription at period end. The caller must own the line. See `/concepts/billing` for the proration rules. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Line released */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Line"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/quota": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a line's quota snapshot * @description Five-bucket quota snapshot: new-contacts, known-contacts, total daily messages, hourly, and minute counters with their effective limits plus 80%-warning flags. Drives the dashboard usage panel and partner self-service throttle decisions. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description 5-bucket quota snapshot */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["QuotaEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/queue": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Inspect a line's queued and scheduled messages * @description Cursor-paginated queue snapshot: per-row `to` / `body` / `status` (`queued` / `scheduled` / `delivered_to_device`) + the schedule reason and the activity-curve hour. Companion to `GET /v1/lines/{id}/quota` for partners debugging delivery delays. */ get: { parameters: { query?: { limit?: number; cursor?: string; status?: "queued" | "scheduled" | "delivered_to_device"; }; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Queue inspection — summary + paginated items */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["QueueEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/health": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a BYOD line's health snapshot * @description BYOD-only health rollup. `state` (`active` / `degraded` / `offline`) is derived from the device's fundamental capabilities — min(send_text, imessage) — with offline (no heartbeat) taking precedence. Also returns `runtime_capabilities` (the per-key tri-valued descriptor the device last reported), last heartbeat timestamp, daily quota headroom, Apple-ID-flagged flag, last-error string, bridge version. Rental tiers receive 403 `rental_health_not_exposed` — they don't own the hardware and surfacing transient state creates support-ticket noise. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description BYOD line health snapshot */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["LineHealthEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lines/{id}/config": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a line's effective send configuration * @description Return the resolved send configuration for this line — tier defaults merged with per-line overrides. Surfaces effective quota caps, timezone, activity-curve target hour, and the `overridden_keys` list so partners can see which fields they've customised. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Effective config (tier defaults + per-line overrides) */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["LineConfigEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; /** * Update a line's send configuration * @description Merge-patch the line's per-line config overrides (quota caps, timezone, activity curve). Pass `null` for a key to revert it to the tier default. Validates the activity curve sums to 1.0 and rejects an unknown timezone with 400. */ patch: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["LineConfigPatch"]; }; }; responses: { /** @description Updated effective config (merged) + overridden_keys */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["LineConfigEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; trace?: never; }; "/v1/pre-orders": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List pre-orders * @description Cursor-paginated list of the caller's pre-orders. Filter by status (`pending`, `provisioning`, `ready`, `fulfilled`, `cancelled`, `refunded`) to surface only the ones the dashboard needs. */ get: { parameters: { query?: { status?: "draft" | "pending" | "provisioning" | "ready" | "fulfilled" | "cancelled" | "refunded"; cursor?: string; limit?: number; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Pre-order list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["PreOrderList"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; /** * Create a pre-order (draft) * @description Place a pre-order as a `draft` (WHA-1155). A draft has NO side effects — no provisioning line, no procurement, no Stripe charge — so the partner can hold intent / bill its own end-user first. Call `POST /v1/pre-orders/{id}/confirm` to engage the month and start procurement. `inbound_only` is remembered for confirm. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": { /** @enum {string} */ billing_tier: "rental_iphone" | "rental_android"; /** @default [] */ preferred_area_codes?: string[]; /** @default false */ inbound_only?: boolean; }; }; }; responses: { /** @description Pre-order draft created */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["PreOrder"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/pre-orders/{id}/confirm": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Confirm a pre-order draft * @description Confirm a `draft` pre-order (WHA-1155). The binding step: engages the one-month commitment (full month, no proration) and triggers procurement — mints the `provisioning` line + fulfillment task, notifies ops, and (for direct customers) creates the first-month Stripe payment intent, returned as `stripe_client_secret`. Only a draft can be confirmed. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Pre-order confirmed (now provisioning) */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["PreOrder"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/pre-orders/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a pre-order * @description Fetch a single pre-order, including its current status, the linked Stripe payment intent, and the fulfilled-line reference if the procurement has completed. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Pre-order */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["PreOrder"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/pre-orders/{id}/cancel": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Cancel a pre-order draft * @description Cancel an unconfirmed `draft` pre-order (WHA-1155) — free, nothing was provisioned or charged. A CONFIRMED pre-order cannot be cancelled here (the month is committed, full month no proration); release its provisioned line through the standard line endpoint `POST /v1/lines/{id}/release` instead. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Pre-order cancelled */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["PreOrder"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/checkout": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Start a Stripe Checkout for a reserved line * @description Create a Stripe Checkout Session for the line previously reserved by `POST /v1/lines/{id}/reserve`. Returns the hosted checkout URL — redirect the customer there. The webhook handler activates the line once the session completes. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": { /** * @description Line to check out. Send the `line_` public ID returned by `POST /v1/lines/{id}/reserve`; the internal UUID is also accepted for backward compatibility. Must be status `reserved`. * @example line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1 */ line_id: string; /** * @description Buy the INBOUND rental product instead of the outbound one. An inbound line cannot INITIATE a conversation — the contact must message first, after which the exchange is unrestricted; it is NOT receive-only. Rental tiers only. The chosen Stripe price is the source of truth: `lines.inbound_only` is re-derived from its lookup_key on `customer.subscription.created/updated`. * @default false */ inbound_only?: boolean; }; }; }; responses: { /** @description Checkout session */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["Checkout"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/byod-checkout": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Start a Stripe Checkout for BYOD slots * @description Create a Stripe Checkout Session for the BYOD subscription product (`$15/mo` or `$150/yr` per slot, `device_count` slots). The completed session writes a `byod_subscriptions` row that `POST /v1/byod/pair` reads to enforce slot capacity. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["ByodCheckoutBody"]; }; }; responses: { /** @description BYOD checkout session */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ByodCheckoutEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/subscriptions": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List Stripe subscriptions * @description Return every active Stripe subscription on the authenticated user's customer record — both per-line rental subs and BYOD slot subs. Period boundaries, line link, price ID, and cancellation flag are surfaced verbatim from Stripe. */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Subscription list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["SubscriptionListEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/invoices": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List Stripe invoices * @description List the authenticated user's Stripe invoices in reverse-chronological order. Each invoice exposes the period boundaries (unix seconds), the integer-cent amounts, the hosted invoice URL, and the PDF link. */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Invoice list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["InvoiceListEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/subscriptions/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; post?: never; /** * Cancel a subscription * @description Schedule the subscription to cancel at the end of the current billing period. The line remains active until the period rolls over; the partner sees the `cancel_at_period_end: true` flag in `GET /v1/billing/subscriptions` until then. */ delete: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Subscription cancelled */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["BillingMessage"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/payment-methods": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List payment methods * @description List the saved payment methods on the authenticated user's Stripe customer. Each entry exposes the brand (`visa`, `mastercard`, etc.), the last four digits, and the default-method flag — never the PAN. */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Payment methods */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["PaymentMethodListEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; /** * Start adding a payment method * @description Create a Stripe SetupIntent so the customer can save a new card. The returned `client_secret` feeds directly into Stripe Elements / PaymentSheet on the partner UI; the resulting payment method is attached to the customer record on completion. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Setup intent */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["SetupIntent"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/payment-methods/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; post?: never; /** * Remove a payment method * @description Detach the payment method from the Stripe customer. Active subscriptions paying with this method will retry against the new default; ensure another method is set as default first. */ delete: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Payment method removed */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["BillingMessage"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/billing/payment-methods/{id}/default": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Set default payment method * @description Mark the payment method as the default for invoice charges. Future invoices on existing subscriptions automatically draw from this card. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Default payment method updated */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["BillingMessage"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/messages": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List messages * @description Cursor-paginated list of messages, newest first. Each item carries the full `MessageV1` shape — partners can write a uniform handler over both `GET /v1/messages` and `GET /v1/messages/{id}`. Filter by `line_id`, `status`, `direction`, or `kind` (`message` drops the separate reaction/voice rows for a clean feed; `reaction`/`voice` fetch only those; omit for all kinds); pass an opaque `cursor` from the previous page's `next_cursor` to advance. */ get: { parameters: { query?: { line_id?: string; status?: "queued" | "sent" | "delivered" | "read" | "failed"; direction?: "outbound" | "inbound"; kind?: "message" | "voice" | "reaction"; cursor?: string; limit?: number; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Cursored message list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["CursoredMessagesEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; /** * Send a message * @description Queue an outbound iMessage / SMS / RCS message on the specified line. The device picks the actual transport at delivery time (`channel: "auto"` is the only supported value in v1). Returns `202` with the queued message envelope; the message progresses through `queued → sent → delivered → read` (or `failed`) reflected via webhooks. Quota-deferred sends return `status: "scheduled"` with a future `scheduled_for`. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": { line_id: string; /** @description Recipient phone number. E.164 preferred; loose national forms are normalized (US default region). Phone-only in v1 — an email / Apple-ID handle is rejected with 400 even when POST /v1/lookup reports it iMessage-capable. */ to: string; body?: string; /** * @default auto * @enum {string} */ channel?: "auto"; /** * @description Ordered list of `att_` attachment publicIds. Mint each ID via `POST /v1/attachments` and upload the bytes to the returned signed URL before referencing the ID here. Up to 10 attachments per message and 100 MB per attachment. Per-channel total budgets at delivery time: iMessage ~100 MB, RCS ~10 MB, SMS ~1 MB — the device picks the transport at send time and degrades to a lower-cap channel only when iMessage is unavailable. * @default [] */ attachments?: string[]; effect?: { /** @enum {string} */ type: "bubble"; /** @enum {string} */ name: "slam" | "loud" | "gentle" | "invisible"; } | { /** @enum {string} */ type: "screen"; /** @enum {string} */ name: "confetti" | "balloons" | "love" | "lasers" | "fireworks" | "celebration" | "shooting_star" | "echo" | "spotlight"; }; reply_to_message_id?: string; use_typing_indicator?: boolean; metadata?: { [key: string]: unknown; }; }; }; }; responses: { /** @description Message queued */ 202: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["MessageV1Envelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unprocessable — domain rule violation */ 422: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/messages/{id}/reactions": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Add or replace a tapback * @description Add an iMessage tapback (love / like / dislike / laugh / emphasize / question) on a prior message you sent. Replace-on-add semantics: posting a different `type` against the same parent updates the existing reaction in place — switching from `like` to `love` is a single POST, no DELETE-then-POST dance. Reactions require iMessage; SMS / RCS parents are rejected with 422. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["SendReactionRequest"]; }; }; responses: { /** @description Reaction queued (added or replaced) */ 202: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ReactionEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unprocessable — domain rule violation */ 422: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; /** * Remove a tapback * @description Remove the caller's tapback on a prior message. Idempotent — `removed: false` is a 200, not a 404, so partners can fire-and-forget without first checking whether a reaction was set. The response reports the type that was removed when applicable. */ delete: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Reaction removed (idempotent — no-op when nothing to remove) */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ReactionRemovalEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/messages/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a message * @description Retrieve a single message by its `msg_` publicId. Returns the full message envelope with the current lifecycle status (`queued | scheduled | sent | delivered | read | failed | cancelled | received`). The `queue_info` field is always `null` on GET reads — that snapshot is populated only on the POST response at send time. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Message */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["MessageV1Envelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; /** * Cancel a queued or scheduled message * @description Cancel a message before it leaves iSnap. Cancellable while the row is still `queued` or `scheduled` (and has not been pushed to a device); returns `409 too_late_to_cancel` once the device has picked it up. Decrements the daily quota when the row had been counted at send time. */ delete: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Message cancelled */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["MessageV1Envelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/webhooks": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List webhook subscriptions * @description Return every webhook subscription owned by the authenticated user. Each entry surfaces the URL, the subscribed event list, the line allowlist, the active flag, the failure counter, and the last success / failure timestamps. Accepts an API key (`isnap_*`) or a dashboard JWT. */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Webhook list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["WebhookList"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; /** * Register a webhook subscription * @description Subscribe a URL to a list of event types and (optionally) a line allowlist. The server signs deliveries with an HMAC-SHA256 secret returned once here. Retries follow exponential backoff; 10 consecutive failures auto-disable the subscription. Accepts an API key (`isnap_*`) or a dashboard JWT. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": { /** Format: uri */ url: string; events: components["schemas"]["WebhookEventName"][]; line_ids?: string[]; }; }; }; responses: { /** @description Webhook created */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Webhook"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/webhooks/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a webhook subscription * @description Fetch a single webhook subscription by its `wh_` id. Returns the URL, the event list, the line allowlist, the active flag, and the metadata — never the secret (rotate via the dedicated endpoint to mint a new one). */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Webhook */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["WebhookV1Envelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; /** * Delete a webhook subscription * @description Permanently delete the webhook subscription. In-flight deliveries already enqueued will still attempt — the deletion only stops new event matching. Accepts an API key (`isnap_*`) or a dashboard JWT. */ delete: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Webhook deleted */ 204: { headers: { [name: string]: unknown; }; content?: never; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; /** * Update a webhook subscription * @description Partial update of a webhook subscription. Any subset of `url`, `events`, `line_ids`, `description`, and `active` is accepted; at least one field is required. Event names are validated against the shared `WebhookEventName` enum so typos reject at 400. */ patch: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": { /** Format: uri */ url?: string; events?: components["schemas"]["WebhookEventName"][]; line_ids?: string[] | null; description?: string | null; active?: boolean; }; }; }; responses: { /** @description Webhook updated */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["WebhookV1Envelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; trace?: never; }; "/v1/webhooks/events": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Replay webhook events within the retention window * @description Pull events fired in the last 72h (the retention window) so a partner whose receiver was down can reconcile. Cursor-paginated on `(created_at DESC, event_id DESC)`. Each item is the same `§4.1` envelope the live webhook delivery carried — partners deduplicate by `event_id`. */ get: { parameters: { query: { since: string; until?: string; /** @description A concrete event type (e.g. `message.received`), a family wildcard (`line.*`), or the catch-all `*`. */ event?: components["schemas"]["WebhookEventName"]; line_id?: string; limit?: number; cursor?: string; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Webhook events (paginated, §4.1 envelope items) */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["WebhookEventsEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/webhooks/{id}/rotate-secret": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Rotate the signing secret * @description Generate a new HMAC signing secret. The returned plaintext is shown once — update your verifier promptly. The old secret remains valid for a short grace window (`old_secret_expires_at`) so in-flight deliveries verify against either secret. */ post: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Secret rotated — plaintext shown once */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["WebhookRotateSecretEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/webhooks/{id}/deliveries": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List delivery attempts for a webhook * @description Inspect delivery attempts for a webhook subscription. Cursor-paginated on `(createdAt DESC, id DESC)`; filter by status (`pending` / `success` / `failed`), event type, or time window. Each row carries the request snippet, the response status + body snippet, and the next-retry timestamp. */ get: { parameters: { query?: { limit?: number; cursor?: string; status?: "pending" | "success" | "failed"; event?: string; since?: string; until?: string; }; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Delivery list (cursor paginated) */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["WebhookDeliveryListEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/webhooks/{id}/deliveries/{delivery_id}/replay": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Replay a delivery attempt * @description Re-enqueue a prior delivery attempt. Mints a new `whd_` delivery row referencing the original via `replay_of` but keeps the same `event_id` so partner-side dedup collapses both into the same logical event. */ post: { parameters: { query?: never; header?: never; path: { id: string; delivery_id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Replay queued */ 202: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["WebhookDeliveryReplayEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/attachments": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Create an attachment + receive a signed upload URL * @description Mint a new attachment record and receive a presigned R2 PUT URL valid for 15 minutes. The client uploads the bytes directly to R2 (the server never proxies them) and then references the returned `id` in the `attachment_ids` array on `POST /v1/messages`. Hard cap: 5 GB (R2 single-PUT ceiling). Attachments larger than 100 MB return a non-blocking `large_attachment_may_delay_delivery` warning. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["CreateAttachment"]; }; }; responses: { /** @description Attachment row created, upload pending */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["CreateAttachmentResponse"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/attachments/{id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get an attachment + signed download URL * @description Fetch an attachment record by its `att_` id. The response includes a short-TTL signed R2 download URL — re-call this endpoint to refresh the URL when the previous one expires. */ get: { parameters: { query?: never; header?: never; path: { id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Attachment record + fresh signed download URL */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": { /** @enum {boolean} */ success: true; data: components["schemas"]["Attachment"]; trace_id: string; request_id: string; }; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/lookup": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Look up a recipient capability * @description Check whether a phone number or email is iMessage-capable. Returns `imessage: true | false | null`; `null` means we have no cached probe result yet and the caller should fall back to SMS-safe behaviour. Rate-limited per line and per account. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["LookupRequest"]; }; }; responses: { /** @description Lookup result — cache hit, fast probe, or an un-probed miss */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["LookupEnvelope"]; }; }; /** @description Lookup pending — a device probe was dispatched but had not answered within the wait budget; re-poll the same request after `retry_after` seconds */ 202: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["LookupEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/area-codes": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List selectable area codes (public) * @description Enumerate the area codes a customer can request as `preferred_area_codes` on `POST /v1/pre-orders`, for rendering a pre-order area-code picker. Optional `?country=US` filters the list. Also returns the selectable countries so the same response can drive a country dropdown. Unauthenticated — mirrors `GET /v1/lines` and `GET /pricing`. The list is the *selectable* set (a curated subset of NANP), not a guarantee of immediate inventory: pre-orders are precisely for tiers with no in-stock line. */ get: { parameters: { query?: { /** @description ISO 3166-1 alpha-2 country filter (case-insensitive). Omit for all countries. */ country?: string; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Selectable area codes + countries */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["AreaCodesEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/chats": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * List chats * @description Cursor-paginated list of the caller's conversations, newest activity first. Filter by `line_id`. On shared-plan (virtual) lines each chat carries `outbound_number` — the physical number currently serving that contact (null once the binding is released; the next outbound is assigned a fresh number). Non-shared lines always carry `outbound_number: null`. Accepts an API key (`isnap_*`) or a dashboard JWT; line-scoped API keys only see chats of the lines in their allowlist. */ get: { parameters: { query?: { line_id?: string; limit?: number; cursor?: string; since?: string; until?: string; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Chat list */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ChatListEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/chats/{chat_id}": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get a chat * @description Fetch a single conversation by its `chat_` id. Same shape as the list items, including `outbound_number` for shared-plan lines. Cross-account and out-of-scope ids resolve 404 (no existence leak). */ get: { parameters: { query?: never; header?: never; path: { chat_id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Chat */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ChatEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/chats/{chat_id}/share-contact-card": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; post: { parameters: { query?: never; header?: never; path: { chat_id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": { first_name: string; last_name?: string; /** Format: uri */ avatar_url?: string; phone_numbers?: string[]; emails?: string[]; organization?: string; note?: string; }; }; }; responses: { /** @description Contact card queued */ 202: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ShareContactCardEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unprocessable — domain rule violation */ 422: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/chats/{chat_id}/typing": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; post: { parameters: { query?: never; header?: never; path: { chat_id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": Record; }; }; responses: { /** @description Typing indicator started */ 202: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ChatTypingStartEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unprocessable — domain rule violation */ 422: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete: { parameters: { query?: never; header?: never; path: { chat_id: string; }; cookie?: never; }; requestBody?: never; responses: { /** @description Typing indicator stopped */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ChatTypingStopEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/chats/{chat_id}/read": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; post: { parameters: { query?: never; header?: never; path: { chat_id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": Record; }; }; responses: { /** @description Chat marked read */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ChatMarkReadEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/trial/init": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Start a trial session * @description Create a pairing-pending trial session owned by the caller (dashboard JWT for direct self-serve, or an API key for wholesale/SDK). Returns a one-time pairing token the partner embeds in their onboarding UI; the recipient claims it by sending an inbound message that links them to a pool line. Direct accounts may hold only one live trial at a time. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["TrialInitBody"]; }; }; responses: { /** @description Trial session created — pairing pending */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["TrialInitEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Conflict — concurrent or terminal state */ 409: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/trial/status": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Check trial pairing status * @description Token-keyed public lookup. Returns the current trial state — `pairing_pending` while waiting for the recipient's first inbound, `paired` once linked, `expired` after the pairing window. On first poll after `paired` the response also reveals the `trial_api_key` (`isnap_test_*`) for the partner to embed. */ get: { parameters: { query: { token: string; }; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Current trial status */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["TrialStatusEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/trial/activity": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get trial activity counters * @description Trial-key-only snapshot of the dormancy and outbound-without-inbound counters. Partner dashboards poll this to render the trial's live state ("3/10 sends since last inbound", dormancy timer remaining). */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Trial activity snapshot */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["TrialActivityEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/trial/{trial_id}/revoke": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Revoke a trial session * @description Terminate a trial owned by the caller, freeing its pool-line slot (the same handle can immediately start a fresh trial) and invalidating its trial API key. Idempotent — revoking an already-revoked trial returns `already_revoked: true` with a 200. A trial the caller does not own returns 404 (no existence leak). Per-partner rebind policy (WHA-1642) applies ONLY to `reason: rebind` (`partner`/`admin` always pass): a partner with rebind disabled gets 403 `rebind_not_allowed`, and one over its daily rebind cap gets 429 `rebind_cap_reached` carrying `retry_at`. */ post: { parameters: { query?: never; header?: never; path: { /** @description The trl_ public id returned by POST /v1/trial/init. */ trial_id: string; }; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["TrialRevokeBody"]; }; }; responses: { /** @description Trial revoked (idempotent) */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["TrialRevokeEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Forbidden — caller authenticated but not allowed */ 403: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unprocessable — domain rule violation */ 422: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/byod/pair": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; get?: never; put?: never; /** * Start a BYOD pairing flow * @description Mint a one-time activation code plus the stable per-platform bridge-installer download links for a customer-owned Mac+iPhone or Android device. The customer runs the bridge, enters the code, and the device-side `POST /device/activate` consumes it to mint a `byod_imessage` or `byod_android` line — the platform is derived there from what the bridge reports. */ post: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: { content: { "application/json": components["schemas"]["ByodPairBody"]; }; }; responses: { /** @description Activation code + bridge download URL */ 201: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ByodPairEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Unauthenticated — missing or invalid bearer token */ 401: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Not found */ 404: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; "/v1/apps": { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; /** * Get the app/bridge download manifest (public) * @description Per-platform installer URL, advertised bridge version, and availability. Requires no pairing and no auth — use it for setup guides, "add a device" flows, reinstalls, and update prompts. The URLs are the same stable 302-redirect endpoints `POST /v1/byod/pair` returns in `download_urls`, and `version` is the same string it returns as `bridge_version`; both surfaces derive from one builder so they cannot drift. */ get: { parameters: { query?: never; header?: never; path?: never; cookie?: never; }; requestBody?: never; responses: { /** @description Per-platform download URL, version and availability */ 200: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["AppManifestEnvelope"]; }; }; /** @description Bad request — validation failed */ 400: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Too many requests — rate limit exceeded */ 429: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; /** @description Internal server error */ 500: { headers: { [name: string]: unknown; }; content: { "application/json": components["schemas"]["ErrorEnvelope"]; }; }; }; }; put?: never; post?: never; delete?: never; options?: never; head?: never; patch?: never; trace?: never; }; } export type webhooks = Record; export interface components { schemas: { HealthResponse: { /** @enum {string} */ status: "ok" | "degraded"; timestamp: string; checks: { [key: string]: string; }; }; ErrorEnvelope: { /** @enum {boolean} */ success: false; data: unknown; error: { status: number; code: string; message: string; retry_after: number | null; retry_at: string | null; issues: { path: (string | number)[]; message: string; code: string; min?: number; max?: number; }[] | null; }; trace_id: string; request_id: string; }; VersionResponse: { /** @enum {boolean} */ success: true; data: { version: string; }; trace_id: string; request_id: string; }; Line: { id: string; phone_number: string; area_code: string | null; country_code: string | null; state: string | null; state_name: string | null; /** @enum {string} */ billing_tier: "rental_iphone" | "rental_android" | "byod_imessage" | "byod_android" | "shared_pool" | "shared_imessage"; /** @description True when the customer owns the underlying hardware (BYOD billing tiers). BYOD lines expose the full line-health surface (offline/degraded events, GET /v1/lines/{id}/health). Rental lines (false) only emit durable, actionable events to keep customers out of transient infrastructure noise they cannot act on. */ byod: boolean; /** @description True when the line cannot INITIATE a conversation: the contact must send the first message, after which the exchange is unrestricted in both directions. It is NOT receive-only — the line replies, reacts and sends attachments normally once contacted. Messaging a handle that has never contacted the line is rejected with 403 outbound_first_forbidden, per recipient and with no time limit once unlocked. Cheaper than the outbound tier; always false on BYOD/pool lines. */ inbound_only: boolean; capabilities?: unknown; status: string; /** * @deprecated * @description Deprecated — derived from the line tier (0 for pool and owned test lines). The price a customer actually pays lives on the Stripe subscription (GET /v1/billing/subscriptions) or the pre-order amount. Removed at the next MAJOR. */ monthly_price: number; stripe_subscription_id: string | null; activated_at: string | null; expires_at: string | null; created_at: string; metadata: { [key: string]: unknown; } | null; /** @description True when the line is an owner-provisioned test line (ownership=pool with a set owner) that backs the owner's own trial pool. Test lines are free: monthly_price is 0, like every pool line. */ test_line: boolean; /** @description True when the line is a virtual shared-plan line (billing_tier shared_imessage): customer-owned, no dedicated hardware, phone_number empty — sends are proxied onto the shared physical iMessage fleet. Line-scoped API keys, webhooks and per-line quota apply to it like any other line. */ shared: boolean; fulfillment_status?: string | null; }; ApiKey: { /** Format: uuid */ id: string; name: string | null; prefix: string; scope: string; line_ids: string[]; rate_limit: number; created_at: string; last_used_at: string | null; }; ApiKeyCreate: components["schemas"]["ApiKey"] & { key: string; }; ApiKeyList: { api_keys: components["schemas"]["ApiKey"][]; }; ApiKeyRevoke: { message: string; }; LineList: { lines: components["schemas"]["Line"][]; total: number; page: number; limit: number; }; MyLines: { lines: components["schemas"]["Line"][]; }; AssignLine: { /** @enum {string} */ billing_tier: "rental_iphone" | "rental_android" | "byod_imessage" | "byod_android"; area_code?: string; inbound_only?: boolean; as_test_line?: boolean; metadata?: { [key: string]: unknown; }; }; MintSharedLine: { metadata?: { [key: string]: unknown; }; }; ActivateLine: { inbound_only?: boolean; }; TransferOwnership: { /** Format: uuid */ to_user_id: string; preserve_device_session?: boolean; }; PatchLine: { metadata: { [key: string]: unknown; }; }; QuotaEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["QuotaSnapshot"]; trace_id: string; request_id: string; }; QuotaSnapshot: { line_id: string; daily_new_contacts_used: number; daily_new_contact_limit: number; daily_known_contacts_used: number; daily_known_contact_limit: number; daily_messages_used: number; daily_total_message_limit: number; hourly_messages_used: number; hourly_message_limit: number; minute_messages_used: number; minute_message_limit: number; buckets_warning: components["schemas"]["BucketsWarning"]; }; BucketsWarning: { new_contacts_at_80_percent: boolean; known_contacts_at_80_percent: boolean; messages_at_80_percent: boolean; hourly_at_80_percent: boolean; minute_at_80_percent: boolean; }; QueueEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["QueueResponseData"]; trace_id: string; request_id: string; }; QueueResponseData: { line_id: string; summary: components["schemas"]["QueueSummary"]; items: components["schemas"]["QueueItem"][]; has_more: boolean; next_cursor: string | null; }; QueueSummary: { total_queued: number; total_scheduled: number; total_delivered_to_device: number; next_scheduled_at: string | null; timezone: string; daily_new_contacts_used: number; daily_new_contact_limit: number; daily_known_contacts_used: number; daily_known_contact_limit: number; daily_messages_used: number; daily_total_message_limit: number; hourly_messages_used: number; hourly_message_limit: number; minute_messages_used: number; minute_message_limit: number; buckets_warning: components["schemas"]["BucketsWarning"]; }; QueueItem: { id: string; to: string; body: string | null; /** @enum {string} */ status: "queued" | "scheduled" | "delivered_to_device"; scheduled_for: string; schedule_reason: string | null; queue_position: number; is_new_contact: boolean | null; target_activity_hour: number | null; created_at: string; }; LineHealthEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["LineHealth"]; trace_id: string; request_id: string; }; LineHealth: { line_id: string; /** @enum {boolean} */ byod: true; /** @enum {string} */ state: "active" | "degraded" | "offline"; last_heartbeat_at: string | null; daily_quota_remaining: number; apple_id_flagged: boolean; last_error: string | null; last_offline_at: string | null; bridge_version: string | null; runtime_capabilities: { [key: string]: "active" | "degraded" | "disabled"; } | null; /** @enum {string|null} */ device_state: "online" | "degraded" | "offline" | "initializing" | "blocked" | null; storage_free_bytes: number | null; }; LineConfigEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["LineConfigResponseData"]; trace_id: string; request_id: string; }; LineConfigResponseData: { line_id: string; config: components["schemas"]["EffectiveLineConfig"]; overridden_keys: string[]; knobs: components["schemas"]["LineConfigKnobs"]; }; EffectiveLineConfig: { default_use_typing_indicator: boolean; /** @enum {string} */ quota_overflow_behavior: "queue" | "drop" | "fail"; daily_new_contact_limit: number; daily_known_contact_limit: number; daily_total_message_limit: number; hourly_message_limit: number; minute_message_limit: number; min_inter_message_seconds: number; min_inter_new_contact_seconds: number; max_queue_depth: number; timezone: string; activity_curve: { hour: number; weight: number; }[]; }; LineConfigKnobs: { daily_new_contact_limit: components["schemas"]["LineConfigKnobBounds"]; daily_known_contact_limit: components["schemas"]["LineConfigKnobBounds"]; daily_total_message_limit: components["schemas"]["LineConfigKnobBounds"]; hourly_message_limit: components["schemas"]["LineConfigKnobBounds"]; minute_message_limit: components["schemas"]["LineConfigKnobBounds"]; }; LineConfigKnobBounds: { current: number; default: number; min: number; max: number; }; LineConfigPatch: { default_use_typing_indicator?: boolean; /** @enum {string} */ quota_overflow_behavior?: "queue" | "drop" | "fail"; daily_new_contact_limit?: number; daily_known_contact_limit?: number; daily_total_message_limit?: number; hourly_message_limit?: number; minute_message_limit?: number; min_inter_message_seconds?: number; min_inter_new_contact_seconds?: number; max_queue_depth?: number; timezone?: string; activity_curve?: { hour: number; weight: number; }[]; }; PreOrder: { /** Format: uuid */ id: string; /** @enum {string} */ billing_tier: "rental_iphone" | "rental_android"; preferred_area_codes: string[]; status: components["schemas"]["PreOrderStatus"]; stripe_payment_intent_id: string | null; stripe_client_secret: string | null; amount: number; estimated_fulfillment: string | null; device_status?: components["schemas"]["DeviceFulfillmentStatus"]; created_at: string; updated_at: string; }; /** @enum {string} */ PreOrderStatus: "draft" | "pending" | "provisioning" | "ready" | "fulfilled" | "cancelled" | "refunded"; /** @enum {string|null} */ DeviceFulfillmentStatus: "awaiting_hardware" | "received" | "setup_in_progress" | "ready" | "deployed" | null; PreOrderList: { /** @enum {boolean} */ success: true; data: { items: components["schemas"]["PreOrder"][]; has_more: boolean; next_cursor: string | null; }; trace_id: string; request_id: string; }; Checkout: { /** Format: uri */ url: string; }; ByodCheckoutEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ByodCheckoutResponse"]; trace_id: string; request_id: string; }; ByodCheckoutResponse: { /** * Format: uri * @description Hosted Stripe Checkout URL — redirect the customer here. */ checkout_url: string; /** * Format: date-time * @description Session expiry (ISO-8601). Stripe default is 24h. */ expires_at: string; }; ByodCheckoutBody: { /** * @description Number of BYOD slots to purchase. Each slot pairs to one device. * @example 2 */ device_count: number; /** * @description Stripe price cadence. Annual is 10x monthly (2 months free). * @example monthly * @enum {string} */ billing_cycle: "monthly" | "annual"; }; SubscriptionListEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["Subscription"][]; trace_id: string; request_id: string; }; Subscription: { id: string; status: string; current_period_start: number; current_period_end: number; cancel_at_period_end: boolean; line_id: string | null; price_id: string | null; amount: number | null; currency: string | null; created_at: number; }; InvoiceListEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["Invoice"][]; trace_id: string; request_id: string; }; Invoice: { id: string; status: string | null; amount_due: number; amount_paid: number; currency: string; invoice_pdf: string | null; hosted_invoice_url: string | null; period_start: number; period_end: number; created_at: number; }; BillingMessage: { message: string; }; PaymentMethodListEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["PaymentMethod"][]; trace_id: string; request_id: string; }; PaymentMethod: { id: string; brand: string; last4: string; exp_month: number; exp_year: number; is_default: boolean; }; SetupIntent: { client_secret: string; }; MessageV1Envelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["MessageV1"]; trace_id: string; request_id: string; }; MessageV1: { id: string; line_id: string; direction: components["schemas"]["MessageDirection"]; kind: components["schemas"]["MessageKind"]; to: string; from: string; body: string | null; channel: components["schemas"]["MessageChannel"]; service: components["schemas"]["MessageService"]; status: components["schemas"]["MessageStatus"]; effect: { /** @enum {string} */ type: "bubble"; /** @enum {string} */ name: "slam" | "loud" | "gentle" | "invisible"; } | { /** @enum {string} */ type: "screen"; /** @enum {string} */ name: "confetti" | "balloons" | "love" | "lasers" | "fireworks" | "celebration" | "shooting_star" | "echo" | "spotlight"; } | unknown; reply_to_message_id: string | null; use_typing_indicator: boolean; metadata: { [key: string]: unknown; } | null; attachments: components["schemas"]["MessageAttachmentV1"][]; reactions: components["schemas"]["MessageReactionV1"][]; scheduled_for: string | null; schedule_reason: string | null; queue_info: { queue_position: number; is_new_contact: boolean; daily_new_contacts_used: number; daily_new_contact_limit: number; daily_known_contacts_used: number; daily_known_contact_limit: number; daily_messages_used: number; daily_total_message_limit: number; messages_ahead_in_queue?: number; daily_slot_of?: string; target_activity_hour?: number; next_slot_available_at?: string; last_new_contact_sent_at?: string; min_inter_new_contact_seconds?: number; jitter_applied_seconds?: number; } | null; created_at: string; queued_at: string | null; sent_at: string | null; delivered_at: string | null; read_at: string | null; failed_at: string | null; cancelled_at: string | null; }; /** @enum {string} */ MessageDirection: "outbound" | "inbound"; /** @enum {string} */ MessageKind: "message" | "voice" | "reaction"; /** @enum {string} */ MessageChannel: "auto" | "iMessage" | "SMS" | "RCS"; /** @enum {string|null} */ MessageService: "iMessage" | "SMS" | "RCS" | null; /** @enum {string} */ MessageStatus: "queued" | "scheduled" | "sent" | "delivered" | "read" | "failed" | "cancelled"; MessageAttachmentV1: { id: string; content_type: string; filename: string; size_bytes: number; duration_ms: number | null; }; MessageReactionV1: { /** @enum {string} */ reaction_type: "love" | "like" | "dislike" | "laugh" | "emphasize" | "question" | "custom"; reaction_emoji: string | null; from_handle: string; /** @enum {string} */ direction: "outbound" | "inbound"; created_at: string; }; ReactionEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["Reaction"]; trace_id: string; request_id: string; }; Reaction: { id: string; parent_message_id: string; /** @enum {string} */ type: "love" | "like" | "dislike" | "laugh" | "emphasize" | "question"; /** @enum {string} */ direction: "outbound" | "inbound"; status: string; created_at: string; }; SendReactionRequest: { /** @enum {string} */ type: "love" | "like" | "dislike" | "laugh" | "emphasize" | "question"; }; ReactionRemovalEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ReactionRemoval"]; trace_id: string; request_id: string; }; ReactionRemoval: { parent_message_id: string; removed: boolean; /** @enum {string|null} */ type: "love" | "like" | "dislike" | "laugh" | "emphasize" | "question" | null; }; CursoredMessagesEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["CursoredMessages"]; trace_id: string; request_id: string; }; CursoredMessages: { items: components["schemas"]["MessageV1"][]; has_more: boolean; next_cursor: string | null; }; Webhook: { /** @example wh_3Kp8Rn9Fh2Lm5Qx7Vw1Yz4Bc */ id: string; url: string; events?: unknown; line_ids?: unknown; active: boolean; failure_count: number; last_success_at: string | null; last_failure_at: string | null; created_at: string; secret?: string; }; /** * @description A concrete event type (e.g. `message.received`), a family wildcard (`line.*`), or the catch-all `*`. * @enum {string} */ WebhookEventName: "message.queued" | "message.sent" | "message.delivered" | "message.read" | "message.failed" | "message.received" | "message.scheduled" | "message.cancelled" | "message.fallback_triggered" | "reaction.added" | "reaction.received" | "reaction.removed" | "line.connected" | "line.disconnected" | "line.offline" | "line.degraded" | "line.apple_id_flagged" | "line.quota_warning" | "line.quota_exceeded" | "line.capability_changed" | "typing_indicator.started" | "typing_indicator.stopped" | "trial.linked" | "trial.dormant" | "trial.reactivated" | "trial.bind_conflict" | "trial.revoked" | "binding.released" | "pre_order.fulfilled" | "pre_order.cancelled" | "pre_order.refunded" | "webhook.test" | "*" | "message.*" | "reaction.*" | "line.*" | "typing_indicator.*" | "trial.*" | "binding.*" | "pre_order.*" | "webhook.*"; WebhookList: { webhooks: components["schemas"]["Webhook"][]; }; WebhookEventsEnvelope: { /** @enum {boolean} */ success: true; data: { items: components["schemas"]["WebhookEventItem"][]; has_more: boolean; next_cursor: string | null; }; trace_id: string; request_id: string; }; WebhookEventItem: { api_version: string; event_id: string; event_type: string; created_at: string; trace_id?: string; data: { [key: string]: unknown; }; }; WebhookV1Envelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["WebhookV1"]; trace_id: string; request_id: string; }; WebhookV1: { id: string; url: string; events: string[]; line_ids: string[] | null; description: string | null; active: boolean; created_at: string; updated_at: string; secret?: string; old_secret_expires_at?: string | null; }; WebhookRotateSecretEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["WebhookRotateSecret"]; trace_id: string; request_id: string; }; WebhookRotateSecret: { id: string; secret: string; old_secret_expires_at: string; }; WebhookDeliveryListEnvelope: { /** @enum {boolean} */ success: true; data: { items: components["schemas"]["WebhookDeliveryV1"][]; has_more: boolean; next_cursor: string | null; }; trace_id: string; request_id: string; }; WebhookDeliveryV1: { id: string; webhook_id: string; event_id: string | null; event_type: string | null; attempt_number: number; status: string; request_headers: { [key: string]: unknown; } | null; request_body_snippet: string | null; response_status: number | null; response_body_snippet: string | null; delivered_at: string | null; next_retry_at: string | null; replay_of: string | null; created_at: string; }; WebhookDeliveryReplayEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["WebhookDeliveryReplay"]; trace_id: string; request_id: string; }; WebhookDeliveryReplay: { delivery_id: string; replay_of: string; /** @enum {string} */ status: "pending"; }; CreateAttachmentResponse: { id: string; /** Format: uri */ upload_url: string; upload_expires_at: string; required_headers: { [key: string]: string; }; status: string; warnings: string[]; }; CreateAttachment: { filename: string; content_type: string; /** @description Attachment size in bytes. Hard cap 5 GB (R2 single-PUT ceiling); oversize requests are rejected with HTTP 413 `attachment_exceeds_storage_limit`. Sizes above 100 MB succeed but carry a `large_attachment_may_delay_delivery` warning. */ size_bytes: number; }; Attachment: { id: string; filename: string; content_type: string; size_bytes: number; status: string; /** Format: uri */ download_url: string | null; download_expires_at: string | null; duration_ms: number | null; created_at: string; uploaded_at: string | null; expires_at: string; warnings: string[]; }; LookupEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["Lookup"]; trace_id: string; request_id: string; }; Lookup: { handle: string; imessage: boolean | null; pending?: boolean; probe_id?: string; retry_after?: number; }; LookupRequest: { handle: string; from_line_id?: string; }; AreaCodesEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["AreaCodes"]; trace_id: string; request_id: string; }; AreaCodes: { area_codes: components["schemas"]["AreaCode"][]; countries: components["schemas"]["Country"][]; }; AreaCode: { /** @example 212 */ area_code: string; /** @example New York, NY */ region: string; /** @example US */ country_code: string; }; Country: { /** @example US */ country_code: string; /** @example United States */ name: string; /** @example 1 */ calling_code: string; }; ChatListEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ChatList"]; trace_id: string; request_id: string; }; ChatList: { items: components["schemas"]["Chat"][]; has_more: boolean; next_cursor: string | null; }; Chat: { /** @example chat_5h7FqK2mNpQ8rT3vW9xY1zA4b */ id: string; line_id: string; contact_handle: string; /** @description Shared-plan lines only: the physical number currently serving this contact (the active binding). Null when no binding is active — e.g. after inactivity release, in which case the next outbound is assigned a fresh number — and always null on non-shared lines. */ outbound_number: string | null; last_message_at: string; message_count: number; unread_count: number; created_at: string; updated_at: string; }; ChatEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["Chat"]; trace_id: string; request_id: string; }; ShareContactCardEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ShareContactCardResult"]; trace_id: string; request_id: string; }; ShareContactCardResult: { message_id: string; chat_id: string; attachment_id: string; /** @enum {string} */ status: "queued"; }; ChatTypingStartEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ChatTypingStart"]; trace_id: string; request_id: string; }; ChatTypingStart: { chat_id: string; started_at: string; expires_at: string; }; ChatTypingStopEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ChatTypingStop"]; trace_id: string; request_id: string; }; ChatTypingStop: { chat_id: string; stopped_at: string; }; ChatMarkReadEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ChatMarkRead"]; trace_id: string; request_id: string; }; ChatMarkRead: { chat_id: string; read_at: string; marked_count: number; }; TrialInitEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["TrialInitData"]; trace_id: string; request_id: string; }; TrialInitData: { trial_id: string; token: string; pool_line_number: string; pairing_expires_at: string; instructions_url: string; }; TrialInitBody: { max_outbound_without_inbound?: number; welcome_message?: string; /** * @description Optional partner-supplied marketing footer appended to this trial's free pool-line outbounds, replacing the default iSnap footer. Omit to keep the iSnap default. Keep it short and GSM-7-safe — non-GSM characters force the SMS into UCS-2 (70 chars/segment) and cost extra segments. * @example Sent free via Acme */ footer?: string; }; TrialStatusEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["TrialStatusData"]; trace_id: string; request_id: string; }; TrialStatusData: { trial_id: string; token: string; /** @enum {string} */ status: "pairing_pending" | "active" | "dormant" | "revoked"; pairing_expires_at: string | null; pool_line_id: string | null; pool_line_number: string | null; paired_handle: string | null; trial_api_key?: string; activity_rule: { max_outbound_without_inbound: number; outbound_since_last_inbound: number; } | null; }; TrialActivityEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["TrialActivityData"]; trace_id: string; request_id: string; }; TrialActivityData: { trial_id: string; pool_line_id: string; paired_handle: string; /** @enum {string} */ status: "active" | "dormant"; last_inbound_at: string | null; last_activity_at: string | null; messages_sent_total: number; messages_received_total: number; activity_rule: { max_outbound_without_inbound: number; outbound_since_last_inbound: number; remaining_before_dormant: number; }; }; TrialRevokeEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["TrialRevokeData"]; trace_id: string; request_id: string; }; TrialRevokeData: { /** @enum {string} */ status: "revoked"; already_revoked: boolean; /** * @description Effective persisted revoke reason — the caller-supplied value, or `pairing_expired` when the endpoint mirrored a lazy pairing-expiry (a pairing_pending trial revoked after its window closed). On an idempotent replay it is the reason the winning revoke persisted. * @example rebind * @enum {string} */ reason: "rebind" | "partner" | "admin" | "pairing_expired"; }; TrialRevokeBody: { /** * @description Why the trial is being revoked, persisted as the audit trail. `rebind` — freed for a demo-line number change; `partner` — partner-initiated teardown (default); `admin` — iSnap ops action. Defaults to `partner` when omitted. * @example rebind * @enum {string} */ reason?: "rebind" | "partner" | "admin"; }; ByodPairEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["ByodPairResponse"]; trace_id: string; request_id: string; }; ByodPairResponse: { /** @example act_4Lp8Rn9Fh2 */ activation_code: string; /** * Format: date-time * @description Activation code expiry — 5 minutes from issue. */ expires_at: string; download_urls: components["schemas"]["ByodDownloadUrls"]; /** @example 2.0.1 */ bridge_version: string; }; ByodDownloadUrls: { /** * Format: uri * @example https://api.isnap.ai/device/apps/download/android */ android: string; /** * Format: uri * @example https://api.isnap.ai/device/apps/download/macos */ macos: string; }; ByodPairBody: { /** * @description Opaque consumer metadata, stored verbatim and echoed back at activation. iSnap never interprets the keys. * @example { * "whatsnap_client_id": "cust_a1b2" * } */ metadata?: { [key: string]: unknown; }; }; AppManifestEnvelope: { /** @enum {boolean} */ success: true; data: components["schemas"]["AppManifestResponse"]; trace_id: string; request_id: string; }; AppManifestResponse: { macos: components["schemas"]["AppDownloadEntry"]; android: components["schemas"]["AppDownloadEntry"]; }; AppDownloadEntry: { /** * Format: uri * @description Absolute URL of the installer redirect for this platform. Follow it (302) to the actual artifact. * @example https://api.isnap.ai/device/apps/download/macos */ url: string; /** * @description Bridge version this deployment advertises — the same string `POST /v1/byod/pair` returns as `bridge_version`. Compare it against an installed version to offer an update. * @example 2.0.1 */ version: string; /** * @description False when this deployment has no artifact configured for the platform. The entry is still returned, so a consumer can hide the affordance instead of surfacing a link that 404s on click. * @example true */ available: boolean; }; }; responses: never; parameters: never; requestBodies: never; headers: never; pathItems: never; } export type $defs = Record; export type operations = Record;