Skip to main content
Browse Documentation

Webhook payloads

Created by r.saunders, last modified by zengenti on 09 Apr 2021

While creating or updating a webhook, Contensis allows you to send a JSON payload to the specified URL whenever a selected event occurs.

Payload types

We support different payload types to give you flexibility and support different development scenarios.

  • Default payloads are an ideal way for a development team to get the barebones information they require about a change in a resource. Once the endpoint is notified, the appropriate API calls and data mapping can take place in the application.
  • Custom payloads offer the opportunity to reduce the need for a middleware application and provide the endpoint with the data and structure that is expected. This is especially useful for services that have an incoming webhook feature like Slack.
  • Empty payloads can be useful if you are providing the the information you require through a templated webhook endpoint URL.

Default payloads

When you add a topic to a webhook we automatically provide a default payload template that will be sent with the webhook. The payload template varies depending on the topic that has been set.

Entries example

The following example outlines the payload that will be sent when the entries topic has been added to a webhook. The webhook payload is structured in the JSON format.

{
    "eventId": "{{event.id}}",
    "eventName": "{{event.name}}",
    "subscriptionId": "{{event.subscriptionId}}",
    "datetime": "{{event.dateTime | date.to_string '%Y-%m-%dT%H:%M:%S.%LZ'}}",
    "invokedBy": {
        "username": "{{user.username}}",
        "firstName": "{{user.firstName}}",
        "lastName": "{{user.lastName}}"
    },
    "resource": {
        "id": "{{event.resourceId}}",
        "type": "entry",
        "projectId": "{{resource.sys.projectId}}",
        "contentTypeId": "{{resource.sys.contentTypeId}}",
        "title": "{{resource.entryTitle}}",
        "language": "{{resource.sys.language}}",
        "url": "{{event.resourceUrl}}"
    }
}

Let's walk through each object in a typical webhook payload template. As you'll see in the example we use the Liquid syntax to define the output of the webhook. Each value in the curly braces represents a value of a Contensis resource which is replaced when the webhook is sent.

Element   Type Description
eventId   string Unique ID for the specific webhook event. Automatically assigned by Contensis.
eventName     The name of the event that triggered the webhook e.g. Created, Updated, Published, Unpublished, workflowStateChange, WorkflowEventRaised.
subscriptionId     The ID of the event subscription.
datetime     The date and time that the event was raised. Formatted to UTC string.
invokedBy      
  username   The username of the person who triggered the webhook being sent.
  firstName   The first name of the person who triggered the webhook being sent, if available from their user profile.
  lastName   The last name of the person who triggered the webhook being sent, if available from their user profile.
resource      
  id   The unique ID of the resource.
  type   The resource type, in this example it has a type of entry.
  projectId   The API ID of the project that the resource relates to.
  contentTypeId   The Content type ID that the entry is based on.
  title   The title of the entry.
  language   The language code of the entry.
  url   The full CMS url for the entry

Custom payloads

You can customise the default template, or completely replace the template with your own to meet the needs of your endpoint.

Liquid templating provides a way to construct really flexible payloads without requiring additional API calls.

Example

The following example demonstrates a custom payload for Slack using their Block Kit syntax.

{
	"text": "{{resource.entryTitle}} has been {{event.name}}",
	"blocks": [
		{
			"type": "header",
			"text": {
				"type": "plain_text",
				"text": "{% assign handle = event.name %} {% case handle %} {% when "workflowStateChanged" %}:page_with_curl: An entry has been transitioned to the {{resource.sys.workflow.state}} workflow state.{% else %}:page_with_curl: An entry has been {{event.name}}{% endcase %}",
				"emoji": true
			}
		},
		{
			"type": "section",
			"text": {
				"type": "mrkdwn",
				"text": "*Entry title:*\n<https://cms-{{event.alias}}.cloud.contensis.com/app/projects/{{resource.sys.projectId}}/entries/{{resource.sys.id}}?language={{resource.sys.language}}>\n\n{% if resource.entryDescription %}*Entry description:*\n {{resource.entryDescription}}{% endif %}"
			}
		},
		{
			"type": "divider"
		},
		{
			"type": "section",
			"fields": [
				{
					"type": "mrkdwn",
					"text": "*Content type:*\n{{resource.sys.contentTypeId}}"
				},
				{
					"type": "mrkdwn",
					"text": "*Last updated:*\n{{event.dateTime | date.to_string "%Y-%m-%d at %H:%M"}}"
				},
				{
					"type": "mrkdwn",
					"text": "*Updated by:*\n{{resource.sys.version.modifiedBy}}"
				},
				{
					"type": "mrkdwn",
					"text": "*Entry ID:*\n{{resource.sys.id}}"
				}
			]
		},
		{
			"type": "divider"
		},
		{
			"type": "actions",
			"elements": [
				{
					"type": "button",
					"text": {
						"type": "plain_text",
						"text": ":pencil2: Edit entry",
						"emoji": true
					},
					"value": "edit_entry",
					"url": "https://cms-{{event.alias}}.cloud.contensis.com/app/projects/{{resource.sys.projectId}}/entries/{{resource.sys.id}}?language={{resource.sys.language}}"
				}
			]
		}
	]
}

Liquid examples

Checking if a value is empty

Nil is a special empty value that is returned when Liquid code has no results.

In the following example, if an entryDescription does not exist (that is, resource.entryDescription returns nil), Liquid will not print the text.

{% if resource.entryDescription %}
	*Entry description:* {{resource.entryDescription}}
{% endif %}

The above example has been formatted for readability. As payloads in webhooks are in JSON, this example would not be a valid string and would fail JSON validation.

Instead you'd have to format the example without any returns as below.

{% if resource.entryDescription %}*Entry description:* {{resource.entryDescription}}{% endif %}

case/when

Creates a switch statement to compare a variable with different values. case initialises the switch statement, and when compares its values.

In this example, the output will be determined when the variable called handle is "equal" to the value of workflowStateChanged. If it does not match it will output the text in the else clause.

{% assign handle = event.name %}
	{% case handle %}
	{% when "workflowStateChanged" %}
		An entry has been transitioned to the {{ resource.sys.workflow.state }} workflow state.
	{% else %}
		An entry has been {{ event.name }}
{% endcase %}

Again for this example to work in the payload you'd need to ensure it validates as a string, as formatted below.

{% assign handle = event.name %} {% case handle %} {% when "workflowStateChanged" %}An entry has been transitioned to the {{ resource.sys.workflow.state }} workflow state.{% else %}An entry has been {{ event.name }}{% endcase %}

Custom Liquid filters

Filters are simple methods that modify the output of numbers, strings, variables and objects. They are placed within an output tag {{ }} and are denoted by a pipe character |.

We have provided the following custom Liquid filters to use with webhook payloads.

to_json and to_xml

You can use these filters to render part of a resource, or the entire resource to JSON or XML.

The following example will output the entire resource e.g. an entry to JSON

{{ resource | to_json }}

Where as this example will output the field of a resource to XML.

{{ resource.somefield | to_xml }}

Empty payloads

In some cases you may want to send an empty payload for your webhook. You can do this by checking the Do not send payload checkbox above the payload field.

XML payloads

Whilst our webhooks services have been designed to deliver JSON payloads by default you can also define your payload as XML.

You will need to edit the payload as normal and replace the JSON structure as XML. In addition to this you'll need to set a header declaring the content-type as application/xml or the particular flavour of XML you require.