# Sources

Source: https://solidhooks.com/docs/sources

> What a source is, how its ingest URL works, and how to create, configure, disable and delete one.

A source is an inbound webhook endpoint. Each source has its own ingest URL, and that URL is all a webhook provider needs to send you events. Sources can also verify that a request really came from the provider before anything is stored or delivered.

Sources live inside a project. Everything a source receives becomes an [event](https://solidhooks.com/docs/events), and its [connections](https://solidhooks.com/docs/connections) decide where those events go.

## How it works

Creating a source gives you a `maskId` and an `ingestUrl`:

```text
https://sdhk.events/<maskId>
```

The provider's payload is accepted raw, as it sends it, with any content type and no wrapper object. The `maskId` is the credential: there is no other auth on the ingest URL.

The ingest URL is served from its own origin, separate from the management API. Posting a webhook to the API host returns 404.

Source ids start with `src_`. The `provider` you choose decides how the source verifies requests and which event type it reads by default.

## Providers

| Provider                                            | What it is                                                                                     |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `github`, `linear`, `shopify`, `nuvion`, `paystack` | HMAC presets.                                                                                  |
| `stripe`                                            | Stripe signature preset.                                                                       |
| `openai`, `anthropic`, `resend`                     | [Standard Webhooks](https://www.standardwebhooks.com) presets.                                 |
| `generic`                                           | Any other sender. You choose the verifier, or none.                                            |
| `email`                                             | Receives inbound email instead of HTTP webhooks. See [Email ingestion](https://solidhooks.com/docs/email-ingestion). |

For a preset provider, the provider fixes the whole signing scheme. You send only the secret.

## Verification at a glance

Name a preset provider and send only its secret:

```bash
curl -s "$API/v1/sources" \
  -H "authorization: Bearer $SDHK_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "GitHub events",
    "provider": "github",
    "verifierConfig": { "secret": "<the-webhook-secret-you-set-in-github>" }
  }'
```

`verifierType` and every scheme detail are filled in for you. A scheme value that differs from the preset is rejected, not ignored. Use `provider: "generic"` when you need full control.

Leave out the secret and `verifierType` (with an empty `verifierConfig`) and the source accepts everything unverified. A config that carries a secret SolidHooks cannot match to a verifier is rejected rather than stored unverified.

A request that fails verification gets an opaque `401`, and is stored as a `rejected` [event](https://solidhooks.com/docs/events) so you can see why. For every verifier type, replay windows and the `generic` HMAC options, see [Verification](https://solidhooks.com/docs/verification).

## Per-source settings

| Field                                             | What it does                                                                                                                      |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `name`                                            | Human-readable name, up to 100 characters.                                                                                        |
| `description`                                     | Optional free text, up to 500 characters.                                                                                         |
| `maxPayloadSizeKb`                                | Largest payload accepted, from 1 to 4096 KB. Defaults to 256.                                                                     |
| `idempotencyKeys`                                 | Dot-paths such as `headers.x-webhook-id` or `body.data.id` used to [deduplicate](https://solidhooks.com/docs/idempotency). Rejected on `email` sources. |
| `eventTypeField`                                  | Dot-path to the event type, `headers.<name>` or `body.<path>`. Connection filters match on it.                                    |
| `customResponseBody`, `customResponseContentType` | A static response returned to the sender. Intended for `generic` sources.                                                         |
| `rateLimit`                                       | Read-only. Requests per second, `0` means unlimited.                                                                              |

`eventTypeField` can be set on any provider. Known providers default it to their standard path, for example `headers.x-github-event` for GitHub and `body.type` for the three Standard Webhooks presets. Omitting it on create, or sending an empty string on update, resets it to the provider's default (`null` for `generic`).

`rateLimit` is an operational clamp that SolidHooks support sets. It is not writable through the API.

Some presets default `idempotencyKeys` when you send none. `openai` and `anthropic` use `["headers.webhook-id"]`, and `resend` uses `["headers.svix-id"]`. Anything you send, including an empty array, wins.

## Create a source

**API**

```bash
curl -s "$API/v1/sources" \
  -H "authorization: Bearer $SDHK_KEY" \
  -H 'content-type: application/json' \
  -d '{
    "name": "Scratch",
    "provider": "generic",
    "verifierConfig": {}
  }'
```

The response includes `id`, `maskId` and `ingestUrl`. Configure `ingestUrl` in the webhook provider.

**Dashboard**

1. Open **Sources** and click **New Source**.
2. Pick a provider and follow the wizard.
3. Click **Finish**, then copy the ingest URL from the source's overview.

## Update a source

**API**

```bash
curl -s -X PATCH "$API/v1/sources/$SOURCE_ID" \
  -H "authorization: Bearer $SDHK_KEY" \
  -H 'content-type: application/json' \
  -d '{ "data": { "name": "renamed" } }'
```

The fields go inside `data`. Send only the ones you are changing; an empty `data` object is a valid no-op.

`verifierType` and `verifierConfig` travel together. To touch verification, including rotating a secret, send both. A `verifierConfig` on its own is rejected.

Changing `provider` or `verifierType` needs a new credential, because a credential belongs to one provider under one scheme. Send the field the new verifier reads (`secret`, `password` or `header_value`), or set `verifierType: "noop"` to turn verification off.

To change a secret without an outage, use [secret rotation](https://solidhooks.com/docs/secret-rotation) instead.

**Dashboard**

1. Open the source and go to its settings.
2. Edit **General**, **Verification** or **Ingest**, then save.

Leave secret fields empty to keep the stored value. The API never returns it.

## Rotate the mask

`POST /v1/sources/{id}/rotate-mask` gives the source a new `maskId` and so a new ingest URL. The old URL stops working. For an `email` source the email address changes too, because it is derived from the `maskId`.

The dashboard has no control for this yet, so it is an API call. It needs a key with `source` set to read and write.

## Disable or enable

`POST /v1/sources/{id}/toggle-disabled` takes `{ "isDisabled": true }` or `false`. A disabled source rejects all incoming webhooks. Nothing is deleted, and enabling it again resumes processing.

In the dashboard, use the **Disable** and **Enable** buttons in the source's header.

## Delete a source

`DELETE /v1/sources/{id}` removes the source. In the dashboard, the delete confirmation warns that the source and all its connections are removed, and that this cannot be undone.

Deleting needs the delete permission: an organisation admin or owner, or an API key with `source` set to read and write. See [API keys](https://solidhooks.com/docs/api-keys).

> **The URL is the credential**
>
> Anyone who has the ingest URL can post to it. On a source with no verification, that means anyone can create events. Turn verification on where the provider supports it.

## Next steps

* [Verification](https://solidhooks.com/docs/verification) — every verifier and the replay window
* [Connections](https://solidhooks.com/docs/connections) — route a source to destinations
* [Events](https://solidhooks.com/docs/events) — what a source stores
* [Secret rotation](https://solidhooks.com/docs/secret-rotation) — change a verifier secret without dropping traffic