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

# Webhooks

Spara's Webhook support completely customizable configurations.

The Spara platform offers powerful integration capabilities through both outbound webhooks and a [Web API](/developers/spara-api/web-api.md).

Not sure whether a webhook is the right tool? See [https://docs.spara.com/guides/webhook-web-api-or-workflow-api-step](https://docs.spara.com/guides/webhook-web-api-or-workflow-api-step "mention") for a side-by-side comparison.

Webhooks provide real-time notifications of lead events via `POST` requests to a customer-defined endpoint, enabling automated workflows, external system synchronization, and timely responses to key updates. APIs allow customers to update and enrich lead data, manage user information, and influence the behavior of our AI engine - supporting deep, flexible integrations. All API interactions must be conducted over `HTTPS` to ensure secure communication.

Both webhooks and API support basic data types via JSON. These types are not explicitly identified in request values.

* `string`, ex. `"John Doe"`
* `integer`, ex. `100`
* `boolean`, i.e. `true` or `false`

Malformed values will be rejected, ex. `John Doe`. Take care to not cast integer or boolean values as strings, ex. `"100"`.

{% hint style="warning" %}
Important: Spara does not recommend using webhooks to update information also being updated by a native integration, as this may result in data conflicts or unexpected behavior.

For example, using information from webhooks to update Salesforce records in conjunction with Spara's native Salesforce integration.
{% endhint %}

### Schema

Spara models leads as a single object with any number of arbitrary fields. For example, if you configure Spara to ask your leads the question "What industry is your company in?" you may decide to name that field `industry`.

The following fields are standard for all leads:

```
# Info about the lead.
email(string)
company_name (string)
first_name (string)
last_name (string)
phone_number (string)
job_title (string)
num_employees (integer)
```

In addition, some leads may have an assigned sales rep in Salesforce:

```
# Info about Salesforce objects connected to the lead. All fields are strings.
sales_rep_name
salesforce_account_id- The unique identifier of the Salesforce account
salesforce_account_name - The name of the Salesforce account which usually represents the company name
salesforce_account_owner_email - The email address of the Salesforce account owner (account's default sales rep)
salesforce_account_owner_id - The unique identifier of the Salesforce account owner
salesforce_account_owner_name - The first & last names of the Salesforce account owner
salesforce_account_website - The website URL associated with the Salesforce account
salesforce_assigned_sales_rep_email - The email address of the lead's assigned sales rep
salesforce_assigned_sales_rep_id - The unique identifier of the lead's assigned sales rep
salesforce_assigned_sales_rep_name
```

### Webhook Registration & Events

Each customer can register one or more webhook endpoints in the Settings > [Webhooks](https://app.spara.co/settings/api-webhooks) page. Use "Add Webhook" to register additional endpoints, and the trash icon to remove one. Each endpoint has its own URL and its own set of subscribed events, so you can route different events to different destinations.

The following information is required to register a webhook:<br>

* **Webhook URL**: A valid `HTTPS` endpoint where webhook payloads will be sent. Test payloads may be triggered by clicking the "Test" button.
* **API Key(s)**: Customers must create at least one API Key to enable outbound webhooks. API Keys are shared across all of an organization's webhook endpoints.
* **Events:** Select which events trigger webhook notifications for that endpoint.
  * See table below for all event types.
  * NB: If multiple events are triggered at the same time, they are grouped into a single webhook call.
  * Each endpoint only receives the events it is subscribed to; an event is delivered to every endpoint subscribed to it.

<table data-full-width="true"><thead><tr><th>Event Name</th><th>When the webhook event is triggered</th><th>Example</th></tr></thead><tbody><tr><td><code>Lead Created</code></td><td>A new lead is created.</td><td></td></tr><tr><td><code>Spara Fact Updated</code></td><td>Spara learns a new fact from conversing with the lead.</td><td>A lead sends the message "My email is joe@test.com."</td></tr><tr><td><code>External Fact Updated</code></td><td>Spara learns of a new fact via query parameters or API.<br><br>NB: Will not trigger when a new lead is created if <code>Lead created</code> is not selected.</td><td></td></tr><tr><td><code>Lead Enrichment Fact Updated</code></td><td>Spara learns of a new fact via lead enrichment.</td><td></td></tr><tr><td><code>Lead Message Sent</code></td><td>The lead sends a message.</td><td></td></tr><tr><td><code>AI Message Sent</code></td><td>Spara Chat AI sends a message.</td><td></td></tr><tr><td><code>Human Message Sent</code></td><td>Human uses "Manual Mode" to send a message to the lead.</td><td></td></tr><tr><td><code>Voice Call Ended</code></td><td>A voice call with the lead ends. The payload includes call timestamps, the Voice agent that handled the call, a short summary, and the transcript.</td><td></td></tr></tbody></table>

<figure><img src="https://4187726466-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCvqVvd2zN1NKClKTrAOy%2Fuploads%2Fgit-blob-e02b3c8a77817a4221fbc6f119045dc93e7d0036%2Fimage.png?alt=media" alt=""><figcaption></figcaption></figure>

### Payload events and channel sources

Every payload carries two fields that together tell you what happened and where:

* `event` — the payload event type: `lead.created`, `lead.updated`, or `lead.call_transferred`.
* `source` — the channel that produced the event: `chat`, `voice`, or `email`.

The event names you subscribe to in Settings do not map one-to-one onto payload event types. Several subscriptions produce `lead.updated`, so filter on `source` (and on which fields changed) rather than on `event` alone.

| Event (in Settings)            | Payload `event`         | `source`        |
| ------------------------------ | ----------------------- | --------------- |
| `Lead Created`                 | `lead.created`          | `chat`, `voice` |
| `Spara Fact Updated`           | `lead.updated`          | `chat`, `email` |
| `Spara Fact Updated`           | `lead.call_transferred` | `voice`         |
| `External Fact Updated`        | `lead.updated`          | `chat`, `email` |
| `Lead Enrichment Fact Updated` | `lead.updated`          | `chat`          |
| `Lead Message Sent`            | `lead.updated`          | `chat`          |
| `AI Message Sent`              | `lead.updated`          | `chat`          |
| `Human Message Sent`           | `lead.updated`          | `chat`          |
| `Voice Call Ended`             | `lead.updated`          | `voice`         |

{% hint style="info" %}
`lead.call_transferred` fires when a Voice agent transfers a live call to a human. It is delivered to endpoints subscribed to `Spara Fact Updated` — there is no separate subscription for it.
{% endhint %}

### Delivery Format

Each webhook is sent as a JSON payload in the body of an HTTP `POST` request. Every delivery contains the **full lead object** — the same object returned by [Web API](/developers/spara-api/web-api.md) — regardless of which event triggered it. Spara does not send a partial "what changed" diff, so your handler is responsible for comparing against its own last-known state if it needs to detect a specific field change.

The top-level keys are always present:

| Key                         | Description                                                                                               |
| --------------------------- | --------------------------------------------------------------------------------------------------------- |
| `event`                     | `lead.created`, `lead.updated`, or `lead.call_transferred`                                                |
| `timestamp`                 | When the event fired                                                                                      |
| `source`                    | `chat`, `voice`, or `email`                                                                               |
| `object`                    | Always `lead`                                                                                             |
| `id`                        | The lead's unique identifier                                                                              |
| `url`                       | Link to the conversation in Spara                                                                         |
| `created_at` / `updated_at` | When the lead was created and last modified                                                               |
| `last_activity_at`          | The lead's most recent activity (`null` if the lead has never been active)                                |
| `data`                      | All known fields on the lead, including custom fields                                                     |
| `account`                   | The lead's account, with the account's `owner` nested inside (`null` if the lead has no account)          |
| `owner`                     | The lead's assigned owner (`null` if unassigned)                                                          |
| `conversation`              | The chat conversation and its full message history (`{}` if the lead has no chat thread)                  |
| `calls`                     | **All** voice calls on the lead, not just the most recent. Always present; empty array if there are none. |

```json
POST /your-endpoint
Content-Type: application/json
X-API-Key: <API Key>

{
  "event": "lead.updated",
  "timestamp": "2026-08-28T16:59:31Z",
  "source": "voice",

  "object": "lead",
  "id": "GTvcBRZd",
  "url": "https://app.spara.co/conversations/GTvcBRZd",
  "created_at": "2026-08-28T16:50:02Z",
  "updated_at": "2026-08-28T16:59:31Z",
  "last_activity_at": "2026-08-28T16:59:31Z",

  "data": {
    "email": "jane@test.com",
    "first_name": "Jane",
    "is_interested": true,
    "ip_country": "US",
    "current_url": "https://www.my-site.co/chat/jJyVt7TQ5"
  },

  "account": {
    "company_name": "Test Co",
    "website": "https://test.co",
    "industry": "Software",
    "employee_count": 250,
    "annual_revenue": 40000000,
    "billing_city": "Denver",
    "crm_source": "SALESFORCE",
    "crm_id": "0011U00000XyZaBQAV",
    "owner": {
      "email": "rep@you.com",
      "full_name": "Alex Rivera",
      "role": "AE",
      "is_active": true,
      "crm_source": "SALESFORCE",
      "crm_id": "0051U00000AbCdEQAV"
    }
  },

  "owner": {
    "email": "rep@you.com",
    "full_name": "Alex Rivera",
    "role": "AE",
    "is_active": true,
    "crm_source": "SALESFORCE",
    "crm_id": "0051U00000AbCdEQAV"
  },

  "conversation": {
    "created_at": "2026-08-28T16:50:02Z",
    "initial_url": "https://www.my-site.co/chat/jJyVt7TQ5",
    "device": "DESKTOP",          // most recent device used
    "initial_device": "DESKTOP",  // first device used
    "messages": [
      {
        "sent_by": "AI",
        "created_at": "2026-08-28T16:50:02Z",
        "text": "Hi, any questions I can help with?",
        "asset": "ACME Overview.mp4"
      },
      {
        "sent_by": "LEAD",
        "created_at": "2026-08-28T16:50:14Z",
        "text": "hi my name is Jane"
      },
      {
        "sent_by": "MANUAL",
        "created_at": "2026-08-28T16:52:38Z",
        "text": "Hi this is your sales rep. I am taking over the conversation."
      }
    ]
  },

  "calls": [
    {
      "started_at": "2026-08-28T16:56:47Z",
      "ended_at": "2026-08-28T16:59:30Z",
      "end_reason": "lead_hung_up",
      "summary": "The lead called interested in purchasing...",
      "call_direction": "INCOMING",
      "voice_agent_id": 42,
      "voice_agent_name": "Inbound Sales",
      "events": [
        { "type": "message", "sent_by": "AI", "text": "Thanks for calling.", "created_at": "2026-08-28T16:56:49Z" },
        { "type": "BOOKING", "data": { "success": true }, "created_at": "2026-08-28T16:58:02Z" }
      ],
      "transcript_data": [],
      "data": { "is_interested": true }
    }
  ]
}
```

Custom fields you have defined on the lead, account, or owner appear as additional keys alongside the standard ones shown above.

{% hint style="warning" %}
Because every delivery carries the lead's entire message and call history, payloads grow as a conversation continues. A long-running lead can produce large, repetitive deliveries. If you only need a subset of the data — or only need it when one particular field changes — consider a Workflow API step or the Web API instead. See [https://docs.spara.com/guides/webhook-web-api-or-workflow-api-step](https://docs.spara.com/guides/webhook-web-api-or-workflow-api-step "mention").
{% endhint %}

### Security

To ensure secure transmission, all webhook payloads include the customer's oldest active API Keys in the `X-API-Key` header. Customers should verify the API Key before trusting the contents of the webhook.

Webhook URLs must use `HTTPS`. Plain `HTTP` endpoints will be rejected during registration.

### Delivery Limits & Retry Policy

* **Timeout:** Spara waits up to **10 seconds** for your endpoint to respond. A slower response is treated as a failed delivery.
* **Retries:** If a delivery fails (a non-2xx response or a timeout), Spara retries with exponential backoff — after 2 seconds, then after 4 seconds. That is up to **2 retries in addition to the original attempt**. After the final attempt the delivery is dropped.
* **Per-lead rate cap:** A single lead can generate at most **20 webhook deliveries per minute**. Events beyond that cap are dropped and are *not* retried. A very fast-moving conversation subscribed to many events can hit this cap.

Design your endpoint to respond quickly, handle retries idempotently, and treat webhooks as best-effort rather than guaranteed delivery. If you need a guaranteed, reconcilable record, back webhooks with a periodic [Web API](/developers/spara-api/web-api.md#search-leads-get) sweep.

### Best Practices

* Validate the API Key of incoming webhook requests before processing.
* Respond to webhooks with a `200 OK` status as quickly as possible.
* Offload heavy processing to background jobs or message queues.
* Log and monitor webhook delivery attempts to aid in troubleshooting.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.spara.com/developers/spara-api/webhooks.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
