# Send events

Post an event once and let Dispatch fan it out to every subscribed customer endpoint.

Sending is one authenticated `POST`. Dispatch checks the event, finds the endpoints that should receive it, and queues one delivery for each.

```bash
curl -X POST https://api.getensue.com/v1/dispatch/events \
  -H "authorization: Bearer ens_live_…" -H "content-type: application/json" \
  -H "idempotency-key: order-1042-shipped" \
  -d '{
    "type": "order.shipped",
    "customer": "acme",
    "data": { "order_id": "1042", "carrier": "UPS" }
  }'
```

## Request

| Field | Notes |
|---|---|
| `type` | **Required.** An existing, non-archived [event type](/dispatch/event-types/). |
| `data` | **Required.** A JSON object, validated against the type's current schema. |
| `customer` | Send only to this customer's endpoints. Your `external_id` or a `cst_…` ID. |
| `endpoint_id` | Send only to this endpoint (`dst_…`). It must be active and subscribed to the type. |
| `idempotency_key` | Up to 200 characters. May also be sent as an `Idempotency-Key` header. |

## Response

`202 Accepted` once the message and its deliveries are stored:

```json
{
  "id": "msg_…",
  "event_type": "order.shipped",
  "version": 1,
  "customer": "acme",
  "idempotency_key": "order-1042-shipped",
  "delivery_count": 2,
  "created": 1767225600000
}
```

`202` means Ensue has the message durably. The deliveries are attempted straight afterwards and retried if they fail, so you never need to retry a send that was accepted. `delivery_count` is how many endpoints it was queued for; it can be `0` when nothing subscribes.

## Who receives it

| You send | Delivered to |
|---|---|
| `customer` only | That customer's active endpoints subscribed to the type |
| nothing but `type` | Every customer's active endpoints subscribed to the type |
| `endpoint_id` | Just that one endpoint, which must be subscribed |

Paused endpoints are skipped. Sending to a paused endpoint **by ID** fails with `409 endpoint_paused`, so a targeted send never silently goes nowhere.

## What the endpoint receives

The delivered body is an envelope around your `data`:

```json
{
  "id": "msg_…",
  "type": "order.shipped",
  "version": 1,
  "timestamp": "2026-11-10T09:30:00.000Z",
  "data": { "order_id": "1042", "carrier": "UPS" }
}
```

Along with the [Standard Webhooks headers](/dispatch/verify-signatures/) `webhook-id`, `webhook-timestamp` and `webhook-signature`. The `webhook-id` is the message ID and is the **same on every retry**, so customers can de-duplicate on it.

## Idempotency

Network failures happen between your app and Ensue, too. Send an idempotency key that is stable for the thing that happened, such as the order ID plus the event name, so you can retry a send safely.

- The same key with the **same request** answers `200` with the original message and `"duplicate": true`. Nothing is sent twice.
- The same key with a **different** request answers `409 idempotency_key_reused`, so a reused key can never silently swallow a different event.
- Two simultaneous sends with one key resolve to one message.

## Validation errors

A body that doesn't match the schema answers `422`:

```json
{
  "ok": false,
  "error": "invalid_data",
  "message": "data doesn't match order.shipped v1: Required property 'order_id' not found",
  "errors": [{ "path": "/", "message": "Required property 'order_id' not found" }]
}
```

Nothing is sent and nothing is counted. See [Limits and errors](/dispatch/limits-and-errors/) for every response.

> [!TIP]
> Send from the same transaction that records the change, using the record's ID in the idempotency key. If your process crashes and re-runs, the retry is harmless.