Skip to main content

Lifecycle Hooks

A lifecycle hook runs automatically around a call, without the agent deciding to call it.
  • PRE_CALL hooks run before the agent speaks. Use them to fetch context — look the caller up in your CRM, pull their order history — and expose the result to the prompt.
  • POST_CALL hooks run after the call ends. Use them to push the outcome somewhere — log an activity, append a row, open a ticket.
This is the difference between a hook and a custom function: a function is a tool the model chooses to call mid-conversation; a hook always runs, at a fixed point, in a fixed order.
These endpoints return a bare object ({ "hook": … }, { "hooks": [ … ] }) and a bare { "error": … } on failure, rather than the success/data envelope used elsewhere in this API. Validation failures return 422 with an issues array naming each bad field.

Create a hook

A hook needs a source: either an integration (toolSlug plus toolFn) or one of your own custom functions (functionId).
Omit position and the hook is appended to the end of its stage.

input is a template

Values in input are substituted from the call’s variables and from the output of hooks that ran earlier in the same stage. So a second hook can consume the first one’s result.

exposeAs publishes the result to the prompt

"exposeAs": "lead" makes the hook’s response available as {{lead.FirstName}}, {{lead.Company}}, and so on inside the agent’s prompt. It is pre-call only — a post-call hook has no prompt left to feed — and must be unique among that agent’s pre-call hooks.

Response mapping

exposeAs keeps the whole response under one name, so a deep field needs a long path in the prompt: {{account.data.var_business_name}}. responseMap lifts chosen fields out to top-level variables instead, so the prompt can say {{business_name}}. Each row names a path in the hook’s response and the variable it is published as:
If the account lookup answers:
then this prompt line:
renders as Business: Sunrise Bakery (active). The long form, {{account.data.var_business_name}}, keeps working too. Rules:
  • Pre-call only. The hook must have stage: "PRE_CALL" and an exposeAs. A PATCH that clears exposeAs while rows remain is rejected.
  • Not on lookup_row. A Google Sheets lookup already maps its columns in input.columns.
  • At most 200 rows.
  • path is a dot path into the response. It is 1–200 characters with no empty segment (a..b). List items are read by index, e.g. items.0.sku.
  • variable matches ^[a-z_][a-z0-9_]*$ and appears once per hook.
  • Blank values never override. If a path comes back "", null or missing, the variable is left as it was. 0 and false do count.
  • A mapped name beats a per-call variable of the same name. If you pass variables: { "business_name": "…" } at call start and the hook maps business_name with a value, the hook’s value wins. When two hooks map the same name, the later one wins.
  • If the hook fails, nothing is mapped. The variables keep their per-call values, if they had any.
PATCH with "responseMap": null or [] removes every row.

Failure handling

required: true does not abort the call. A hook problem never fails a session — on the post-call stage required is recorded and otherwise ignored, since the caller is already gone. If the agent must not speak without the data, branch on {{pre_call_degraded}} in the prompt.
All pre-call hooks share a 5-second wall budget. Two hooks at timeoutMs: 3000 can exhaust it, and every remaining hook in the stage is skipped and recorded as failed — so keep the sum of your pre-call timeouts under 5000.

Preview before you ship

A mistyped template key does not error; it renders empty and the agent quietly gets nothing. Check it first:
The integration is not called — only the template is rendered. Omit variables to render against built-in samples.
For Google Sheets hooks, GET /agent/{id}/tools/google_sheets/sheet-columns returns the real header row so you can write lookup_row against actual column names instead of guessing.

Ordering

Hooks run by ascending position within their stage. To change the order, send the stage’s hook IDs in the order you want:
Order matters when one hook consumes another’s output.

Turn one off without deleting it

PATCH with enabled: false leaves the hook and its position intact:
Prefer this to DELETE while debugging — deleting loses the configuration and the ordering. stage cannot be changed on an existing hook. Delete it and create it on the other stage.

Debugging

When a hook does not seem to fire, GET /integration-logs has the audit row — what was sent, what came back, the upstream status code, and how long it took:
It covers the last 30 days, successes and failures alike.

Next steps

Custom Functions

Tools the agent chooses to call mid-conversation.

Pre-call Variables

What a pre-call hook can read from and write to.

B2B2B Customers

Connect integrations on behalf of your own customers.

API Reference

Every hook field and error code.