Speko Docs
Build

Workspace webhooks

Route lifecycle events to organization endpoints by event, agent, and session tags, then inspect every delivery attempt.

Workspace webhooks are managed independently from agents. Configure them in Settings → Webhooks or through /v1/webhooks. One endpoint can subscribe to several lifecycle events and either every agent or an explicit agent set.

Create an endpoint

const endpoint = await speko.webhooks.create({
  name: 'Production call events',
  url: 'https://hooks.example.com/speko',
  events: ['call.status', 'call.report', 'call.analysis', 'call.recording'],
  allAgents: true,
  filterTags: { environment: 'production' },
  headers: { 'x-source': 'speko' },
  authHeaders: [{ name: 'Authorization', value: 'Bearer secret-token' }],
  timeoutMs: 4000,
});

Supported events are:

EventWhen it firesAutomatic retries
call.pre_callBefore a phone worker starts; the response may override supported call configurationNo
call.statusWhen a call lifecycle event is recordedNo; best effort
call.reportWhen the report is finalized, and again when a recording becomes readyYes
call.analysisWhen post-call analysis completesYes
call.recordingWhen recording reaches ready or failedYes

Every delivery includes a stable webhook_id for that event occurrence and webhook_tags from the session. A report's initial and recording-ready emissions are separate occurrences with different IDs; retries and manual redeliveries of one occurrence reuse its ID.

Agent and tag routing

allAgents defaults to true. Set it to false and provide agentIds to narrow the endpoint. New agents automatically match all-agent endpoints.

Tags are free-form, case-sensitive string pairs. Attach them when creating an agent-backed browser, realtime, or phone session:

await speko.voice.dial({
  to: '+12015551234',
  agentId: 'agent_123',
  webhookTags: {
    environment: 'production',
    customer: 'acme',
  },
});

An endpoint matches when all of its filterTags exist on the session with equal values. If at least one tagged endpoint matches, Speko sends to every matching tagged endpoint and does not use untagged defaults. If none match, Speko falls back to all matching untagged endpoints. Agentless sessions reject webhookTags because they cannot resolve an agent-scoped route.

Only one effective call.pre_call endpoint may exist for an agent/tag route. Conflicting endpoint configuration is rejected, and session creation fails explicitly if inconsistent stored data would still make the route ambiguous.

Signing and secrets

Requests use Standard Webhooks headers and default to the workspace signing secret. Set signingSecretSource: 'custom' with a write-only signingSecret to override it for one endpoint. Secret auth-header values are encrypted and returned only as { name, configured: true }. On update, omit an existing auth-header value to keep it or supply a value to rotate it.

Query values, signatures, authorization, cookies, API keys, and credential-shaped headers are redacted from delivery logs. Response bodies are capped at 64 KiB and expose responseTruncated.

Extraction fields

Endpoints subscribed to call.report or call.analysis can request extractionFields. Speko executes one extraction pass using the union required by all matching endpoints, then includes only each endpoint's requested fields in its custom_data. Identical definitions deduplicate; conflicting definitions are rejected.

Delivery logs and redelivery

Use the Logs tab in Settings or the SDK:

const page = await speko.webhooks.deliveries.list({
  endpointId: endpoint.id,
  event: 'call.report',
  status: 'failed',
  limit: 50,
});

const detail = await speko.webhooks.deliveries.get(page.data[0]!.id);
await speko.webhooks.deliveries.redeliver(detail.id);

Logs contain the endpoint-specific request, sanitized URL and headers, HTTP result, duration, bounded response, error, routing tags, and chronological attempts. They are retained for 30 days. Private phone-number API deliveries for imessage.received, imessage.reaction_received, imessage.sent, imessage.delivered, and imessage.delivery_failed appear in the same Logs tab and API, using the stable Inkbox event ID.

Manual redelivery is available for unexpired workspace lifecycle events except call.pre_call. It uses the stored original payload and webhook ID with the endpoint's current URL and credentials, and adds an attempt to the existing delivery. A manual failure does not create a new automatic retry or cancel one that was already pending; after an earlier success, the delivery remains successful. Inkbox owns retry scheduling for imessage.* subscriber deliveries, so those records are view-only.

Migrating agent webhooks

Existing agent webhook configuration is backfilled to central endpoints and dispatch reads only those endpoints, avoiding duplicates. agent.webhooks reads and writes remain supported through the current SDK major version; legacy writes synchronize legacy-managed central endpoints. New integrations should use speko.webhooks exclusively.

On this page