> ## Documentation Index
> Fetch the complete documentation index at: https://docs.truagents.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks

> Request format, payload fields, response handling, retries, and security for the Send webhook workflow step.

## Summary

A webhook is an HTTPS request that TruAgents sends to a URL you choose the moment something happens, so your system doesn't have to keep asking.

Today, TruAgents sends webhooks from one place: the **Send webhook** step in a [workflow](/automations/overview). When a workflow reaches that step, TruAgents sends one `POST` with a JSON description of the workflow, the contact, and the inbound communication that started it.

This page is the technical contract for receivers. For the in-app setup, see [Send webhook step](/automations/send-webhook-step). For a complete CRM example, see [Send hot leads to Salesforce](/automations/hot-leads-to-salesforce).

## Who this is for

Developers and technical admins building or configuring the endpoint that receives TruAgents webhooks.

## The request

| Property | Value |
| - | - |
| Method | `POST` |
| URL | The **Endpoint URL** configured on the step. Must be `https://` and resolve to a public address. |
| `Content-Type` | `application/json`, unless you configure your own `Content-Type` header on the step. |
| Extra headers | Every header configured on the step, sent as-is. |
| Body | One JSON object, described next. |
| Redirects | Not followed. A `3xx` response counts as a rejection. |

### Example body

```json theme={null}
{
  "id": "evt_1fa0308c9f2262fdaa5a1df703bfa3c8",
  "event": "workflow.step.webhook_sent",
  "timestamp": "2026-10-01T20:45:17Z",
  "data": {
    "workflow": {
      "id": "cmupz6h610006l4we5wbu7xd3",
      "name": "Hot lead → Salesforce",
      "run_id": "cq5xcdvh9m69rzqg8azxe0eb",
      "_links": {
        "self": "https://app.truagents.com/workflows/cmupz6h610006l4we5wbu7xd3/history?run=cq5xcdvh9m69rzqg8azxe0eb"
      }
    },
    "contact": {
      "id": "s6x3d0am6tofim3bmeaequ1u",
      "name": "Nathaniel Wisoky",
      "email": "nathaniel@example.com",
      "phone": "+16503775420",
      "_links": {
        "self": "https://app.truagents.com/contacts/s6x3d0am6tofim3bmeaequ1u"
      }
    },
    "communication": {
      "id": "x8f2kq0n3m5r7t9v1w4y6z0a",
      "channel": "email",
      "subject": "Enterprise pricing + demo this week?",
      "preview": "Hi there — we are evaluating vendors this week and your platform is on our shortlist. Could you send enterprise pricing for 40 seats and book a demo with our VP of Sales on Thursday afternoon?",
      "_links": {
        "self": "https://app.truagents.com/communications/x8f2kq0n3m5r7t9v1w4y6z0a"
      }
    }
  }
}
```

### Field reference

| Field | Type | Description |
| - | - | - |
| `id` | string | Event id, `evt_` followed by 32 hex characters. The same for every delivery attempt of the same step in the same run, so you can de-duplicate on it. |
| `event` | string | Always `workflow.step.webhook_sent`. |
| `timestamp` | string | When this delivery attempt was built, in UTC (RFC 3339). It can differ between attempts. |
| `data.workflow.id` | string | The workflow that fired. |
| `data.workflow.name` | string | The workflow name at the time of the run. |
| `data.workflow.run_id` | string | One run per workflow per inbound communication. |
| `data.workflow._links.self` | string | The run in the workflow's **History** tab, including the AI's reasoning for any **Look for** step. |
| `data.contact.id` | string | The TruAgents contact id. Stable across communications from the same contact. |
| `data.contact.name` | string | The full name as one string. TruAgents doesn't send separate first and last names. |
| `data.contact.email` | string | The contact's email address. |
| `data.contact.phone` | string | The contact's phone number in E.164 format. |
| `data.contact._links.self` | string | The contact in TruAgents. |
| `data.communication.id` | string | The inbound email, SMS, or call that started the workflow. |
| `data.communication.channel` | string | `email`, `sms`, or `phone`. |
| `data.communication.subject` | string | The subject line. Emails only. |
| `data.communication.preview` | string | The first 280 characters of the message. For calls, the first 280 characters of the transcript, with `Agent:` and `Contact:` speaker labels. |
| `data.communication._links.self` | string | The communication in TruAgents. |

Fields with no value are left out rather than sent empty. Your parser should treat every field under `data.contact` and `data.communication` as optional.

The payload doesn't include the AI's decision details (reason or confidence), the tags applied by earlier steps, or a company name. Follow `data.workflow._links.self` to see the decision in TruAgents.

## Responding

* Return any `2xx` status to confirm delivery. TruAgents ignores the response body.
* Respond within **30 seconds**. A slower response counts as a timeout.
* Reply `2xx` only after the event is safe: either the work is done, or the event is saved to a durable queue or database that your own worker retries. If you reply first and the work then fails, TruAgents won't send the event again.

## Failures and retries

| What your endpoint does | What TruAgents does |
| - | - |
| Returns `2xx` | Marks the step **Succeeded** ("Posted webhook to `<host>`") and continues the workflow. |
| Returns `3xx`, or `4xx` other than `408` / `429` | Marks the step **Failed** with "The webhook endpoint rejected the request". No retry. |
| Returns `5xx`, `408`, or `429`, times out, or can't be reached | Treats it as temporary and tries again, as described next. |

### Retry schedule

After a temporary failure, TruAgents sends the same request again roughly every 5 to 10 minutes, for up to 24 hours from when the workflow run started. Every attempt carries the same `id`.

* If an attempt returns `2xx`, the step is marked **Succeeded** and the workflow carries on from there. Steps that already ran before the webhook aren't repeated.
* If an attempt returns a non-retryable status (for example `400` or `401`), the step is marked **Failed** and the retries stop.
* If the endpoint is still failing after 24 hours, the run is marked **Failed** and TruAgents stops trying.

<Warning>
  While a webhook is being retried, the run stays **Running**. For that communication, any later workflows and the automatic reply wait until the webhook succeeds, is rejected, or reaches the 24-hour limit. Fix outages on your endpoint quickly. For requests that will never succeed, return a `4xx` other than `408` or `429`, not a `5xx`.
</Warning>

TruAgents also refuses to send, and marks the step **Failed** without retrying, when the URL isn't `https://` ("Webhook URL must use HTTPS") or when the host resolves to a private, loopback, or internal address ("Webhook URL is not allowed").

### Choosing your status codes

* Return `2xx` once you have safely stored or processed the event.
* Return `5xx` when the failure is on your side and a later attempt could succeed (for example, your CRM is down).
* For a request that can never succeed, such as a bad token or a payload you refuse, use a `4xx` status other than `408` or `429`. TruAgents stops trying. `408` and `429` are temporary, and TruAgents retries them.

## Duplicates and ordering

Delivery is **at least once**. In rare cases, such as a timeout after your endpoint already processed the request, you receive the same event more than once.

* De-duplicate on `id`. It's the same across every attempt of the same step in the same run.
* Or make your write idempotent. For example, upsert on `data.contact.id` or the contact's email instead of always creating a record.
* Don't rely on ordering. Two communications from the same contact can arrive in either order.

## Security

* **HTTPS only, public hosts only.** TruAgents won't deliver to `http://` URLs, IP literals in private ranges, `localhost`, or internal hostnames.
* **Header values aren't secret.** Headers are stored with the workflow and anyone in your organization who can open the workflow can read them. Never put CRM credentials, API keys for other systems, or passwords in a header.
* **No request signing yet.** TruAgents doesn't sign webhook requests today. To make sure a request came from your workflow, use one of these:
  * an endpoint URL that is long and random (no-code tools like Zapier and Make generate these), or
  * a dedicated, low-privilege token in a header (for example `X-Relay-Token`) that your endpoint checks and that unlocks nothing else. Rotate it when people leave.
* **Personal data.** The payload contains the contact's name, email, phone, and the start of their message. Send it only to systems your company controls or has a data processing agreement with.

## Where to see delivery results

Open the workflow, then the **History** tab, and click a run.

| What happened | What the run shows |
| - | - |
| Your endpoint returned `2xx` | The **Send webhook** step is **Succeeded** with "Posted webhook to `<host>`". |
| Your endpoint rejected the request (`3xx` or `4xx`), or the URL was refused | The step is **Failed** with the reason. |
| TruAgents is retrying (`5xx`, `408`, `429`, timeout, unreachable) | The run is **Running**. Individual attempts and their status codes aren't listed in TruAgents today. |
| Retries ran out after 24 hours | The run is **Failed**. |

Because TruAgents doesn't show individual attempts yet, keep your own record on the receiving side. Zapier's **Zap history** and Make's **History** tab list every request they receive, and a custom endpoint should log the `id`, the status code it returned, and any error.

<img src="https://mintcdn.com/truagents/7Y0lE5__31S8Ok9L/images/truagents-workflows-history-webhook-succeeded.png?fit=max&auto=format&n=7Y0lE5__31S8Ok9L&q=85&s=8febe257e4ced32b454671db9a1399ca" alt="The History tab showing a succeeded Send webhook step" width="2880" height="1800" data-path="images/truagents-workflows-history-webhook-succeeded.png" />

## Related pages

* [Send webhook step](/automations/send-webhook-step)
* [Send hot leads to Salesforce](/automations/hot-leads-to-salesforce)
* [Workflow automations overview](/automations/overview)
* [Rate limits and retries](/developers/rate-limits-and-retries) — for calls you make *to* TruAgents.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.