> This page is for Build.

> Append .md to any page URL for clean Markdown. Index: https://docs.speechify.ai/llms.txt.
>
> Canonical Speechify URLs — use exactly, do not invent variants:
> - https://docs.speechify.ai — this site (API reference, SDKs, quickstarts)
> - https://speechify.ai — marketing + product site
> - https://platform.speechify.ai — customer dashboard, signup, API keys, billing
> - https://api.speechify.ai — API base URL
> - https://github.com/Speechify-AI: GitHub org for the API (cookbook, demos, CLI). `github.com/speechify` does not exist.
> - https://status.speechify.ai — status + incidents
> - https://speechify.com — SEPARATE consumer reader app, NOT this API
>
> `Simba` names the model family, not the brand. Model ids: `simba-3.5` (48 languages under one id, English included; recommended), `simba-3.2` (English) and `simba-3.0` (English, German, Spanish, French, Italian and Portuguese; the default). `simba-english` and `simba-multilingual` are retired: a new workspace that sends either gets `400 model_retired`. `SimbaVoice` / `simbavoice.ai` are retired.
>
> Ask, don't scrape. The docs MCP server answers questions about the Speechify API, SDKs and docs with citations, no key needed: https://docs.speechify.ai/_mcp/server (Streamable HTTP, tool `searchDocs`). Setup: https://docs.speechify.ai/build/guides/get-started/connect-mcp

# List webhook endpoints

GET https://api.speechify.ai/v1/webhooks/endpoints

The caller's workspace's registered webhook endpoints. Cursor-paginated:
omit `cursor` for the first page; walk pages while `has_more` is true
(default page size 50, max 200). The signing `secret` is never returned
here — it is shown only when an endpoint is created or its secret is
rotated. Filter by delivery scope with `project_id`.

Reference: https://docs.speechify.ai/build/api-reference/v1/webhooks/endpoints/get

## Authentication

- `Authorization` header (bearer token, required) — Enter your API key with the `Bearer` prefix, e.g. 'Bearer sk_...'.

## Request

### Query parameters

- `cursor` (string, optional) — Opaque pagination cursor from a previous response.
- `limit` (integer, optional, default: 50) — Max items per page (default 50, max 200).
- `project_id` (string, optional) — Filter endpoints by project scope: omit for everything the caller may see, pass the literal `shared` for workspace-wide endpoints only, or a `proj_...` id for endpoints scoped to that project. Endpoints have no Default project - a null `project_id` means workspace-wide, so the literal here is `shared`, never `default`. Returns 404 project_not_found for a malformed id and for any project outside your reach: a project-pinned API key or service-account key reaches only its pinned project, and a member holding project grants reaches only the granted projects. That 404 is the same in every case and does not reveal whether such a project exists - outside your reach a project is answered as nonexistent, never as forbidden. `shared` is always inside your reach. Inside it, a well-formed id that matches nothing yields an empty page.

## Response

### 200

The workspace's webhook endpoints.

- `next_cursor` (string, required, nullable) — Opaque keyset cursor for the next page. Pass back as the `cursor` request parameter. `null` when the caller has reached the end of the list (`has_more` is also `false` in that case).
- `has_more` (boolean, required) — True when more rows exist beyond this page.
- `endpoints` (list of WebhookEndpoint, required)

## Errors

### 400 Bad Request Error

The request was malformed or failed validation. The response body is the standard `Error` envelope; for validation failures `error.fields` enumerates the offending fields as a `path -> message` map (code = `validation_failed`).

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 401 Unauthorized Error

Authentication is missing or invalid. The request did not carry a recognised credential (console session token, API key, or worker JWT).

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 404 Not Found Error

The referenced resource does not exist or is not visible to the caller's workspace.

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 429 Too Many Requests Error

Rate limit or concurrency limit exceeded. `error.code` says which ceiling, and they need different responses: `rate_limited` is the request-rate budget (slow down), `concurrency_limit_reached` is a workspace-wide concurrency ceiling (fewer at once, or raise it), and `conversation_turn_in_progress` is contention over one named conversation (keep one message in flight on it). Every 429 carries `Retry-After` and the request-rate budget headers; the active-call cap also carries `RateLimit-Remaining-Calls: 0`.

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

### 500 Internal Server Error

An unexpected server-side error occurred. Safe to retry with exponential backoff for idempotent requests.

- `error` (ErrorDetail, required)
- `request_id` (string, optional) — Server-side request identifier. Echoes the `Speechify-Request-Id` response header. Stable across the request's lifetime, written to structured logs, and useful when reporting issues.

## Types

### WebhookEndpoint

A workspace webhook endpoint: a destination URL, the events it subscribes to, and a server-minted HMAC signing secret. `secret` is returned ONLY in the create and rotate-secret responses — store it then; every other read omits it. An endpoint is either scoped to one project (`project_id`) or workspace-wide, and a workspace-wide endpoint receives events from every project.

- `id` (string, required) — Prefixed wire id (`whe_<26 char Crockford base32>`).
- `url` (string, required) — HTTPS destination Speechify POSTs signed events to.
- `enabled_events` (list of string, required) — The events this endpoint receives: a list of catalog event names (for example `workspace.spend_budget.warning`) or `["*"]` for every event, current and future.
- `include` (list of string, required) — Per-event payload shaping. Deliveries are lean by default: `data.object` carries only the resource GET snapshot. List heavy collections here to have them appended under the event's `data` alongside `object`, so receivers behind hard request-size caps stay lean unless they opt in. An event's documentation names the keys it recognises. Empty = lean.
- `api_version` (date, required) — The dated payload shape this endpoint receives (`YYYY-MM-DD`), the same versioning vocabulary the REST API uses. Every delivery is rendered back to this version and carries it in the `Speechify-Version` header and the payload's top-level `version` field. Defaults to your workspace's current version at creation; change it to opt into a newer shape.
- `disabled` (boolean, required) — When true, Speechify stops delivering to this endpoint.
- `created_at` (datetime, required)
- `updated_at` (datetime, required)
- `project_id` (string, optional, nullable) — The project whose events this endpoint receives (prefixed external id). Null means workspace-wide - it receives every project's events. Endpoints have no Default project. An event is routed by the project frozen on the row that produced it (an API key's project, for example), and for a `project.spend_budget.*` event the project itself. Workspace-level events such as `workspace.spend_budget.*` belong to no project and reach workspace-wide endpoints only, so a scoped endpoint subscribed to those alone is refused with a 400 naming `enabled_events` rather than accepted and never delivered to. A scoped endpoint records no delivery for another project's events.
- `description` (string, optional, nullable) — Optional human-readable label for the endpoint.
- `secret` (string, optional) — The HMAC-SHA256 signing secret (`whsec_…`) used to verify the `Speechify-Signature` header. Returned ONLY when the endpoint is created or its secret is rotated — it is never shown again.

### ErrorDetail

- `code` (enum, required) — Stable machine-readable error code. Additive only: codes are never renamed, only deprecated. SDKs may map each code to a typed exception class. Status-code semantics: 4xx codes describe caller-fixable issues; 5xx codes describe server-side failures and are safe to retry with backoff for idempotent requests.
  - Allowed values: `endpoint_moved`, `bad_request`, `validation_failed`, `unauthorized`, `payment_required`, `forbidden`, `not_found`, `method_not_allowed`, `conflict`, `idempotency_conflict`, `payload_too_large`, `unsupported_media_type`, `rate_limited`, `concurrency_limit_reached`, `invalid_api_version`, `internal_error`, `upstream_failure`, `service_unavailable`, `caller_not_found`, `contact_not_found`, `contact_identifier_not_found`, `contact_identifier_conflict`, `contact_resolver_not_found`, `credential_not_found`, `credential_in_use`, `agent_not_found`, `agent_in_use`, `agent_run_not_found`, `kb_not_found`, `kb_document_not_found`, `kb_folder_not_found`, `tool_not_found`, `tool_name_taken`, `channel_instance_not_found`, `team_not_found`, `trigger_not_found`, `store_not_found`, `store_document_not_found`, `hosted_api_not_found`, `api_route_not_found`, `consumer_key_not_found`, `skill_not_found`, `skill_version_not_found`, `file_not_found`, `file_path_taken`, `file_storage_limit_reached`, `store_limit_reached`, `store_document_limit_reached`, `store_bytes_limit_reached`, `store_not_configured`, `store_document_version_conflict`, `store_document_deleted`, `entitlement_override_exists`, `hosted_apis_not_in_plan`, `skills_not_in_plan`, `voice_agents_not_in_plan`, `skill_in_use`, `skill_tool_name_conflict`, `skill_limit_reached`, `agent_skill_limit_reached`, `hosted_api_slug_taken`, `api_route_conflict`, `mount_plan_changed`, `route_output_unavailable`, `route_run_timeout`, `route_run_failed`, `route_run_limit_reached`, `route_read_limit_reached`, `route_write_limit_reached`, `hosted_api_public_refused`, `route_tool_not_readable`, `route_tool_unavailable`, `route_upstream_rate_limited`, `route_upstream_error`, `hosted_mcp_not_enabled`, `hosted_api_busy`, `conversation_not_found`, `phone_number_not_found`, `sip_trunk_not_found`, `voice_not_found`, `audio_asset_not_found`, `builtin_not_found`, `batch_not_found`, `agent_test_not_found`, `workspace_not_found`, `invite_not_found`, `project_not_found`, `cross_project_reference`, `project_has_scoped_credentials`, `project_not_empty`, `project_limit_reached`, `agent_limit_reached`, `project_too_large_to_promote`, `call_not_found`, `message_not_found`, `thread_not_found`, `call_not_active`, `relay_displaces_agent`, `brain_not_found`, `brain_in_use`, `custom_model_not_found`, `custom_model_in_use`, `insufficient_scope`, `purchased_numbers_not_included`, `phone_number_quota_reached`, `batch_calls_not_included`, `voice_cloning_not_included`, `voice_cloning_unavailable_in_region`, `consent_challenge_not_found`, `consent_challenge_expired`, `consent_challenge_already_used`, `consent_phrase_mismatch`, `consent_speaker_mismatch`, `consent_recording_unusable`, `consent_verification_unavailable`, `consent_verification_required`, `watermark_audio_unusable`, `watermark_detection_unavailable`, `workspace_last_owner`, `workspace_last_workspace`, `account_deletion_blocked`, `workspace_free_limit`, `workspace_single_owner`, `invite_email_mismatch`, `invite_already_pending`, `service_account_limit_reached`, `service_accounts_not_in_plan`, `speech_marks_unsupported`, `pronunciations_unsupported`, `model_retired`, `too_many_voices`, `content_policy_violation`, `safety_identifier_blocked`, `topup_not_in_plan`, `credit_purchase_unpaid`, `credit_purchase_payment_in_progress`, `tool_config_shared`, `spend_cap_exceeded`, `spend_budget_exceeded`, `project_spend_limit_exceeded`, `project_archived`, `project_not_archived`, `project_not_purged`, `project_restore_window_expired`, `project_name_taken`, `funded_balance_required`, `agent_publish_gate_failed`, `agent_publish_gate_required`, `agent_publish_gate_unavailable`, `agent_publish_gate_tool_unreachable`, `text_channel_not_in_plan`, `channel_not_in_plan`, `text_turn_failed`, `conversation_turn_in_progress`, `conversation_closed`, `conversation_not_reachable`, `conversation_channel_bound`, `conversation_prompt_not_found`, `text_message_quota_exceeded`, `durable_runs_not_in_plan`, `tool_transport_unsupported`, `agent_config_too_large`, `agent_run_not_pending`, `agent_run_action_stale`, `share_link_not_found`, `share_link_exhausted`, `share_link_limit_reached`, `destination_not_allowed`, `international_dialing_not_enabled`, `number_not_sms_capable`, `verification_required`, `intended_use_required`
- `message` (string, required) — Human-readable explanation of this specific occurrence. Safe to surface in UI banners or pass to support. The wording can change between releases; clients should match on `code`, not on the message string.
- `fields` (map from string to string, optional) — Per-field validation errors as `path -> message`. Only present on 400 responses caused by request validation (typically code=`validation_failed`). Keys are field paths in dotted/bracket notation; values are short human explanations safe to inline-surface next to the offending form field.
- `details` (map from string to any, optional) — Structured, endpoint-specific context beyond the flat `fields` map. Present only on the few errors that carry it (e.g. the `used_by` referrer list on a credential delete-conflict); its shape depends on the error `code`. Clients that don't recognise a `details` shape can ignore it - the `code` + `message` contract is unchanged.
- `docs_url` (string, optional) — Link to the documentation that resolves this class of error, when a stable page exists. Rate and concurrency 429s link the API limits reference, which lists each plan's limits and how to raise them.

## Examples

**Response**

```json
{
  "next_cursor": "string",
  "has_more": true,
  "endpoints": [
    {
      "id": "whe_01kwxwcbyb6wk952swa0432cf1",
      "url": "https://example.com/speechify/webhooks",
      "enabled_events": [
        "project.spend_budget.warning"
      ],
      "include": [],
      "api_version": "2026-06-28",
      "disabled": true,
      "created_at": "2024-01-15T09:30:00Z",
      "updated_at": "2024-01-15T09:30:00Z",
      "project_id": "proj_01arz3ndektsv4rrffq69g5fav",
      "description": "string",
      "secret": "whsec_demo••••0000"
    }
  ]
}
```