> ## Documentation Index
> Fetch the complete documentation index at: https://cadenya.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# List webhook deliveries

> The delivery log for an agent's webhooks: what Cadenya sent, what your endpoint answered, and how long it took.

When a [webhook](/docs/guides/webhooks) does not arrive, this endpoint tells you whether Cadenya sent it and what your endpoint said back. It settles the question of whose bug it is before you go looking.

Set `webhookEventsUrl` on an agent's spec and every objective event for that agent is POSTed to it. Each attempt is recorded here.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const deliveries = await client.agents.webhookDeliveries.list(agentId, { workspaceId });

  for await (const delivery of deliveries) {
    const d = delivery.data;
    console.log(d.eventType, d.status, d.httpStatusCode, `${d.latencyMs}ms`, d.errorMessage ?? '');
  }
  ```

  ```go Go theme={null}
  deliveries := client.Agents.WebhookDeliveries.ListAutoPaging(ctx, agentID,
  	cadenya.AgentWebhookDeliveryListParams{
  		WorkspaceID: cadenya.String(workspaceID),
  	})

  for deliveries.Next() {
  	d := deliveries.Current().Data
  	fmt.Println(d.EventType, d.Status, d.HTTPStatusCode, d.LatencyMs, d.ErrorMessage)
  }
  ```

  ```ruby Ruby theme={null}
  deliveries = cadenya.agents.webhook_deliveries.list(agent_id, workspace_id: workspace_id)

  deliveries.auto_paging_each do |delivery|
    d = delivery.data
    puts "#{d.event_type}  #{d.status}  #{d.http_status_code}  #{d.latency_ms}ms  #{d.error_message}"
  end
  ```

  ```bash cURL theme={null}
  curl "https://api.cadenya.com/v1/workspaces/${WORKSPACE_ID}/agents/${AGENT_ID}/webhook_deliveries" \
    -H "Authorization: Bearer ${CADENYA_API_KEY}"
  ```
</CodeGroup>

## Read a delivery

Each record carries the event it delivered, where it went, and what came back.

| Field                             | What it tells you                                                  |
| --------------------------------- | ------------------------------------------------------------------ |
| `eventType`                       | Which objective event triggered the send.                          |
| `objectiveId`, `objectiveEventId` | The exact event, so you can go read it.                            |
| `webhookUrl`                      | Where it went.                                                     |
| `webhookId`                       | The `webhook-id` header value, for matching against your own logs. |
| `status`                          | `COMPLETED` or `FAILED`.                                           |
| `httpStatusCode`                  | What your endpoint answered.                                       |
| `attemptCount`                    | How many tries it took.                                            |
| `errorMessage`                    | Why it failed, when it did.                                        |
| `latencyMs`                       | How long your endpoint took.                                       |

`webhookId` is the useful one when correlating. It is the same value Cadenya sent in the `webhook-id` header, so a delivery here and a request in your access log can be matched exactly.

<Note>
  Only `WEBHOOK_DELIVERY_STATUS_COMPLETED` and `WEBHOOK_DELIVERY_STATUS_FAILED` are ever written. The enum also declares `PENDING` and `DISABLED`, which never appear on a record.

  On a failed delivery, `httpStatusCode`, `latencyMs`, and `attemptCount` are unreliable: `attemptCount` is set to the maximum rather than the number of tries made, and the response fields are left at zero. Read `errorMessage`.
</Note>

## Filter to the delivery you want

The list takes `objectiveId`, `eventType`, `labels`, and cursor pagination.

```typescript theme={null}
const failures = await client.agents.webhookDeliveries.list(agentId, {
  workspaceId,
  eventType: 'OBJECTIVE_EVENT_TYPE_TOOL_APPROVAL_REQUESTED',
});
```

An unrecognized `eventType` is a `400`, so a typo announces itself. There is no `status` filter: to find failures, list and filter on the client.

<Warning>
  Delivery records are kept for **24 hours** and then expire. This is a live debugging surface, not an audit log. Persist what you need from your own handler.
</Warning>

## How Cadenya delivers

Each event is one `POST` with `Content-Type: application/json` and a Standard Webhooks envelope:

```json theme={null}
{
  "timestamp": "2026-07-08T17:14:00Z",
  "type": "objective_event.tool_approval_requested",
  "data": { }
}
```

Failures retry **five times** with exponential backoff, starting at one second and doubling to a one-minute ceiling. Each attempt gets 30 seconds to complete. Any non-2xx response is retried.

Three failures do **not** retry, because retrying cannot help:

* The URL is unparseable, or is not `https`. Cadenya refuses plain HTTP.
* Your endpoint answers `410 Gone`, which Cadenya reads as "stop sending."
* The signing key cannot be loaded.

## Verify the signature

Every delivery carries `webhook-id`, `webhook-timestamp`, and `webhook-signature` headers, following [Standard Webhooks](https://www.standardwebhooks.com/). The signature is an HMAC-SHA256 over `{webhook-id}.{webhook-timestamp}.{body}`, keyed by your **account** signing key, not a per-agent secret.

The SDK verifies and parses in one call:

```typescript theme={null}
const event = client.webhooks.unwrap(rawBody, headers); // throws if the signature is bad
```

Read the key from `GET /v1/account` (`info.webhookEventsHmacSecret`) and store it as a secret. Rotate it with [`POST /v1/account:rotateWebhookSigningKey`](/docs/api-reference/accountservice/rotates-the-webhook-signing-key-for-the-account), which returns the new key and takes effect immediately, with no overlap window. Deploy the new key to your handler before you rotate.

<Warning>
  Rotation has no grace period. The old key stops signing the moment the new one is issued, so a handler still holding the old key rejects every delivery until you redeploy.
</Warning>

## Streaming or webhooks

Webhooks retry, record their outcome, and survive your process restarting. [Streaming](/docs/api-reference/objectiveeventstreamsservice/stream-objective-events) does none of that, but it needs no public endpoint and shows up instantly.

Use webhooks when a machine reacts. Use streaming when a person watches.

## Related

<CardGroup cols={2}>
  <Card title="Webhooks" icon="bell" href="/docs/guides/webhooks">
    Signature verification, the payload envelope, and a full handler.
  </Card>

  <Card title="Approving a tool" icon="hand" href="/docs/guides/callbacks/approving-a-tool">
    The webhook that pauses an objective until a person decides.
  </Card>

  <Card title="Stream objective events" icon="tower-broadcast" href="/docs/api-reference/objectiveeventstreamsservice/stream-objective-events">
    The same events, live, with no endpoint to host.
  </Card>

  <Card title="Rotate the signing key" icon="key" href="/docs/api-reference/accountservice/rotates-the-webhook-signing-key-for-the-account">
    One call, immediate effect, no overlap.
  </Card>
</CardGroup>


## OpenAPI

````yaml get /v1/workspaces/{workspaceId}/agents/{agentId}/webhook_deliveries
openapi: 3.1.0
info:
  title: Cadenya API
  description: API for the Cadenya Agent Runtime platform.
  version: '1.0'
servers:
  - url: https://api.cadenya.com
    description: Production server
security:
  - bearerAuth: []
tags:
  - name: AIProviderKeyService
  - name: APIKeyService
    description: |-
      Issue, rotate, disable, and revoke a workspace's API keys. Every key
       belongs to exactly one workspace; the system-managed global account key is
       managed via GlobalAPIKeyService instead.
  - name: AccountService
    description: >-
      Manage the authenticated account. Accounts are the top-level
      organizational
       unit and contain one or more workspaces.
  - name: AgentScheduleService
    description: >-
      Manage recurring schedules attached to agents. Schedules trigger
      objectives
       on a cadence defined by AgentScheduleSpec.Schedule.
  - name: AgentService
    description: >-
      Manage AI agents within a workspace. Agents define AI behavior and tool
      access.
  - name: AgentVariationService
    description: >-
      Manage variations of an agent and their tool, sub-agent, and memory layer
      assignments.
  - name: GlobalAPIKeyService
    description: |-
      Manage the account's system-provisioned global API key. The global key is
       the only key that spans every workspace; it is created by the system and
       cannot be deleted, so the surface is retrieve, rotate, and the
       disable/enable kill switch.
  - name: MemoryService
    description: >-
      Manage memory layers and their entries. Layers are named containers that
      can
       be composed into an objective's memory cascade; entries are the keyed values
       within a layer. System-managed layers (e.g., episodic layers created by the
       runtime) cannot be mutated through this API.
  - name: ModelService
    description: |-
      Manage LLM models available to a workspace. Models represent provider and
       family pairs (e.g., "anthropic/claude-sonnet-4.6"). Workspaces are seeded
       with the supported models and you can enable or disable each one.
  - name: ObjectiveEventStreamsService
  - name: ObjectiveService
  - name: ProfilesService
    description: |-
      Operations on profiles, the account-level principals (users, API keys,
       system) that authenticate against the API.
  - name: SearchService
  - name: TenantService
    description: >-
      Read and erase tenants and the subjects under them. Tenants and subjects
      are
       created by assertion — on objective creation or widget session mint — never
       directly, so this service has no create or update: it exists to enumerate what
       assertions have produced, and to destroy it on request.
  - name: ToolService
    description: >-
      Manage tool sets and the tools they contain. Tool sets group related
      tools,
       and tools define specific capabilities available to agents.

       When a tool set is managed, only API key actors can modify its tools; human
       (profile) actors cannot.
  - name: UploadService
    description: |-
      Issue short-lived presigned URLs for direct client-to-object-storage
       uploads. Created uploads can be referenced by id when creating or updating
       resources that accept binary content (e.g., MemoryEntry).
  - name: WidgetService
    description: |-
      Manage embeddable chat widgets. A widget binds an agent to a globally
       unique hostname with a per-widget origin allowlist; browsers reach it with
       session tokens minted via WidgetSessionService.
  - name: WidgetSessionService
    description: >-
      Mint and manage widget sessions. Session creation is server-to-server
      only:
       the customer's backend authenticates its visitor, asserts tenant/subject
       context, attaches any per-visitor secrets, and receives a short-lived
       bearer token the browser uses against the widget host.
  - name: WorkspaceAdminService
    description: >-
      Administer workspaces across the account: create and archive workspaces
      and
       manage their membership. These operations are account-scoped and require the
       admin role (a token whose profile holds the WorkOS admin role); they live
       under /v1/account/workspaces rather than the workspace-scoped /v1/workspaces
       tree so an admin can manage any workspace in the account, including ones they
       are not themselves a member of.
  - name: WorkspaceSecretService
  - name: WorkspaceService
    description: |-
      Manage workspaces within an account. Workspaces provide organizational
       grouping and isolation for resources such as agents, tools, and API keys.

       This is the workspace-scoped, end-user surface. Administrative operations
       (create / archive workspaces, manage members) live in WorkspaceAdminService
       under /v1/account/workspaces and require the admin role.
paths:
  /v1/workspaces/{workspaceId}/agents/{agentId}/webhook_deliveries:
    get:
      tags:
        - AgentService
        - Agents
      summary: List webhook deliveries
      description: Lists all webhook deliveries for an agent
      operationId: AgentService_ListAgentWebhookDeliveries
      parameters:
        - name: workspaceId
          in: path
          description: Workspace ID.
          required: true
          schema:
            type: string
            example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
        - name: agentId
          in: path
          required: true
          schema:
            example: agent_01HXKD2E5NQM3T9AYWCFMGWT9Y
            type: string
        - name: cursor
          in: query
          description: Pagination cursor from previous response
          schema:
            type: string
        - name: limit
          in: query
          description: Maximum number of results to return
          schema:
            type: integer
            format: int32
        - name: objectiveId
          in: query
          description: Optional filter by objective ID
          schema:
            example: obj_01HXKD2E5NQM3T9AYWCFQAZGFV
            type: string
        - name: eventType
          in: query
          description: Optional filter by event type
          schema:
            enum:
              - OBJECTIVE_EVENT_TYPE_UNSPECIFIED
              - OBJECTIVE_EVENT_TYPE_USER_MESSAGE
              - OBJECTIVE_EVENT_TYPE_TOOL_APPROVAL_REQUESTED
              - OBJECTIVE_EVENT_TYPE_TOOL_APPROVED
              - OBJECTIVE_EVENT_TYPE_TOOL_DENIED
              - OBJECTIVE_EVENT_TYPE_TOOL_CALLED
              - OBJECTIVE_EVENT_TYPE_ERROR
              - OBJECTIVE_EVENT_TYPE_ASSISTANT_MESSAGE
              - OBJECTIVE_EVENT_TYPE_TOOL_RESULT
              - OBJECTIVE_EVENT_TYPE_TOOL_ERROR
              - OBJECTIVE_EVENT_TYPE_CONTEXT_WINDOW_COMPACTED
              - OBJECTIVE_EVENT_TYPE_MEMORY_READ
              - OBJECTIVE_EVENT_TYPE_CANCELLED
              - OBJECTIVE_EVENT_TYPE_SUB_AGENT_SPAWNED
              - OBJECTIVE_EVENT_TYPE_SUB_AGENT_UPDATED
              - OBJECTIVE_EVENT_TYPE_FINALIZED
              - OBJECTIVE_EVENT_TYPE_NOTICE
              - OBJECTIVE_EVENT_TYPE_TIMED_OUT
            type: string
            format: enum
        - name: labels
          in: query
          description: |-
            Filters by metadata labels. Comma-separated key=value pairs,
             e.g. "env=prod,team=ai". A resource matches only if every pair
             matches exactly (AND semantics).
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListAgentWebhookDeliveriesResponse'
        default:
          description: Default error response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Status'
      x-codeSamples:
        - lang: JavaScript
          source: >-
            import Cadenya from '@cadenya/cadenya';


            const client = new Cadenya({
              apiKey: process.env['CADENYA_API_KEY'], // This is the default and can be omitted
            });


            // Automatically fetches more pages as needed.

            for await (const webhookDelivery of
            client.agents.webhookDeliveries.list(
              'agent_01HXKD2E5NQM3T9AYWCFMGWT9Y',
              { workspaceId: 'workspace_01HXKD2E5NQM3T9AYWCF133E3Q' },
            )) {
              console.log(webhookDelivery.data);
            }
        - lang: Python
          source: |-
            import os
            from cadenya import Cadenya

            client = Cadenya(
                api_key=os.environ.get("CADENYA_API_KEY"),  # This is the default and can be omitted
            )
            page = client.agents.webhook_deliveries.list(
                agent_id="agent_01HXKD2E5NQM3T9AYWCFMGWT9Y",
                workspace_id="workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
            )
            page = page.items[0]
            print(page.data)
        - lang: Go
          source: "package main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"go.cadenya.com/cadenya-go\"\n\t\"go.cadenya.com/cadenya-go/option\"\n)\n\nfunc main() {\n\tclient := cadenya.NewClient(\n\t\toption.WithAPIKey(\"My API Key\"),\n\t)\n\tpage, err := client.Agents.WebhookDeliveries.List(\n\t\tcontext.TODO(),\n\t\t\"agent_01HXKD2E5NQM3T9AYWCFMGWT9Y\",\n\t\tcadenya.AgentWebhookDeliveryListParams{\n\t\t\tWorkspaceID: cadenya.String(\"workspace_01HXKD2E5NQM3T9AYWCF133E3Q\"),\n\t\t},\n\t)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\tfmt.Printf(\"%+v\\n\", page)\n}\n"
        - lang: Ruby
          source: |-
            require "cadenya"

            cadenya = Cadenya::Client.new(api_key: "My API Key")

            page = cadenya.agents.webhook_deliveries.list(
              "agent_01HXKD2E5NQM3T9AYWCFMGWT9Y",
              workspace_id: "workspace_01HXKD2E5NQM3T9AYWCF133E3Q"
            )

            puts(page)
        - lang: CLI
          source: |-
            cadenya agents:webhook-deliveries list \
              --api-key 'My API Key' \
              --workspace-id workspace_01HXKD2E5NQM3T9AYWCF133E3Q \
              --agent-id agent_01HXKD2E5NQM3T9AYWCFMGWT9Y
components:
  schemas:
    ListAgentWebhookDeliveriesResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/WebhookDelivery'
        pagination:
          $ref: '#/components/schemas/Page'
    Status:
      type: object
      properties:
        code:
          type: integer
          description: >-
            The status code, which should be an enum value of
            [google.rpc.Code][google.rpc.Code].
          format: int32
        message:
          type: string
          description: >-
            A developer-facing error message, which should be in English. Any
            user-facing error message should be localized and sent in the
            [google.rpc.Status.details][google.rpc.Status.details] field, or
            localized by the client.
        details:
          type: array
          items:
            $ref: '#/components/schemas/GoogleProtobufAny'
          description: >-
            A list of messages that carry the error details.  There is a common
            set of message types for APIs to use.
      description: >-
        The `Status` type defines a logical error model that is suitable for
        different programming environments, including REST APIs and RPC APIs. It
        is used by [gRPC](https://github.com/grpc). Each `Status` message
        contains three pieces of data: error code, error message, and error
        details. You can find out more about this error model and how to work
        with it in the [API Design
        Guide](https://cloud.google.com/apis/design/errors).
    WebhookDelivery:
      required:
        - metadata
        - data
      type: object
      properties:
        metadata:
          $ref: '#/components/schemas/OperationMetadata'
        data:
          allOf:
            - $ref: '#/components/schemas/WebhookDeliveryData'
          description: Webhook delivery details.
    Page:
      type: object
      properties:
        nextCursor:
          type: string
      description: >-
        Page carries cursor-based pagination state. There is no total: the
        cursor
         walks the result set without ever counting it, and a count would cost a second
         query on every list.
    GoogleProtobufAny:
      type: object
      properties:
        '@type':
          type: string
          description: The type of the serialized message.
      additionalProperties: true
      description: >-
        Contains an arbitrary serialized message along with a @type that
        describes the type of the serialized message.
    OperationMetadata:
      required:
        - id
        - accountId
        - workspaceId
        - profileId
        - createdAt
      type: object
      properties:
        id:
          readOnly: true
          type: string
          description: >-
            Unique identifier for the operation (prefixed ULID, e.g.,
            "obj_01HXK...")
        accountId:
          readOnly: true
          example: account_01HXKD2E5NQM3T9AYWCFTJHJVF
          type: string
          description: >-
            Account this operation belongs to for multi-tenant isolation
            (prefixed ULID)
        workspaceId:
          readOnly: true
          example: workspace_01HXKD2E5NQM3T9AYWCF133E3Q
          type: string
          description: >-
            Workspace this operation belongs to for organizational grouping
            (prefixed ULID)
        labels:
          type: object
          additionalProperties:
            type: string
          description: |-
            Key-value pairs for categorization and filtering. Values are 0-63
             alphanumeric characters with "-", "_", or "." allowed between; keys
             follow the same shape and additionally accept an optional DNS-subdomain
             prefix (e.g. "cadenya.com/") of at most 253 characters.
             Examples: {"priority": "high", "source": "api", "workflow": "onboarding"}
        createdAt:
          readOnly: true
          type: string
          description: |-
            Timestamp when this operation was created
             ULID includes timestamp information, but this explicit field enables easier querying
          format: date-time
        externalId:
          type: string
          description: >-
            External ID for the operation (e.g., a workflow ID from an external
            system)
        profileId:
          readOnly: true
          example: profile_01HXKD2E5NQM3T9AYWCFS0AP08
          type: string
          description: >-
            ID of the actor (user or service account) that created this
            operation
      description: >-
        Metadata for ephemeral operations and activities (e.g., objectives,
        executions, runs)
    WebhookDeliveryData:
      required:
        - agentId
        - objectiveId
        - objectiveEventId
        - webhookUrl
        - webhookId
        - status
        - attemptCount
        - lastAttemptAt
        - httpStatusCode
        - latencyMs
        - eventType
        - responseContentLength
      type: object
      properties:
        agentId:
          example: agent_01HXKD2E5NQM3T9AYWCFMGWT9Y
          type: string
          description: Related resources
        objectiveId:
          example: obj_01HXKD2E5NQM3T9AYWCFQAZGFV
          type: string
        objectiveEventId:
          example: objevt_01HXKD2E5NQM3T9AYWCF8ZWBY0
          type: string
        webhookUrl:
          type: string
          description: Webhook delivery details
        webhookId:
          example: wh_01HXKD2E5NQM3T9AYWCFGVF6Y6
          type: string
        status:
          enum:
            - WEBHOOK_DELIVERY_STATUS_UNSPECIFIED
            - WEBHOOK_DELIVERY_STATUS_PENDING
            - WEBHOOK_DELIVERY_STATUS_COMPLETED
            - WEBHOOK_DELIVERY_STATUS_FAILED
            - WEBHOOK_DELIVERY_STATUS_DISABLED
          type: string
          format: enum
        attemptCount:
          type: integer
          format: int32
        lastAttemptAt:
          type: string
          format: date-time
        httpStatusCode:
          type: integer
          description: Response details. The response body is not retained.
          format: int32
        errorMessage:
          type: string
        latencyMs:
          type: integer
          format: int32
        eventType:
          enum:
            - OBJECTIVE_EVENT_TYPE_UNSPECIFIED
            - OBJECTIVE_EVENT_TYPE_USER_MESSAGE
            - OBJECTIVE_EVENT_TYPE_TOOL_APPROVAL_REQUESTED
            - OBJECTIVE_EVENT_TYPE_TOOL_APPROVED
            - OBJECTIVE_EVENT_TYPE_TOOL_DENIED
            - OBJECTIVE_EVENT_TYPE_TOOL_CALLED
            - OBJECTIVE_EVENT_TYPE_ERROR
            - OBJECTIVE_EVENT_TYPE_ASSISTANT_MESSAGE
            - OBJECTIVE_EVENT_TYPE_TOOL_RESULT
            - OBJECTIVE_EVENT_TYPE_TOOL_ERROR
            - OBJECTIVE_EVENT_TYPE_CONTEXT_WINDOW_COMPACTED
            - OBJECTIVE_EVENT_TYPE_MEMORY_READ
            - OBJECTIVE_EVENT_TYPE_CANCELLED
            - OBJECTIVE_EVENT_TYPE_SUB_AGENT_SPAWNED
            - OBJECTIVE_EVENT_TYPE_SUB_AGENT_UPDATED
            - OBJECTIVE_EVENT_TYPE_FINALIZED
            - OBJECTIVE_EVENT_TYPE_NOTICE
            - OBJECTIVE_EVENT_TYPE_TIMED_OUT
          type: string
          description: The type of objective event that triggered this webhook delivery
          format: enum
        responseHeaders:
          type: object
          additionalProperties:
            type: string
          description: Response headers received from the webhook endpoint
        responseContentLength:
          type: string
          description: Content length of the response body in bytes
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

````