# Sources

A source is the URL a sender posts webhooks to. Learn how to create one and what it does with each request.

A source represents a service that sends HTTP requests to Ensue: Stripe, GitHub, Shopify, a partner, or another system of yours. Each source has its own URL, its own verification settings and its own history.

```text
https://hooks.getensue.com/in/<token>
```

The token in the URL is unique to the source. Treat it as a secret, and use [verification](/gateway/verify-webhooks/) as the real protection.

## Create a source

In the dashboard, open **Sources → New source**. Over the API, `POST /v1/sources` ([reference](/api-reference/create-a-source/)):

```bash
curl -X POST https://api.getensue.com/v1/sources \
  -H "authorization: Bearer ens_<key>" -H "content-type: application/json" \
  -d '{
    "name": "stripe-prod",
    "description": "Stripe live webhooks",
    "auth_type": "hmac",
    "header": "stripe-signature",
    "secret": "whsec_…"
  }'
```

| Field | Notes |
|---|---|
| `name` | 2 to 41 lowercase letters, numbers or dashes, starting with a letter or number. Unique in the workspace. |
| `description` | Optional, up to 200 characters. |
| `auth_type` | How requests are verified. See [Verify incoming webhooks](/gateway/verify-webhooks/). Defaults to `none`. |
| `dedupe` | A path whose value identifies a duplicate. See [Deduplication](/gateway/deduplication/). |
| `correlate` | A path whose value [agents subscribe on](/agents/subscriptions/). |

## What a source does with a request

For every request, in this order:

1. Refuses methods it does not accept (`405`). The default is `POST`, `PUT` and `PATCH`.
2. Refuses the request if the workspace is over its monthly quota (`429`, with `Retry-After`).
3. Verifies it (`401` on failure).
4. Rejects bodies larger than the size limit (`413`).
5. Stores the request, creates one event per matching active flow, and answers `200`.

The `200` response is small and fixed:

```json
{ "received": true, "request_id": "req_…", "events": 2, "filtered": 1 }
```

`events` is how many events were created, and `filtered` how many flows skipped the request because of their [filter](/gateway/filters/). A request that matches no flow is still stored and still shows in the source's log.

## Handshakes are answered for you

Some senders check that an endpoint is alive before they deliver. Ensue replies correctly to the common ones, logs them, and creates no events: CloudEvents and Azure Event Grid validation, Amazon SNS subscription confirmation, and the verification requests from Slack, Zoom, Meta and WooCommerce.

## Test a source

**Send test event** on the source page, or `POST /v1/sources/:id/test`, sends a sample through the source's real flows without involving the sender. Test events do not match [agent subscriptions](/agents/subscriptions/).

`GET /v1/sources/:id/last-body` returns the most recent body the source received, which is the quickest way to get a real payload for building a [transformation](/gateway/transformations/).

## Pause or delete a source

Set `status` to `paused` to stop accepting requests without losing the URL. A source that still has a flow or an active subscription cannot be deleted (`409 in_use`); remove those first.

> [!NOTE]
> Requests and events are kept according to your plan's [retention](/gateway/usage-and-retention/).