# Push every call to a webhook

## Requirement

"After every call, push the transcript to our webhook."

## Pieces

- **Connection**: the webhook's key, provider `http_key`.
- **One tool per action**: an HTTP tool that posts to the webhook.
- **Post hook**: the tool attached as `post_hook_tool_ids` on the agent draft,
  then published.

## The shape to avoid

Instructions in the prompt telling the model to "send the transcript at the
end". The model does not run after the last word, and a tool it could call
mid-conversation is one it may forget to call. A post hook runs after the
conversation whether or not anybody asked, and its failures land in the
interaction's record where they can be found.

## Steps

Ids from an earlier step are written as `{{step_N.field}}`; the test that runs
this page substitutes them.

A hook is called with no arguments from the model. What the webhook receives
is what a human fixed on the instance (`fixed_parameters`) and nothing else:
the platform's own ids never leave it. To be told which conversation ended, fix
a parameter of your choosing — here `conversation_id` — to
`$ref:conversation.id`; it is resolved when the hook runs and arrives in the
payload as the conversation's id. The transcript itself is not in the request;
the webhook reads that id from the payload and fetches the transcript with
`get_trace`, and the interaction's end reason and duration with
`get_interaction`.

1. The webhook's key.

   `create_connection`

   ```json
   {
     "provider": "http_key",
     "account_label": "Call-ended webhook",
     "scopes": [],
     "grant_secret": "{\"header\":\"X-Webhook-Key\",\"prefix\":\"\",\"value\":\"whk_replace_me\"}",
     "granted_via": "pasted"
   }
   ```

2. The tool. `fixed_parameters` travel with every call; the model never sees
   them and, in a hook, there is no model to. The webhook receives
   `{"event": "conversation_ended", "conversation_id": "<the id>"}`.

   `create_tool`

   ```json
   {
     "name": "notify_call_ended",
     "description": "Tell the client's system that a conversation ended.",
     "source": "builtin",
     "config": {"endpoint": "https://crm.example.com/hooks/call-ended", "method": "POST"},
     "input_schema": {"type": "object", "properties": {}},
     "fixed_parameters": {
      "event": "conversation_ended",
      "conversation_id": "$ref:conversation.id"
    },
     "connection_id": "{{step_1.id}}"
   }
   ```

3. An agent publishes only with an AI profile; one LLM setting makes one.

   `create_llm_settings`

   ```json
   {"name": "fast", "provider": "openai", "model": "gpt-4o-mini"}
   ```

4. The profile.

   `create_profile`

   ```json
   {"name": "default", "text": {"llm_settings_id": "{{step_3.id}}"}}
   ```

5. The agent.

   `create_agent`

   ```json
   {"name": "Support line"}
   ```

6. Its draft: the version to edit and the graph to fill.

   `get_draft` (`agent_id` = `{{step_5.id}}`)

   ```json
   {}
   ```

7. A root node.

   `add_node` (`graph_id` = `{{step_6.graph_id}}`)

   ```json
   {"instructions": "You answer support questions briefly and hand off to a person when asked."}
   ```

8. Named as the root.

   `set_root` (`graph_id` = `{{step_6.graph_id}}`, `node_id` = `{{step_7.id}}`)

   ```json
   {}
   ```

9. The hook is a property of the draft, next to the profile.

   `edit_draft` (`version_id` = `{{step_6.id}}`)

   ```json
   {"ai_profile_id": "{{step_4.id}}", "post_hook_tool_ids": ["{{step_2.id}}"]}
   ```

10. Published: the hook runs after every conversation from here on.

    `publish_version` (`version_id` = `{{step_6.id}}`)

    ```json
    {}
    ```

## What the agent sees

Nothing. The tool is in the `post` slot, not on a node, so the model is never
offered it; it runs once, after the conversation, and every attempt is a tool
call in the interaction's trace.

## Variations

- **Several steps, or a branch**: the same work as an automation. Put the
  webhook tool in a flow and create an automation with
  `trigger_kind: "interaction_ended"`; its run starts with `interaction_id`,
  `agent_id`, `channel`, `direction`, `end_reason`, `started_at`, `ended_at`,
  `duration_seconds`, `contact_spoke` and `usage`, all readable as
  `$ref:trigger.<key>`:

  ```json
  {
    "name": "Push ended conversations",
    "trigger_kind": "interaction_ended",
    "trigger_config": {"agent_id": "<the agent's id, or omit for every agent>"},
    "flow_id": "<the flow's id>",
    "min_interval_seconds": 300
  }
  ```

- **Only calls that ended one way**: `trigger_config.end_reasons` on that
  automation lists the end reasons that count.
- **Before the conversation instead of after**: the same tool in
  `pre_hook_tool_ids`; its output is injected as context for the first word.
