Webhook payload shape
You control exactly what Roxels sends. Webhooks aren't a fixed shape — they're a templated HTTP request you configure per goal.
What you configure
For each webhook output, you set:
| Field | What it is |
|---|---|
url |
The destination URL. Supports {{context.*}} and {{secret:*}} interpolation. |
method |
POST (default), PUT, PATCH, GET, or DELETE. |
headers |
A map of header name → value. Values support interpolation. |
body_template |
Jinja-like template for the request body. Variables include {{data.*}}, {{context.*}}, {{secret:*}}. |
schema |
Optional JSON Schema describing the body. Used for client-side validation and shown to the LLM. |
raw_payload |
If true, the body is the raw committed data (no body_template). |
Variables available in templates
| Variable | What it is |
|---|---|
{{data.<field>}} |
A field from the committed goal data. |
{{context.<key>}} |
A value from the conversation context (what you passed when creating the session). |
{{secret:<id>}} |
A secret from your Roxels secret store. Resolved at fire time; never visible in templates. |
{{session.id}} |
The session id. |
{{interview.id}} |
The conversation (interview) id. |
{{template.id}} |
The template id. |
{{goal.id}} |
The goal id whose commit triggered the fire. |
{{commit_id}} |
The unique commit id (also sent as Idempotency-Key). |
{{committed_at}} |
ISO-8601 timestamp of the commit. |
Example: POST JSON to your service
Config:
- URL:
https://api.example.com/v1/customers - Method:
POST - Headers:
Authorization: Bearer {{secret:abc-123}} Content-Type: application/json - Body template:
{ "name": "{{data.name}}", "email": "{{data.email}}", "source": "roxels", "external_id": "{{context.external_id}}", "captured_at": "{{committed_at}}" }
Result, on fire:
POST /v1/customers HTTP/1.1
Host: api.example.com
Authorization: Bearer sk-xxxxxxxxxx
Content-Type: application/json
Idempotency-Key: commit_a1b2c3
User-Agent: Roxels-Webhook/1
X-Roxels-Signature: t=1716480000,v1=...
{
"name": "Ada Lovelace",
"email": "ada@example.com",
"source": "roxels",
"external_id": "user_123",
"captured_at": "2026-05-23T12:34:56Z"
}Example: URL with path parameter
Sometimes the destination depends on the captured data — for example, you have a per-customer webhook URL.
Config:
- URL:
https://api.example.com/v1/customers/{{context.external_id}}/notes - Method:
POST - Body template:
{ "note": "{{data.use_case}}", "source": "onboarding-conversation" }
{{context.external_id}} interpolates from the session's context (which you passed at session creation).
Example: raw payload (no template)
If you want Roxels to send the goal's committed data as-is, set raw_payload: true and skip body_template.
Config:
- URL:
https://api.example.com/v1/raw-capture raw_payload: true
Result:
The request body is exactly the goal's committed data:
{
"name": "Ada Lovelace",
"email": "ada@example.com"
}Useful when you have your own ingestion pipeline that already knows the shape.
Schema validation
You can attach a JSON Schema to the output. Two things happen with it:
- The schema is shown to the agent's commit-author tool so the agent knows the exact shape required and can self-correct before committing.
- The rendered body is validated against the schema before sending. Failed validation is logged and the webhook is held back (retryable failure — see Retries).
{
"type": "object",
"required": ["name", "email"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" }
}
}Default headers Roxels adds
Even if you don't specify them, every outbound webhook includes:
| Header | Value |
|---|---|
Idempotency-Key |
The commit id. Use this to deduplicate on your side. |
X-Roxels-Signature |
HMAC signature for replay/tamper protection. |
User-Agent |
Roxels-Webhook/1. |
Any header you set in the config overrides the defaults except Idempotency-Key and X-Roxels-Signature (those are always Roxels-controlled).
Multiple outputs per goal
Configure as many outputs as you need. Each fires independently. A common pattern:
- Webhook 1: POST to your customer-data warehouse.
- Webhook 2: POST to your CRM.
- Frontend callback: update the UI.
If one fails, the others still go through. Retries happen per-output, not as a batch.
Read next
- Webhooks overview — Concepts, signature verification, idempotency.
- Retries and idempotency — What happens when your endpoint is down.
- Embed events — Frontend equivalent.