Roxels/ docs
webhooks

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:

  1. 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.
  2. 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.