API reference
Webhooks
Endpoints on your server that receive signed events as requests change. Deliveries are retried for 24 hours.
Create a webhook
POST/webhooks Registers an endpoint for the event types you choose (["*"] for all). The response includes the signing secret once; store it to verify deliveries. A retry with the same Idempotency-Key returns the webhook without its secret, so if you lost the first response, rotate the secret. Endpoints must use https on port 443 and resolve to public addresses. An agency can have up to 10 webhooks; creating more returns 400 VALIDATION_FAILED.
Headers
-
Idempotency-Keystring requiredA unique value per create, such as a UUID (1-255 visible ASCII characters). Retrying with the same key and body returns the original result instead of creating another object; keys are kept for 24 hours.
Body
-
urlstring requiredAn https URL on port 443 that resolves to public addresses. Up to 2048 characters.
-
eventsarray of string requiredEvent types to receive, each once, or
["*"]on its own for all. -
descriptionstringUp to 200 characters.
Returns
201 The new webhook, with its signing secret.
Fields (object)
-
iduuid -
urlstring -
descriptionstring | null -
eventsarray of stringSubscribed event types, or
["*"]. -
activeboolean -
previousSecretValidUntildatetime | nullAfter a rotation, the previous secret also signs deliveries until then.
-
createdAtdatetime -
updatedAtdatetime -
secretstringThe signing secret, only when the webhook is created or its secret rotated.
- 400 The request is invalid (
VALIDATION_FAILED);detailslists the problems, withfieldwhen one applies. - 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 409 A conflict: an
externalClientIdin use or immutable, an Idempotency-Key reused with a different body or still in progress, or a request in a state that does not allow this. - 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X POST "https://api.agencyaccess.co/api/v2/webhooks" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"url": "https://portal.example.com/hooks/agencyaccess",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"description": "Production portal"
}
JSON const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"url": "https://portal.example.com/hooks/agencyaccess",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"description": "Production portal"
}),
})
const { data } = await res.json() {
"data": {
"id": "b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63",
"url": "https://portal.example.com/hooks/agencyaccess",
"description": "Production portal",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"active": true,
"previousSecretValidUntil": null,
"createdAt": "2026-10-07T09:20:00.000Z",
"updatedAt": "2026-10-07T09:20:00.000Z",
"secret": "whsec_3kq9Xb7Lw2PzR8vN5tY1cD6hJ0mF4sGaQe7Tu2Wy9Bn"
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} List webhooks
GET/webhooks Returns
200 All webhooks (never more than one page).
Fields of each item (object)
-
iduuid -
urlstring -
descriptionstring | null -
eventsarray of stringSubscribed event types, or
["*"]. -
activeboolean -
previousSecretValidUntildatetime | nullAfter a rotation, the previous secret also signs deliveries until then.
-
createdAtdatetime -
updatedAtdatetime -
secretstringThe signing secret, only when the webhook is created or its secret rotated.
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl "https://api.agencyaccess.co/api/v2/webhooks" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks", {
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data, meta } = await res.json()
// meta.nextCursor: pass as ?cursor= for the next page {
"data": [
{
"id": "b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63",
"url": "https://portal.example.com/hooks/agencyaccess",
"description": "Production portal",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"active": true,
"previousSecretValidUntil": null,
"createdAt": "2026-10-07T09:20:00.000Z",
"updatedAt": "2026-10-07T09:20:00.000Z"
}
],
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0",
"nextCursor": null
}
} Retrieve a webhook
GET/webhooks/{id} Path parameters
-
iduuid requiredThe webhook ID.
Returns
200 The webhook.
Fields (object)
-
iduuid -
urlstring -
descriptionstring | null -
eventsarray of stringSubscribed event types, or
["*"]. -
activeboolean -
previousSecretValidUntildatetime | nullAfter a rotation, the previous secret also signs deliveries until then.
-
createdAtdatetime -
updatedAtdatetime -
secretstringThe signing secret, only when the webhook is created or its secret rotated.
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63", {
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data } = await res.json() {
"data": {
"id": "b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63",
"url": "https://portal.example.com/hooks/agencyaccess",
"description": "Production portal",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"active": true,
"previousSecretValidUntil": null,
"createdAt": "2026-10-07T09:20:00.000Z",
"updatedAt": "2026-10-07T09:20:00.000Z"
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} Update a webhook
PATCH/webhooks/{id} Changes the URL, events or description, or pauses (active: false) and resumes it. Deliveries and retries that come due while it is paused are cancelled, and events that happen while it is paused are never delivered to it. A new URL also applies to pending retries.
Path parameters
-
iduuid requiredThe webhook ID.
Body
-
urlstringUp to 2048 characters.
-
eventsarray of string -
descriptionstring | nullUp to 200 characters.
-
activebooleanfalsepauses deliveries.
Returns
200 The updated webhook.
Fields (object)
-
iduuid -
urlstring -
descriptionstring | null -
eventsarray of stringSubscribed event types, or
["*"]. -
activeboolean -
previousSecretValidUntildatetime | nullAfter a rotation, the previous secret also signs deliveries until then.
-
createdAtdatetime -
updatedAtdatetime -
secretstringThe signing secret, only when the webhook is created or its secret rotated.
- 400 The request is invalid (
VALIDATION_FAILED);detailslists the problems, withfieldwhen one applies. - 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X PATCH "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON'
{
"active": false
}
JSON const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
"active": false
}),
})
const { data } = await res.json() {
"data": {
"id": "b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63",
"url": "https://portal.example.com/hooks/agencyaccess",
"description": "Production portal",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"active": false,
"previousSecretValidUntil": null,
"createdAt": "2026-10-07T09:20:00.000Z",
"updatedAt": "2026-10-08T08:00:00.000Z"
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} Delete a webhook
DELETE/webhooks/{id} Deletes the webhook and cancels its pending deliveries. Deleting it again returns 204.
Path parameters
-
iduuid requiredThe webhook ID.
Returns
204 Deleted.
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X DELETE "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
if (!res.ok) throw new Error(`HTTP ${res.status}`) No content.
Rotate the signing secret
POST/webhooks/{id}/rotate-secret Issues a new signing secret, returned once. For 24 hours deliveries carry a second signature made with the previous secret (previousSecretValidUntil), so you can switch without missing events. Rotating again ends the older secret immediately.
Path parameters
-
iduuid requiredThe webhook ID.
Returns
200 The webhook with its new secret.
Fields (object)
-
iduuid -
urlstring -
descriptionstring | null -
eventsarray of stringSubscribed event types, or
["*"]. -
activeboolean -
previousSecretValidUntildatetime | nullAfter a rotation, the previous secret also signs deliveries until then.
-
createdAtdatetime -
updatedAtdatetime -
secretstringThe signing secret, only when the webhook is created or its secret rotated.
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X POST "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/rotate-secret" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/rotate-secret", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data } = await res.json() {
"data": {
"id": "b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63",
"url": "https://portal.example.com/hooks/agencyaccess",
"description": "Production portal",
"events": [
"request.completed",
"service.granted",
"service.grant_failed"
],
"active": true,
"previousSecretValidUntil": "2026-10-09T08:00:00.000Z",
"createdAt": "2026-10-07T09:20:00.000Z",
"updatedAt": "2026-10-08T08:00:00.000Z",
"secret": "whsec_6YSpsQTSPMWERqf-P4oznCQPRnqegQk6TUd5AwSUE3M"
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} Send a test event
POST/webhooks/{id}/test Sends a webhook.test event to the endpoint now (also while it is paused) and returns the delivery with your endpoint's status code or connection error. Failed tests are not retried. Limited to 10 tests a minute per agency.
Path parameters
-
iduuid requiredThe webhook ID.
Returns
200 The test delivery.
Fields (object)
-
iduuidAlso sent as
AgencyAccess-Delivery-Id; stays the same across retries. -
eventIdstring -
eventTypestring -
testboolean -
statusstringOne of
pending,succeeded,failed,cancelled. -
attemptCountinteger -
lastAttemptAtdatetime | null -
lastStatusCodeinteger | null -
lastErrorstring | null -
nextAttemptAtdatetime | nullWhen the next retry is due.
-
completedAtdatetime | null -
createdAtdatetime -
attemptsarray of objectThe 10 most recent attempts, newest first.
Show fields of attempts
-
idstring -
attemptedAtdatetime -
durationMsinteger | null -
statusCodeinteger | null -
errorstring | null
-
- 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl -X POST "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/test" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/test", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data } = await res.json() {
"data": {
"id": "d4a7c1e2-58b9-4f3a-8e6d-2c9b0f1a7e35",
"eventId": "evt_5b1f0c9a2e7d4c38a6f1d2e9b8c7a604",
"eventType": "webhook.test",
"test": true,
"status": "succeeded",
"attemptCount": 1,
"lastAttemptAt": "2026-10-07T10:02:11.000Z",
"lastStatusCode": 200,
"lastError": null,
"nextAttemptAt": null,
"completedAt": "2026-10-07T10:02:11.000Z",
"createdAt": "2026-10-07T10:02:10.000Z",
"attempts": [
{
"id": "a1f9e3c7-2b6d-4e8a-9c0f-5d7b3e1a8f24",
"attemptedAt": "2026-10-07T10:02:11.000Z",
"durationMs": 184,
"statusCode": 200,
"error": null
}
]
},
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0"
}
} List deliveries
GET/webhooks/{id}/deliveries The webhook's deliveries, newest first, each with its recent attempts.
Path parameters
-
iduuid requiredThe webhook ID.
Query parameters
-
limitintegerResults per page, 1-100.
-
cursorstringThe
meta.nextCursorof the previous page. Cursors only work with the filters they were issued for. -
statusstringOnly deliveries with this status.
One of
pending,succeeded,failed,cancelled.
Returns
200 A page of deliveries.
Fields of each item (object)
-
iduuidAlso sent as
AgencyAccess-Delivery-Id; stays the same across retries. -
eventIdstring -
eventTypestring -
testboolean -
statusstringOne of
pending,succeeded,failed,cancelled. -
attemptCountinteger -
lastAttemptAtdatetime | null -
lastStatusCodeinteger | null -
lastErrorstring | null -
nextAttemptAtdatetime | nullWhen the next retry is due.
-
completedAtdatetime | null -
createdAtdatetime -
attemptsarray of objectThe 10 most recent attempts, newest first.
Show fields of attempts
-
idstring -
attemptedAtdatetime -
durationMsinteger | null -
statusCodeinteger | null -
errorstring | null
-
- 400 The request is invalid (
VALIDATION_FAILED);detailslists the problems, withfieldwhen one applies. - 401 The API key is missing, invalid or revoked.
- 403 The key lacks the scope (
CAPABILITY_REQUIRED), the plan has no API access (PLAN_NOT_ENTITLED), or, when creating a request, the monthly limit is reached (PLAN_LIMIT_REACHED). - 404 Not found, or it belongs to another agency.
- 429 Rate limited (
RATE_LIMITED); wait forRetry-Afterseconds.
curl "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/deliveries?status=failed" \
-H "Authorization: Bearer $AGENCYACCESS_API_KEY" const res = await fetch("https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/deliveries?status=failed", {
headers: {
Authorization: `Bearer ${process.env.AGENCYACCESS_API_KEY}`,
},
})
const { data, meta } = await res.json()
// meta.nextCursor: pass as ?cursor= for the next page {
"data": [
{
"id": "d4a7c1e2-58b9-4f3a-8e6d-2c9b0f1a7e35",
"eventId": "evt_5b1f0c9a2e7d4c38a6f1d2e9b8c7a604",
"eventType": "request.completed",
"test": false,
"status": "succeeded",
"attemptCount": 1,
"lastAttemptAt": "2026-10-07T10:02:11.000Z",
"lastStatusCode": 200,
"lastError": null,
"nextAttemptAt": null,
"completedAt": "2026-10-07T10:02:11.000Z",
"createdAt": "2026-10-07T10:02:10.000Z",
"attempts": [
{
"id": "a1f9e3c7-2b6d-4e8a-9c0f-5d7b3e1a8f24",
"attemptedAt": "2026-10-07T10:02:11.000Z",
"durationMs": 184,
"statusCode": 200,
"error": null
}
]
}
],
"meta": {
"requestId": "req_1f0b6c2e9a7d4f58b3c1e6a9d2f4b7c0",
"nextCursor": null
}
}