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.

Scope webhooks:write Requires Idempotency-Key

Headers

  • Idempotency-Key string required

    A 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

  • url string required

    An https URL on port 443 that resolves to public addresses. Up to 2048 characters.

  • events array of string required

    Event types to receive, each once, or ["*"] on its own for all.

  • description string

    Up to 200 characters.

Returns

201 The new webhook, with its signing secret.

Fields (object)
  • id uuid
  • url string
  • description string | null
  • events array of string

    Subscribed event types, or ["*"].

  • active boolean
  • previousSecretValidUntil datetime | null

    After a rotation, the previous secret also signs deliveries until then.

  • createdAt datetime
  • updatedAt datetime
  • secret string

    The signing secret, only when the webhook is created or its secret rotated.

  • 400 The request is invalid (VALIDATION_FAILED); details lists the problems, with field when 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 externalClientId in 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 for Retry-After seconds.
Request
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
Response 201
{
  "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
Scope webhooks:read

Returns

200 All webhooks (never more than one page).

Fields of each item (object)
  • id uuid
  • url string
  • description string | null
  • events array of string

    Subscribed event types, or ["*"].

  • active boolean
  • previousSecretValidUntil datetime | null

    After a rotation, the previous secret also signs deliveries until then.

  • createdAt datetime
  • updatedAt datetime
  • secret string

    The 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 for Retry-After seconds.
Request
curl "https://api.agencyaccess.co/api/v2/webhooks" \
  -H "Authorization: Bearer $AGENCYACCESS_API_KEY"
Response 200
{
  "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}
Scope webhooks:read

Path parameters

  • id uuid required

    The webhook ID.

Returns

200 The webhook.

Fields (object)
  • id uuid
  • url string
  • description string | null
  • events array of string

    Subscribed event types, or ["*"].

  • active boolean
  • previousSecretValidUntil datetime | null

    After a rotation, the previous secret also signs deliveries until then.

  • createdAt datetime
  • updatedAt datetime
  • secret string

    The 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 for Retry-After seconds.
Request
curl "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63" \
  -H "Authorization: Bearer $AGENCYACCESS_API_KEY"
Response 200
{
  "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.

Scope webhooks:write

Path parameters

  • id uuid required

    The webhook ID.

Body

  • url string

    Up to 2048 characters.

  • events array of string
  • description string | null

    Up to 200 characters.

  • active boolean

    false pauses deliveries.

Returns

200 The updated webhook.

Fields (object)
  • id uuid
  • url string
  • description string | null
  • events array of string

    Subscribed event types, or ["*"].

  • active boolean
  • previousSecretValidUntil datetime | null

    After a rotation, the previous secret also signs deliveries until then.

  • createdAt datetime
  • updatedAt datetime
  • secret string

    The signing secret, only when the webhook is created or its secret rotated.

  • 400 The request is invalid (VALIDATION_FAILED); details lists the problems, with field when 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 for Retry-After seconds.
Request
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
Response 200
{
  "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.

Scope webhooks:write

Path parameters

  • id uuid required

    The 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 for Retry-After seconds.
Request
curl -X DELETE "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63" \
  -H "Authorization: Bearer $AGENCYACCESS_API_KEY"
Response 204

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.

Scope webhooks:write

Path parameters

  • id uuid required

    The webhook ID.

Returns

200 The webhook with its new secret.

Fields (object)
  • id uuid
  • url string
  • description string | null
  • events array of string

    Subscribed event types, or ["*"].

  • active boolean
  • previousSecretValidUntil datetime | null

    After a rotation, the previous secret also signs deliveries until then.

  • createdAt datetime
  • updatedAt datetime
  • secret string

    The 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 for Retry-After seconds.
Request
curl -X POST "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/rotate-secret" \
  -H "Authorization: Bearer $AGENCYACCESS_API_KEY"
Response 200
{
  "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.

Scope webhooks:write

Path parameters

  • id uuid required

    The webhook ID.

Returns

200 The test delivery.

Fields (object)
  • id uuid

    Also sent as AgencyAccess-Delivery-Id; stays the same across retries.

  • eventId string
  • eventType string
  • test boolean
  • status string

    One of pending, succeeded, failed, cancelled.

  • attemptCount integer
  • lastAttemptAt datetime | null
  • lastStatusCode integer | null
  • lastError string | null
  • nextAttemptAt datetime | null

    When the next retry is due.

  • completedAt datetime | null
  • createdAt datetime
  • attempts array of object

    The 10 most recent attempts, newest first.

    Show fields of attempts
    • id string
    • attemptedAt datetime
    • durationMs integer | null
    • statusCode integer | null
    • error string | 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 for Retry-After seconds.
Request
curl -X POST "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/test" \
  -H "Authorization: Bearer $AGENCYACCESS_API_KEY"
Response 200
{
  "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.

Scope webhooks:read

Path parameters

  • id uuid required

    The webhook ID.

Query parameters

  • limit integer

    Results per page, 1-100.

  • cursor string

    The meta.nextCursor of the previous page. Cursors only work with the filters they were issued for.

  • status string

    Only deliveries with this status.

    One of pending, succeeded, failed, cancelled.

Returns

200 A page of deliveries.

Fields of each item (object)
  • id uuid

    Also sent as AgencyAccess-Delivery-Id; stays the same across retries.

  • eventId string
  • eventType string
  • test boolean
  • status string

    One of pending, succeeded, failed, cancelled.

  • attemptCount integer
  • lastAttemptAt datetime | null
  • lastStatusCode integer | null
  • lastError string | null
  • nextAttemptAt datetime | null

    When the next retry is due.

  • completedAt datetime | null
  • createdAt datetime
  • attempts array of object

    The 10 most recent attempts, newest first.

    Show fields of attempts
    • id string
    • attemptedAt datetime
    • durationMs integer | null
    • statusCode integer | null
    • error string | null
  • 400 The request is invalid (VALIDATION_FAILED); details lists the problems, with field when 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 for Retry-After seconds.
Request
curl "https://api.agencyaccess.co/api/v2/webhooks/b81f3c2e-6a4d-4f19-9c7e-0d5a2e8f1b63/deliveries?status=failed" \
  -H "Authorization: Bearer $AGENCYACCESS_API_KEY"
Response 200
{
  "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
  }
}