---
id: webhooks-create
slug: /apis/webhooks/create
title: 'Create a Subscription'
description: 'Create a webhook subscription with POST ~/api/webhook.'
sidebar_label: 'Create'
sidebar_position: 4
tags: ['api', 'webhooks', 'create']
---

## Request

```text
POST ~/api/webhook
```

### Payload

```json
{
  "url": "https://externalsystem.com",
  "authorizationHeader": "Optional HTTP Authorization Header to send with each webhook",
  "secret": "Optional shared secret used to sign each webhook",
  "webhookId": "Webhook identifier",
  "enabled": true,
  "description": "Short description of the webhook subscription",
  "eventTypes": []
}
```

| Field                 | Type     | Required | Description                                                                                              |
| --------------------- | -------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `url`                 | string   | yes      | Where deliveries are `POST`ed. Must be reachable from the Agility instance.                               |
| `webhookId`           | string   | yes      | **Your** identifier for this subscription. Echoed in every delivery. Make it meaningful and unique.       |
| `eventTypes`          | array    | no       | What to be notified about. See [event types](/apis/webhooks/event-types). Omitting it is accepted — validation is skipped when the key is absent — but a subscription with no event types is not useful. |
| `notificationEmails`  | array    | no       | Addresses notified about delivery problems for this subscription.                                        |
| `enabled`             | boolean  | no       | Whether the subscription is live. Create disabled to stage a configuration.                              |
| `description`         | string   | no       | Free text: where this goes and why. Worth filling in — it is what a future administrator will read.      |
| `authorizationHeader` | string   | no       | Sent verbatim as the `Authorization` header on every delivery.                                            |
| `secret`              | string   | no       | Shared secret used to HMAC-sign each delivery. See [verifying the payload](/apis/webhooks/payload#verifying-the-payload). |

:::info Unknown keys are rejected — this endpoint is stricter than most

`api/webhook` validates the request body strictly and rejects **any property that is not
a member of the model**, answering `400` with `{ "errorMessage": … }`.

This applies to both create and update, and it is worth knowing because it is the opposite of
how several other Agility surfaces behave — the Bulk API, for instance, tolerates keys it does
not recognise. Do not assume a payload that works elsewhere will be accepted here.

:::

### Example

```bash title="Request"
curl -X POST "https://{host}/{instance}/api/webhook" \
  -H "Authorization: Bearer <access-token>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
        "url": "https://externalsystem.example.com/hooks/agility",
        "webhookId": "story-status-to-slack",
        "description": "Notifies #delivery when a Story status changes in the Sandbox project",
        "enabled": true,
        "authorizationHeader": "Bearer s3cr3t-token-the-receiver-checks",
        "secret": "a-long-random-string-used-for-hmac",
        "eventTypes": [
          {
            "type": "AssetChanged",
            "from": "Story",
            "attributes": ["Status"],
            "filter": ["Scope=$Scope"],
            "with": { "$Scope": "Scope:0" },
            "select": ["Name", "Number", "Status.Name", "Owners.Name"]
          }
        ]
      }'
```

## Response

```json title="200 OK"
{
  "id": "336d997f-d9fa-4612-80e8-9f41a4b41352",
  "webhookId": "Webhook identifier",
  "url": "https://externalsystem.com",
  "description": "Short description of the webhook subscription",
  "enabled": true,
  "eventTypes": [],
  "createdAt": "2018-09-27T20:28:41.3829451Z",
  "updatedAt": "2018-09-27T20:28:41.3829451Z",
  "createdBy": "Member:20"
}
```

| Field            | Description                                                                                       |
| ---------------- | ------------------------------------------------------------------------------------------------- |
| `id`             | **Server-assigned UUID.** This — not `webhookId` — is what update, delete and status take.         |
| `webhookId`      | The identifier you supplied.                                                                       |
| `createdBy`      | Oid token of the member who created the subscription.                                              |

The create response does not carry delivery-health fields — a subscription
that has never fired has no delivery history. For `retryCount` and
`lastResponseAt`, use [Query subscriptions](/apis/webhooks/query) or
[Delivery status](/apis/webhooks/status).

:::danger Two identifiers, and only one works in URLs
`id` is the server-generated UUID and is the **only** thing the
[update](/apis/webhooks/update), [delete](/apis/webhooks/delete) and
[status](/apis/webhooks/status) endpoints accept in their path. `webhookId` is your own label,
returned in deliveries so your handler can route them.

Store the `id` from this response. If you lose it, recover it with
[query](/apis/webhooks/query) and match on your `webhookId`.
:::

Note that `authorizationHeader` and `secret` are **not** echoed back. They are write-only.

## After Creating

1. Confirm it is live with [query](/apis/webhooks/query).
2. Make a change that should match, then check [delivery status](/apis/webhooks/status) to see
   what your endpoint actually returned.

Creating with `"enabled": false` first, then enabling via [update](/apis/webhooks/update) once
the receiver is deployed, avoids burning retry attempts against an endpoint that is not up yet.

## Related Topics

- [Event types](/apis/webhooks/event-types)
- [Webhook payload](/apis/webhooks/payload)
- [Query subscriptions](/apis/webhooks/query)
- [Webhooks overview](/apis/webhooks) — permissions and instance limits
