# Create a webhook

`POST https://api.signwithsigil.com/v1/webhooks`

Register a URL for Sigil to post events to. The response carries the signing secret, this once.

Store the `secret` where your receiver can read it: it is not shown again, though you can [roll it](/api-reference/webhooks/roll-secret). Then [send a ping](/api-reference/webhooks/ping-webhook) to check the receiver. The URL must be a public HTTPS address; private, local and link-local addresses are refused, both here and when the name is resolved at delivery time.

## Authorization

Header `Authorization: Bearer sgk_…` (or `X-Api-Key: sgk_…`).

## Body (application/json)

- `url` (string, required): Where to post, `https://` only, 500 characters at most. No user name, password or `#fragment`.
- `events` (array of strings): The [event types](/webhooks#events) to receive. Leave it out, or send `null` or `[]`, for every pact event. A `ping` is always delivered.
- `description` (string): A note on what the endpoint is for (100 characters at most).

## Response

`201 Created`: The new [webhook](/api-reference/objects/webhook), with its `secret`.

- `uid` (string): The endpoint's id: 8 letters and digits.
- `url` (string): Where events are posted: a public HTTPS URL.
- `description` (string | null): Your note on what it is for (100 characters at most).
- `events` (array of strings | null): The [event types](/webhooks#events) it receives, or `null` for every pact event. A `ping` is delivered whatever this says.
- `active` (boolean): Whether events are delivered to it. Switched off by you, by a `410 Gone` answer, or after 10 failed deliveries in a row; switch it on again with [Update a webhook](/api-reference/webhooks/update-webhook).
- `consecutive_failures` (integer): Deliveries given up on since the last success. Resets to 0 on the next success.
- `last_status` (integer | null): The HTTP status of the last attempt. `null` before the first attempt, and when the last attempt got no response.
- `date_last_success` (string (date-time) | null): When a delivery last got a `2xx`.
- `date_disabled` (string (date-time) | null): When it was switched off, while it is.
- `date_created` (string (date-time)): When it was added.
- `date_updated` (string (date-time)): When its settings or secret last changed.
- `secret` (string): The signing secret, `sgw_` and 40 letters and digits. Keep it where your receiver can read it. Only in the response to [Create a webhook](/api-reference/webhooks/create-webhook) and [Roll the secret](/api-reference/webhooks/roll-secret). It cannot be read back later.

## Errors

| Status | When |
|---|---|
| 400 | The body is not a JSON object; the URL is missing, not HTTPS, not public, or malformed; or an event is unknown. |
| 401 | The API key is missing, wrong or revoked. |
| 403 | The account's plan does not include API access (Pro and Business do). |
| 409 | The account already has 10 endpoints. |

Every error body is `{ "error": "message" }`.

## Example request

```bash
curl -X POST https://api.signwithsigil.com/v1/webhooks \
  -H "Authorization: Bearer $SIGIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/sigil/webhook",
    "events": [
      "pact.sealed",
      "pact.declined",
      "pact.voided",
      "pact.lapsed"
    ],
    "description": "Production CRM"
  }'
```

## Example response

```json
{
  "uid": "W8NQ3KTE",
  "url": "https://example.com/sigil/webhook",
  "description": "Production CRM",
  "events": [
    "pact.sealed",
    "pact.declined",
    "pact.voided",
    "pact.lapsed"
  ],
  "active": true,
  "consecutive_failures": 0,
  "last_status": 200,
  "date_last_success": "2026-10-21T09:12:46Z",
  "date_disabled": null,
  "date_created": "2026-10-20T13:58:21Z",
  "date_updated": "2026-10-20T13:58:21Z",
  "secret": "sgw_Qm4Vx7Kp2Lr9TcNb0YhWzS8EaDj3GuPo6RiK1tHfXe"
}
```
