# Destinations

A destination is where events are delivered: an HTTPS URL with its own queue, retries and rate limit.

A destination is the service that should receive events. Ensue keeps a separate queue for every destination, so a slow or failing service never holds up another one.

There are two kinds:

- **URL destination.** Ensue sends each event to an HTTPS URL as a `POST`. This page covers it.
- **Inbox.** Events wait for an agent to pull them. See [Inbox and leases](/agents/inbox-and-leases/).

## Create a destination

Open **Destinations → New destination**, or `POST /v1/destinations` ([reference](/api-reference/create-a-destination/)):

```bash
curl -X POST https://api.getensue.com/v1/destinations \
  -H "authorization: Bearer ens_<key>" -H "content-type: application/json" \
  -d '{
    "name": "billing-api",
    "url": "https://api.example.com/webhooks/ensue",
    "auth_type": "bearer",
    "token": "…",
    "timeout_ms": 10000,
    "max_in_flight": 5
  }'
```

| Field | Notes |
|---|---|
| `url` | An HTTPS URL. Ensue refuses URLs that would loop back into itself. |
| `auth_type` | How Ensue authenticates to **your** service: `none`, `bearer` (with `token`), `basic` (`username`, `password`) or `apikey` (`header`, `value`). Stored secrets are never shown again. |
| `timeout_ms` | Per-attempt timeout, 1,000 to 30,000. Default 15,000. A timeout counts as a failed attempt. |
| `max_attempts`, `retry` | The [delivery policy](/gateway/delivery-policy/). |
| `max_in_flight`, `rate_per_second` | Concurrency and rate caps. |
| `groups` | [Delivery groups](/gateway/delivery-groups/): per-tenant rates. |

## What your service receives

The request body is the event body, after any [transformation](/gateway/transformations/). Ensue adds:

| Header | Value |
|---|---|
| `x-ensue-event-id` | The event ID. The same on every retry. |
| `idempotency-key` | The event ID again; for a [replay](/gateway/replay-and-dead-letters/), the ID of the event being replayed. |
| `x-ensue-request-id` | The request the event came from. |
| `x-ensue-timestamp` | Unix seconds when this attempt was sent. |
| `x-ensue-signature` | `t=<timestamp>,v1=<hex>`, an HMAC-SHA256 of `"<timestamp>.<body>"` with the destination's signing secret. |
| `x-ensue-replay-of` | Present on replays. The original event ID. |

Original sender headers are forwarded, except hop-by-hop headers and proxy headers added in transit.

### Answering Ensue

| Your response | What Ensue does |
|---|---|
| `2xx` | The event is **delivered**. |
| `429`, `503` with `Retry-After` | Retries, no sooner than the header says. |
| `408`, `409`, `425`, `429`, `5xx`, timeout, network error | Retries on the schedule. |
| Other `4xx` | Fails immediately: a request your service rejected will not succeed on a retry. |
| `3xx` | Fails. Redirects are never followed. |
| `Retry-After: -1` or `x-ensue-retry: never` | Stops retrying that event at once. |

You can widen or narrow which responses retry with `retry.retry_on`. See [Delivery policy](/gateway/delivery-policy/).

## Pause, probe and reset

- **Pause** a destination to hold its events. Nothing is lost and nothing is retried until you resume.
- **Probe** (`POST /v1/destinations/:id/probe`) sends a test request to confirm the URL responds.
- A destination that keeps failing is stopped by its [circuit breaker](/gateway/circuit-breaker/); changing its URL resets it.

A destination used by a flow or an active subscription cannot be deleted (`409 in_use`).

> [!IMPORTANT]
> Your handler can see an event more than once. Make it idempotent by keying on `x-ensue-event-id`.