*** title: Webhooks subtitle: Real-time event notifications for resource changes slug: webhooks -------------- ## Overview AskElephant delivers real-time webhook notifications when resources change in your workspace. Events are emitted consistently regardless of how the change originated — the web app, API writes, background jobs, or imports. Webhook delivery is managed by [Svix](https://www.svix.com), providing automatic retries with exponential backoff, delivery logging, and endpoint management through a self-service portal. ## Setup 1. Navigate to **Settings > Webhooks** in the AskElephant dashboard 2. The embedded Svix App Portal lets you: * Add webhook endpoints (your receiving URL) * Select which event types to subscribe to * View delivery history and debug failed deliveries * Rotate endpoint signing secrets * Send test events No API keys or engineering setup required — any workspace owner can configure webhooks directly. ## Event types All event types are versioned with a `v1.` prefix. Standard resources emit `created`, `updated`, and `deleted` events. One special event fires when a transcript finishes processing. | Event type | Trigger | | --------------------------------------------- | ----------------------------------------------- | | `v1.contact.created` | A contact is created | | `v1.contact.updated` | A contact is updated | | `v1.contact.deleted` | A contact is deleted | | `v1.company.created` | A company is created | | `v1.company.updated` | A company is updated | | `v1.company.deleted` | A company is deleted | | `v1.engagement.created` | An engagement is created | | `v1.engagement.updated` | An engagement is updated | | `v1.engagement.deleted` | An engagement is deleted | | `v1.tag.created` | A tag is created | | `v1.tag.updated` | A tag is updated | | `v1.tag.deleted` | A tag is deleted | | `v1.engagement.transcript_timeline.completed` | An engagement transcript is ready for retrieval | ## Payload envelope Every webhook delivery uses a consistent envelope structure: ```json { "id": "evt_01JWAS...", "type": "v1.contact.updated", "api_version": "v1", "created_at": "2026-03-09T12:00:00.000Z", "workspace_id": "wrks_01JWAS...", "data": { "object": { }, "previous_attributes": { } } } ``` | Field | Type | Description | | -------------------------- | ------ | ------------------------------------------------------------------------------------------ | | `id` | string | Unique event identifier | | `type` | string | The event type (see table above) | | `api_version` | string | API version, currently `v1` | | `created_at` | string | ISO 8601 timestamp of when the event was emitted | | `workspace_id` | string | The workspace that owns the resource | | `data.object` | object | Full current state of the resource, matching the REST API response shape | | `data.previous_attributes` | object | Only present on `updated` events. Contains the **previous values** of fields that changed. | ## Delivery headers Every webhook request also includes Svix signature headers alongside the JSON body: | Header | Description | | ---------------- | ------------------------------------------------ | | `svix-id` | Unique identifier for the delivery attempt | | `svix-timestamp` | Unix timestamp used when computing the signature | | `svix-signature` | Signature for the raw request payload | HTTP header names are case-insensitive, but the examples in this guide use Svix's canonical lowercase header names. ## Resource payloads The `data.object` field matches the corresponding REST API response for each resource type. ### Contact ```json { "object": "contact", "id": "cnt_01JWAS...", "first_name": "Jane", "last_name": "Doe", "emails": [ { "email": "jane@example.com", "is_primary": true } ], "phone_numbers": [ { "phone_number": "+1234567890", "is_primary": true } ], "crm_association": { "object_type": "contact", "crm_object_id": "sf_003ABC...", "source": "salesforce" }, "created_at": "2026-03-01T10:00:00.000Z", "updated_at": "2026-03-09T12:00:00.000Z" } ``` Optional fields (`description`, `company`, `time_zone`, `crm_association`) are included only when they have a value. ### Company ```json { "object": "company", "id": "cmp_01JWAS...", "name": "Acme Corp", "domains": [ { "domain": "acme.com" } ], "crm_association": { "object_type": "account", "crm_object_id": "sf_001ABC...", "source": "salesforce" }, "created_at": "2026-03-01T10:00:00.000Z", "updated_at": "2026-03-09T12:00:00.000Z" } ``` Optional fields (`description`, `industry`, `website`, `number_of_employees`, `logo_url`, `crm_association`) are included only when they have a value. ### Engagement ```json { "object": "engagement", "id": "ngmt_01JWAS...", "title": "Q1 Pipeline Review", "engagement_type": "MEETING", "data_source": "ZOOM", "processing_status": "COMPLETED", "is_internal": false, "engagement_at": "2026-03-09T14:00:00.000Z", "start_at": "2026-03-09T14:00:00.000Z", "end_at": "2026-03-09T14:45:00.000Z", "duration_seconds": 2700, "owner_user_id": "usr_01JWAS...", "created_at": "2026-03-09T14:00:00.000Z", "updated_at": "2026-03-09T15:00:00.000Z" } ``` Optional fields (`description`, `meeting_url`, `host_user_id`) are included only when they have a value. ### Tag ```json { "object": "tag", "id": "tag_01JWAS...", "name": "churn-risk", "hex_color": "#FF5733", "description": "Customer showing signs of churn", "auto_tagging_enabled": false, "created_at": "2026-03-01T10:00:00.000Z", "updated_at": "2026-03-09T12:00:00.000Z" } ``` ## Behavior details ### `previous_attributes` on update events When a resource is updated, the `data.previous_attributes` object contains only the fields that changed, with their **pre-update** values. Fields that did not change are omitted. ```json { "type": "v1.contact.updated", "data": { "object": { "object": "contact", "id": "cnt_01JWAS...", "first_name": "Jane", "last_name": "Doe", "updated_at": "2026-03-09T12:00:00.000Z" }, "previous_attributes": { "first_name": "Janet" } } } ``` Changes to relation fields (`emails`, `phone_numbers`, `domains`) that do not modify the parent resource will not emit a webhook event. To detect these changes, poll the REST API or listen for the parent resource's next `updated` event. ### Delete events When a resource is deleted, the `data.object` contains the full resource state at the time of deletion, including relation fields (`emails`, `phone_numbers`, `domains`). No `previous_attributes` is included on delete events. ### Transcript timeline completion The `v1.engagement.transcript_timeline.completed` event fires once when the engagement transcript has been persisted and is available for retrieval. The `data.object` is the full engagement (not the transcript timeline). Note that additional processing (AI analysis, enrichment) may still be in progress after this event. Use the engagement `id` from the payload to fetch the transcript via `GET /v2/engagements/{id}/transcript_timeline`. **Recommended pattern:** 1. Receive `v1.engagement.transcript_timeline.completed` event 2. Extract `data.object.id` (the engagement ID) 3. Call `GET /v2/engagements/{id}/transcript_timeline` to fetch the full transcript with speaker details, sentiment, and timing ## Delivery guarantees Webhook delivery is managed by Svix, which provides: * **Automatic retries** with exponential backoff on failed deliveries * **Delivery logging** visible in the Svix App Portal * **Endpoint signing** so you can verify webhook authenticity * **At-least-once delivery** — your endpoint should be idempotent * **Non-blocking** — webhook delivery failures never block the operation that triggered the event ## Verifying webhook signatures Each webhook delivery is signed by Svix. Verify the request before processing it to ensure the payload is authentic and has not been tampered with. **Recommended verification flow:** 1. Read the `svix-id`, `svix-timestamp`, and `svix-signature` headers from the incoming request. 2. Use the exact raw request body bytes or string as received over HTTP. Do not reserialize the parsed JSON before verification. 3. Verify the request with the endpoint secret shown in the Svix App Portal for that webhook endpoint. 4. Only parse and process the event after signature verification succeeds. See the [Svix verification documentation](https://docs.svix.com/receiving/verifying-payloads/how) for language-specific examples.