Skip to main content

Webhook Payload

This is what arrives at the url on your subscription.

Request​

Agility sends an HTTP POST with a JSON body.

Headers Sent to Your Endpoint​

HeaderValueSent when
Content-Typeapplication/jsonAlways.
AuthorizationVerbatim value of the subscription's authorizationHeaderOnly if the subscription has a non-empty one.
X-SIGNATURE-HMAC-SHA512Base64 HMAC-SHA512 of the raw body, keyed with your secretOnly if the subscription has a non-empty secret.
X-SIGNATURELegacy signature, computed differentlyOnly where the instance has legacy signatures enabled.

No fixed User-Agent is set, so do not filter on one.

Body​

{
"events": [
{
"webhookId": "YYY",
"sequenceId": 1,
"eventType": "AssetChanged",
"timestamp": "UTC timestamp",
"instigator": {
"_oid": "Member:20",
"href": "https://V1Host/V1Instance/assetdetail.v1?oid=Member:20",
"name": "Administrator",
"nickName": "Admin",
"email": "admin@admin.com",
"role": "Role:2",
"avatar": "https://V1Host/V1Instance/Image.mvc/Show?imageOid=Image:192923"
},
"targetAsset": {
"_oid": "Story:123:456",
"assetType": "Story",
"href": "https://V1Host/V1Instance/assetdetail.v1?oid=Story:123"
},
"changes": [
{
"name": "Name",
"old": "Original Name",
"new": "New Name",
"act": "Set"
},
{
"name": "Owners",
"new": "Member:20",
"act": "Added"
}
],
"snapshot": [
{
"_oid": "Story:123",
"Owners.Name": ["Administrator"],
"Owners.ID": [{ "_oid": "Member:20" }],
"SubsAndMe.Owners.Name": ["Administrator"]
}
]
}
]
}

The href values are asset permalinks — assetdetail.v1 URLs that open the asset in the product UI, built for the humans on the receiving end of a delivery rather than for programs.

note
events is an array

A single delivery can carry more than one event. Iterate; do not read events[0] and stop.

Event Fields​

FieldTypeDescription
webhookIdstringThe identifier you chose when creating the subscription. Use it to route the delivery.
sequenceIdintegerMonotonic sequence number. Use it to restore ordering.
eventTypestringAssetCreated or AssetChanged — which event type rule fired.
timestampstringUTC timestamp of the change.
instigatorobjectThe member who made the change.
targetAssetobjectThe asset that was created or changed.
changesarrayWhat actually changed. Empty or absent for AssetCreated.
snapshotarrayProjection of the asset, shaped by the event type's select.

instigator​

FieldDescription
_oidOid token of the member, e.g. Member:20.
hrefDeep link to the member's detail page in the Agility UI.
nameFull name.
nickNameShort name.
emailEmail address.
roleOid token of the member's role, e.g. Role:2.
avatarURL to the avatar image.

targetAsset​

FieldDescription
_oidOid token of the asset — often including a moment, e.g. Story:123:456.
assetTypeThe asset type system name.
hrefDeep link to the asset in the Agility UI.
warning
targetAsset._oid may carry a moment

Story:123:456 is Story 123 at version 456, not Story 123. If you use this value as a correlation key or pass it back into the API expecting current data, strip the third segment first. Note that href and snapshot[]._oid in the same payload use the momentless form (Story:123) — the two are not interchangeable.

changes[]​

One entry per attribute affected.

FieldDescription
nameThe attribute definition name.
oldPrevious value. Absent where there was none — including on relation adds.
newNew value.
actWhat was done: Set, Added, Removed.

Scalar attributes are Set and carry both old and new. Multi-value relations are Added or Removed and carry only new — the Oid token of the value added or removed, not the whole resulting collection.

snapshot[]​

The projection requested by the event type's select, in the same shape a query.v1 result would take: _oid plus one key per selected path. Multi-value projections are arrays, and relation projections without a further attribute come back as { "_oid": ... } objects.

With no select on the event type, snapshot carries little more than the _oid.

Verifying the Payload​

If you set a secret on the subscription, every delivery carries X-SIGNATURE-HMAC-SHA512: the Base64-encoded HMAC-SHA512 of the raw UTF-8 request body, keyed with your secret.

Verify against the raw body

Compute the HMAC over the exact bytes received, before any JSON parsing or re-serialization. Parsing and re-encoding will change whitespace and key order and the signature will not match. Capture the raw body first, verify, then parse.

Verifying a delivery in Python
import base64
import hashlib
import hmac

def is_signature_valid(raw_body: bytes, secret: str, header_value: str) -> bool:
expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha512).digest()
return hmac.compare_digest(
base64.b64encode(expected).decode("ascii"),
header_value,
)

Always compare in constant time, as above.

note
secret was undocumented

Neither the secret subscription field nor the signature headers appeared in the published reference material.

secret is write-only from the API's point of view: it is accepted on create and update, but it is never returned by query or included in delivery receipts. Store your copy of it when you set it.

authorizationHeader Versus secret​

They solve different problems and are worth using together:

FieldDirectionPurpose
authorizationHeaderAgility proves itself to youSent verbatim as the Authorization header so your endpoint can authenticate the caller.
secretAgility proves the payloadSigns the body, so you know it was not altered and really came from your instance.

A static Authorization value proves the caller knows a shared token, but does nothing about payload integrity, and it is replayable if intercepted. The HMAC signature covers the body.

Responding​

Return 200 OK. Any other status counts as a failed delivery, and consecutive failures take the subscription out of service — see delivery semantics.

Acknowledge quickly and process asynchronously: enqueue the payload, return 200, and do the work outside the request. A slow handler produces timeouts, which count as failures.

Make handlers idempotent. Deduplicate on webhookId plus sequenceId; a retry after a response you actually processed will redeliver the event.