> ## 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.

# Objective finalized

> Cadenya sends objective_event.finalized when an objective completes with a final result.

## When it fires

* The Objective reaches `STATE_FINALIZED` after it completes its work.
* If the Agent uses structured output, retrieve the Objective to read the result from its `output` field.

<RequestExample>
  ```json Payload theme={null}
  {
    "type": "objective_event.finalized",
    "timestamp": "2026-08-26T14:03:11Z",
    "data": {
      "agent": {
        "id": "agent_01HXKD2E5NQM3T9AYWCFMGWT9Y",
        "accountId": "account_01HXKD2E5NQM3T9AYWCFTJHJVF",
        "workspaceId": "workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
        "name": "Support Triage",
        "externalId": "",
        "labels": {},
        "profileId": "profile_01HXKD2E5NQM3T9AYWCFS0AP08",
        "createdAt": "2026-08-01T09:00:00Z",
        "updatedAt": "2026-08-01T09:00:00Z"
      },
      "agentVariation": {
        "id": "agentvar_01HXKD2E5NQM3T9AYWCF32BSPP",
        "accountId": "account_01HXKD2E5NQM3T9AYWCFTJHJVF",
        "workspaceId": "workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
        "name": "sonnet-strict-tools",
        "externalId": "",
        "labels": {},
        "profileId": "profile_01HXKD2E5NQM3T9AYWCFS0AP08",
        "createdAt": "2026-08-01T09:00:00Z",
        "updatedAt": "2026-08-01T09:00:00Z"
      },
      "objective": {
        "id": "obj_01HXKD2E5NQM3T9AYWCFQAZGFV",
        "accountId": "account_01HXKD2E5NQM3T9AYWCFTJHJVF",
        "workspaceId": "workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
        "externalId": "ticket-4821",
        "labels": {
          "source": "zendesk"
        },
        "profileId": "profile_01HXKD2E5NQM3T9AYWCFS0AP08",
        "createdAt": "2026-08-26T14:03:11Z"
      },
      "objectiveEvent": {
        "metadata": {
          "id": "objevt_01HXKD2E5NQM3T9AYWCF8ZWBY0",
          "accountId": "account_01HXKD2E5NQM3T9AYWCFTJHJVF",
          "workspaceId": "workspace_01HXKD2E5NQM3T9AYWCF133E3Q",
          "externalId": "",
          "labels": {},
          "profileId": "profile_01HXKD2E5NQM3T9AYWCFS0AP08",
          "createdAt": "2026-08-26T14:03:11Z"
        },
        "contextWindowId": "objwin_01HXKD2E5NQM3T9AYWCFN7BSTR",
        "data": {
          "type": "finalized",
          "finalized": {
            "output": {
              "name": "Kareem Wuckert"
            }
          }
        }
      }
    }
  }
  ```

  ```http Headers theme={null}
  POST /webhooks/cadenya HTTP/1.1
  Host: example.com
  Content-Type: application/json
  webhook-id: wh_01HXKD2E5NQM3T9AYWCFGVF6Y6
  webhook-timestamp: 1787752991
  webhook-signature: v1,K5oZfzN95Z9UVu1EsPQhBaJMSUuGgeEtBXQrZ2lZ+1s=
  ```
</RequestExample>

## Event data

The event-specific fields live at `data.objectiveEvent.data`. The rest of the payload is the shared [envelope](#envelope).

<ResponseField name="type" type="string" required>
  Always `finalized`.
</ResponseField>

<ResponseField name="finalized" type="object" required>
  The terminal event data.

  <Expandable title="properties">
    <ResponseField name="output" type="object">
      The structured output produced by the Objective, when the Agent defines an output schema.
    </ResponseField>
  </Expandable>
</ResponseField>

## Retrieve the result

Use `data.objective.id` from the webhook to retrieve the Objective. Its `output` field matches the schema configured on the Agent.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Cadenya from "@cadenya/cadenya";

  const cadenya = new Cadenya();
  const objective = await cadenya.objectives.retrieve(
    "obj_01HXKD2E5NQM3T9AYWCFQAZGFV",
  );

  console.log(objective.output);
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"log"

  	cadenya "go.cadenya.com/cadenya-go"
  )

  func main() {
  	client, err := cadenya.NewClient()
  	if err != nil {
  		log.Fatal(err)
  	}

  	objective, err := client.Objectives().Retrieve(
  		context.Background(),
  		"obj_01HXKD2E5NQM3T9AYWCFQAZGFV",
  		&cadenya.ObjectiveRetrieveParams{},
  	)
  	if err != nil {
  		log.Fatal(err)
  	}

  	fmt.Printf("%+v\n", objective.Output)
  }
  ```

  ```ruby Ruby theme={null}
  require "cadenya"

  client = Cadenya::Client.new
  objective = client.objectives.retrieve("obj_01HXKD2E5NQM3T9AYWCFQAZGFV")

  puts objective.output
  ```

  ```bash cURL theme={null}
  curl --silent \
    "https://api.cadenya.com/v1/workspaces/${CADENYA_WORKSPACE_ID}/objectives/obj_01HXKD2E5NQM3T9AYWCFQAZGFV" \
    --header "Authorization: Bearer ${CADENYA_API_KEY}" | jq '.output'
  ```
</CodeGroup>

## Envelope

Every objective event arrives in the same envelope. `type` names the event, `data` carries the agent, variation, and objective it belongs to, and `data.objectiveEvent.data` holds the fields documented on each event page.

<AccordionGroup>
  <Accordion title="Headers">
    Cadenya signs every delivery per the [Standard Webhooks](https://www.standardwebhooks.com/) specification and sends it as a `POST` with a JSON body.

    | Header              | Value                                                                                                                                                                                                                   |
    | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `webhook-id`        | Unique per delivery. Key your idempotency on it.                                                                                                                                                                        |
    | `webhook-timestamp` | Unix seconds when Cadenya sent the delivery. The SDKs reject anything more than five minutes off your clock.                                                                                                            |
    | `webhook-signature` | `v1,` followed by a base64 HMAC-SHA256 of `id.timestamp.body`, keyed with the base64-decoded part of your `whsec_` signing key. The header can carry several space-separated signatures. One valid signature is enough. |
    | `content-type`      | `application/json`                                                                                                                                                                                                      |

    Find the signing key under **Account Admin** and rotate it with [rotate the webhook signing key](/docs/api-reference/accountservice/rotates-the-webhook-signing-key-for-the-account). Cadenya records every attempt, and you can inspect them with [list webhook deliveries](/docs/api-reference/agentservice/list-webhook-deliveries).
  </Accordion>

  <Accordion title="Envelope fields">
    <ResponseField name="type" type="string" required>
      The event name, for example `objective_event.tool_called`.
    </ResponseField>

    <ResponseField name="timestamp" type="string" required>
      RFC 3339 time when Cadenya emitted the delivery.
    </ResponseField>

    <ResponseField name="data" type="object" required>
      Everything you need to route the event without a lookup.

      <Expandable title="properties">
        <ResponseField name="agent" type="object" required>
          Resource metadata of the agent: `id`, `name`, `workspaceId`, `accountId`, `externalId`, `labels`, `profileId`, `createdAt`, `updatedAt`.
        </ResponseField>

        <ResponseField name="agentVariation" type="object" required>
          Resource metadata of the variation that ran. Same shape as `agent`.
        </ResponseField>

        <ResponseField name="objective" type="object" required>
          Operation metadata of the objective: `id`, `workspaceId`, `accountId`, `externalId`, `labels`, `profileId`, `createdAt`. If you set `externalId` when you created the objective, it comes back here, so you can route on your own ID.
        </ResponseField>

        <ResponseField name="objectiveEvent" type="object" required>
          The event on the objective's timeline.

          <Expandable title="properties">
            <ResponseField name="metadata" type="object" required>
              Operation metadata of the event itself. `id` is the event ID (`objevt_…`), `createdAt` is when it was persisted.
            </ResponseField>

            <ResponseField name="data" type="object" required>
              The event-specific payload. `type` is the discriminator (`toolCalled`, `userMessage`, and so on) and the matching key holds the fields. See the **Event data** section of each event page.
            </ResponseField>

            <ResponseField name="contextWindowId" type="string" required>
              The context window the event belongs to. Changes when compaction opens a new window.
            </ResponseField>

            <ResponseField name="info" type="object">
              Extra context, when present: `objective` (operation metadata) and `createdBy` (the profile that caused the event).
            </ResponseField>

            <ResponseField name="startedAt" type="string">
              When the work this event records began. Present on events that measure something (an assistant turn, a tool execution), always together with `duration`.
            </ResponseField>

            <ResponseField name="duration" type="string">
              Elapsed time as a duration string, for example `"4.1s"`. Absent when the event is instantaneous.
            </ResponseField>
          </Expandable>
        </ResponseField>
      </Expandable>
    </ResponseField>
  </Accordion>
</AccordionGroup>

## Related

* [Get an objective](/docs/api-reference/objectiveservice/get-an-objective-by-id)
* [Configure structured output](/docs/guides/the-basics/agents#structured-output)
* [Configure webhooks](/docs/guides/the-basics/webhooks#configure-a-webhook)
