Webhook Payload Customization
Customize outbound webhook payloads with field selection, templates, and per-event overrides
Webhook Payload Customization
TotalAccess allows you to customize the payload sent to your webhook endpoints. This guide covers the three customization modes, per-event template overrides, and live preview.
Overview
Each webhook can be configured with a payload template that transforms the default payload before delivery. The payload_template field on the webhook configuration accepts a JSON object with the following structure:
{
"mode": "passthrough | fields | template",
"fields": ["event", "data.id", "data.status"],
"template": { "event_type": "{{ event }}", "id": "{{ data.id }}" },
"per_event": {
"job.completed": { "mode": "fields", "fields": ["event", "data.work_item_id"] }
}
}Modes
1. Passthrough (Default)
Sends the full payload as-is. No transformation is applied.
{
"mode": "passthrough"
}Example output:
{
"event": "job.completed",
"version": "1.0",
"timestamp": "2026-07-22T14:30:00Z",
"data": {
"work_item_id": "550e8400-...",
"status": "completed",
"title": "Install solar panels",
"assigned_to": { "id": 1, "name": "John Doe" }
},
"metadata": { "client_id": 1 }
}2. Field Selection
Includes only the specified field paths from the payload. Uses dot notation for nested fields.
{
"mode": "fields",
"fields": ["event", "data.work_item_id", "data.status", "timestamp"]
}Example output:
{
"event": "job.completed",
"data": {
"work_item_id": "550e8400-...",
"status": "completed"
},
"timestamp": "2026-07-22T14:30:00Z"
}Field paths use dot notation (e.g., data.assigned_to.name). Only existing fields are included — missing paths are silently skipped.
3. Custom Template
Define a custom JSON structure using {{ field.path }} interpolation. The template is a JSON object where string values can contain interpolation expressions.
{
"mode": "template",
"template": {
"event_type": "{{ event }}",
"job_id": "{{ data.work_item_id }}",
"status": "{{ data.status }}",
"assignee": "{{ data.assigned_to.name }}",
"completed_at": "{{ timestamp }}"
}
}Example output:
{
"event_type": "job.completed",
"job_id": "550e8400-...",
"status": "completed",
"assignee": "John Doe",
"completed_at": "2026-07-22T14:30:00Z"
}Per-Event Template Overrides
You can define different templates for specific event types within a single webhook. The per_event field maps event names to template configurations.
{
"mode": "fields",
"fields": ["event", "data.id", "data.status", "timestamp"],
"per_event": {
"job.completed": {
"mode": "template",
"template": {
"type": "job_done",
"id": "{{ data.work_item_id }}",
"completed_by": "{{ data.assigned_to.name }}"
}
},
"job.created": {
"mode": "passthrough"
}
}
}When an event is delivered:
- If
per_eventcontains an entry for the event, that template is used - Otherwise, the default (top-level) template is applied
Per-event overrides are useful when different events have different consumer requirements — e.g., a compact payload for status updates but a full payload for creation events.
Template Preview
The OutboundWebhookModal includes a live preview tool that renders your template against sample data:
- Select an event type from the dropdown
- Click Preview to see the rendered output
- The sample input and rendered output are displayed side-by-side
You can also use the API directly:
POST /api/v1/work-items/webhooks/template-preview
Content-Type: application/json
Authorization: Bearer <token>
{
"event": "job.completed",
"template": {
"mode": "fields",
"fields": ["event", "data.work_item_id", "data.status"]
}
}Response:
{
"success": true,
"event": "job.completed",
"sample_payload": {
"event": "job.completed",
"version": "1.0",
"timestamp": "2026-07-22T14:30:00Z",
"data": {
"work_item_id": "550e8400-...",
"status": "completed",
"title": "Sample Work Item"
}
},
"rendered_payload": {
"event": "job.completed",
"data": {
"work_item_id": "550e8400-...",
"status": "completed"
}
}
}DLQ Alerts
Each webhook can be configured with DLQ (Dead Letter Queue) alert settings:
| Field | Type | Default | Description |
|---|---|---|---|
dlq_alert_enabled | boolean | false | Enable DLQ alerts for this webhook |
dlq_alert_threshold | integer | 10 | Alert when DLQ count exceeds this in 24h |
dlq_alert_email | string | null | Recipient email (null = use webhook creator's email) |
When enabled, an alert email is sent when the number of dead-lettered deliveries in the past 24 hours exceeds the threshold. Alerts are rate-limited to 1 per hour per webhook.
Configuration via API
PATCH /api/v1/work-items/webhooks/:id
Content-Type: application/json
Authorization: Bearer <token>
{
"dlq_alert_enabled": true,
"dlq_alert_threshold": 5,
"dlq_alert_email": "[email protected]"
}Configuration via UI
In the OutboundWebhookModal, scroll to the DLQ Alert Configuration section:
- Check "Enable DLQ alerts for this webhook"
- Set the threshold (number of DLQ items in 24h before alerting)
- Optionally specify an alert email (defaults to the webhook creator's email)
Auto-Expiry
Dead-lettered deliveries are automatically expired after 30 days (configurable) by the webhook maintenance cron job:
POST /api/v1/cron/webhook-maintenance
x-cron-secret: <secret>
{
"dlq_retention_days": 30
}Expired items are marked with status = 'expired' and the reason is appended to dlq_reason.
Signing Key Rotation
When a signing key is rotated:
- A new key is generated and becomes the active signing key
- The old key enters a grace period (default: 72 hours)
- Both keys are valid for signature verification during the grace period
- An email notification is sent to the webhook owner
- After the grace period, the old key is automatically deactivated
Rotation Notification
The notification email includes:
- New key version
- Grace period duration
- Old key validity end time
- Action required message
Auto-Deactivation
The webhook maintenance cron job automatically deactivates keys whose grace period has ended:
POST /api/v1/cron/webhook-maintenance
x-cron-secret: <secret>This runs both cleanupExpiredDlqItems and cleanupExpiredKeys in a single call.