Skip to content

Webhook payload templates

Webhook templates let you replace the default webhook payload with custom text. A template is evaluated separately for every webhook event and can use the fields of that event as variables. The rendered payload can use JSON, XML, plain text, or any other format expected by the receiving system.

Configure a template in Settings Integration Targets when creating or editing a webhook target. Leave the field empty to send the default webhook payload.

Note

A template replaces the complete request body, and the rendered text is sent exactly as produced. Regardless of the payload format, the HTTP Content-Type header is application/json. The receiving system must accept the rendered payload with this content type.

Available variables

The following variables are available for every event:

Variable Type Description
eventId string Unique identifier of the event.
queryId string Identifier of the Rule that produced the event.
queryDisplayName string Display name of the Rule.
queryTypeId string Identifier of the Use Case.
queryTypeDisplayName string Display name of the Use Case.

A payload also contains exactly one of these variables:

Variable Type Description
detections array Detections affected by an alarm event. Detection fields use names such as video_id, frame_time, track_id, and obj_type.
result object A counting result containing its aggregation window and count or statistical values.

For complete field descriptions and examples, see Webhook payload.

Custom header values

The custom header value supports the same variables and template syntax as the request body. The custom header key is static and must be a valid HTTP header name.

For example, configure the custom header key as X-Query-Id and its value as:

{{ queryId }}

Each request then contains the query identifier in the X-Query-Id header. The rendered value must not contain line breaks or other control characters.

Variable substitution

Use {{ and }} to insert a value.

{
  "eventId": "{{ eventId }}",
  "query": "{{ queryDisplayName }}",
  "queryType": "{{ queryTypeDisplayName }}"
}

Templates do not have to produce JSON. For example, a plain-text payload can be written as:

Event {{ eventId }} from {{ queryDisplayName }}
{% for detection in detections %}
- {{ detection.obj_type }} on video {{ detection.video_id }}
{% endfor %}

When producing JSON, surround substituted strings with quotation marks. Numeric and Boolean values should normally remain unquoted.

Use a dot to access an object field and brackets to access an array item:

{
  "videoId": "{{ detections[0].video_id }}",
  "objectType": "{{ detections[0].obj_type }}",
  "trackId": {{ detections[0].track_id }}
}

Variable and field names are case-sensitive.

Loops

Use a for block to process every item in an array. The block ends with endfor.

{
  "eventId": "{{ eventId }}",
  "objects": [
{% for detection in detections %}
    {
      "type": "{{ detection.obj_type }}",
      "trackId": {{ detection.track_id }},
      "confidence": {{ detection.confidence }}
    }{% if not loop.last %},{% endif %}
{% endfor %}
  ]
}

The special loop variable provides information about the current iteration:

Variable Description
loop.index Current iteration number, starting at 1.
loop.index0 Current iteration number, starting at 0.
loop.first true for the first iteration.
loop.last true for the last iteration.

When producing JSON, the loop.last check in the example prevents a trailing comma after the final object.

Conditionals

Use if, elseif, and else blocks to select output based on the event. The block ends with endif.

{
  "eventId": "{{ eventId }}",
{% if detections %}
  "kind": "detection",
  "itemCount": {{ detections | length }}
{% elseif result %}
  "kind": "counting-result",
  "windowStart": "{{ result.windowStart }}",
  "windowEnd": "{{ result.windowEnd }}"
{% else %}
  "kind": "unknown"
{% endif %}
}

Use is not null before accessing an optional field:

{% if result.count is not null %}
  "value": {{ result.count }}
{% elseif result.minimum is not null %}
  "average": {{ result.average }},
  "minimum": {{ result.minimum }},
  "maximum": {{ result.maximum }}
{% else %}
  "average": {{ result.average }}
{% endif %}

Conditions support the comparison operators ==, !=, >, <, >=, and <=, and the logical operators and, or, and not.

{% if detection.confidence >= 0.8 and detection.obj_type == "person" %}
  "priority": "high"
{% else %}
  "priority": "normal"
{% endif %}

Filters

Filters transform a value and are applied with the | character. Common filters include:

Filter Example Result
length {{ detections | length }} Number of detections.
upper {{ queryDisplayName | upper }} Uppercase text.
lower {{ queryDisplayName | lower }} Lowercase text.
default {{ value | default("unknown") }} Fallback when a value is empty or missing.
join {{ values | join(", ") }} Array values joined into text.

Filters can be chained from left to right:

"query": "{{ queryDisplayName | default("unnamed query") | upper }}"

Troubleshooting

  • Ensure the rendered output follows the format expected by the receiving system. For JSON, pay particular attention to quotation marks and commas around loops and conditionals.
  • Use the exact variable names shown on this page. Names are case-sensitive.
  • Guard optional fields with is not null before using them.
  • Remember that detections and result are mutually exclusive.
  • The integration-target connection test sends a fixed request and uses sample event values for a templated custom header. Validate the rendered body and actual header value by running a query that uses the target.
  • If template evaluation fails, no webhook request is sent for that event.