Skip to main content

Create a Subscription

Request​

POST ~/api/webhook

Payload​

{
"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": []
}
FieldTypeRequiredDescription
urlstringyesWhere deliveries are POSTed. Must be reachable from the Agility instance.
webhookIdstringyesYour identifier for this subscription. Echoed in every delivery. Make it meaningful and unique.
eventTypesarraynoWhat to be notified about. See event types. Omitting it is accepted — validation is skipped when the key is absent — but a subscription with no event types is not useful.
notificationEmailsarraynoAddresses notified about delivery problems for this subscription.
enabledbooleannoWhether the subscription is live. Create disabled to stage a configuration.
descriptionstringnoFree text: where this goes and why. Worth filling in — it is what a future administrator will read.
authorizationHeaderstringnoSent verbatim as the Authorization header on every delivery.
secretstringnoShared secret used to HMAC-sign each delivery. See verifying the payload.
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​

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​

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"
}
FieldDescription
idServer-assigned UUID. This — not webhookId — is what update, delete and status take.
webhookIdThe identifier you supplied.
createdByOid 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 or Delivery status.

Two identifiers, and only one works in URLs

id is the server-generated UUID and is the only thing the update, delete and 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 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.
  2. Make a change that should match, then check delivery status to see what your endpoint actually returned.

Creating with "enabled": false first, then enabling via update once the receiver is deployed, avoids burning retry attempts against an endpoint that is not up yet.