Webhooks notify a person or service when content has changed by calling a preconfigured HTTP endpoint. You can use this for notifications, static site generators or other forms of post-processing sourced from Contentful.
Every webhook request includes the following predefined headers:
Header Name ValueX-Contentful-Topic
ContentManagement.[Type].[Action]
X-Contentful-Webhook-Name
Webhook's name Content-Type
application/vnd.contentful.management.v1+json
Consuming services can use the X-Contentful-Topic
header to determine the type of the payload included in the webhook call without looking into it. The topics depend on various actions which are described in the concepts section.
X-Contentful-Topic
header can have the following values:
ContentManagement.ContentType.create
ContentManagement.ContentType.save
ContentManagement.ContentType.publish
ContentManagement.ContentType.unpublish
ContentManagement.ContentType.delete
ContentManagement.Entry.create
ContentManagement.Entry.save
ContentManagement.Entry.auto_save
ContentManagement.Entry.archive
ContentManagement.Entry.unarchive
ContentManagement.Entry.publish
ContentManagement.Entry.unpublish
ContentManagement.Entry.delete
ContentManagement.Asset.create
ContentManagement.Asset.save
ContentManagement.Asset.auto_save
ContentManagement.Asset.archive
ContentManagement.Asset.unarchive
ContentManagement.Asset.publish
ContentManagement.Asset.unpublish
ContentManagement.Asset.delete
Other event types
ContentManagement.BulkAction.create
ContentManagement.BulkAction.execute
ContentManagement.Release.create
ContentManagement.Release.save
ContentManagement.Release.delete
ContentManagement.ReleaseAction.create
ContentManagement.ReleaseAction.execute
ContentManagement.ScheduledAction.create
ContentManagement.ScheduledAction.save
ContentManagement.ScheduledAction.delete
Additionally, extra headers may be emitted to provide context for an action, such as an entry being published by a scheduled action or as part of a release.
Header Name Valuex-contentful-scheduled-action-id
The sys.id
of the ScheduledAction that was executed. Present only when executed via a scheduled action x-contentful-bulk-action-id
The sys.id
of the BulkAction that was executed. Present only when executed via a bulk action x-contentful-release-action-id
The sys.id
of the ReleaseAction that was executed. Present only when executed via a release action x-contentful-release-id
The sys.id
of the Release that contained this Entry or Asset. Present only when executed via a release action x-contentful-release-version
The sys.version
of the Release that contained this Entry or Asset at execution time. Present only when executed via a release action
Consuming services can use these headers to correlate events across multiple webhook calls to group the calls into a single downstream action, such as debouncing a rebuild of a website
When triggered by an App Action call or an App Installation, Asset, or Entry event, the X-Contentful-CRN
header is included. This header provides the Contentful Resource Name (crn), an API identifier of the entity that triggered the webhook.
Besides these headers, you can configure webhooks to be called with a set of additional headers of your choice. When creating or updating a webhook, you can provide a list of headers that will be included in successive calls to that webhook.
For example assume that:
One of your entries has just been published.
The name of your webhook is 'Notify subscribers'.
You have defined two custom headers, X-Notify: subscribers
and Authentication: subscribers
.
The webhook would be called with the following set of headers:
X-Contentful-Topic: ContentManagement.Entry.publish
X-Contentful-Webhook-Name: Notify subscribers
X-Notify: subscribers
Authentication: subscribers
Custom headers are provided with the headers
property of the Webhook Definition:
{
...,
"headers": [
{ "key": "X-Notify", "value": "subscribers" },
{ "key": "Authentication", "value": "subscribers" }
]
}
You can mark a header as secret. Value of secret headers is hidden in the web app, API responses and logs. The first time you define a secret header you need to provide its value and the secret
flag set to true
:
{
...,
"headers": [
{ "key": "X-Notify", "value": "subscribers" },
{ "key": "Authentication", "value": "subscribers", "secret": true }
]
}
Any consecutive update can omit the value
property as long as the secret
is true
. Previously provided value will be used in this scenario:
{
...,
"headers": [
{ "key": "X-Notify", "value": "updated subscribers" },
{ "key": "Authentication", "secret": true }
]
}
Header values may contain values from the original payload: see header transformation.
When you've configured a webhook signing secret in the webhook settings of your space, a signature will be generated for all webhook requests coming from events in that space. When a consuming service receives a request, it can compute a signature in the same way. If the secret is the same, the signatures will match too, thus verifying that the sender knows the secret. For this reason, it is also important to not share the secret with any third party.
This signature and some additional metadata is then added to the headers of the request before it is sent. The headers look like this:
Header Name Valuex-contentful-signature
The computed signature of an individual request. x-contentful-signed-headers
A comma-separated list of headers included in the computed signature. x-contentful-timestamp
Timestamp of when the request was signed. Can be used to ensure a TTL and avoid processing stale or replayed events.
Consuming services can use these headers to verify the authenticity and integrity of incoming webhooks. See verifyRequest in node-apps-toolkit for more information.
BodyThe payload received by a webhook will change depending on the action type:
For Publish
it will receive the latest published version. The same payload as the CDA with the exception that the payload for Entries and Assets will contain all locales, e.g. instead of fields.name
will be fields['en-US'].name
.
For Unpublish
and Delete
it will receive a deletion object. In the case of Entries, the deletions are extended with the id of their Content Type.
For Auto Save
, Archive
, Unarchive
and Create
it will receive the latest draft version at the time the webhook was triggered. That's the same payload as the one available through the Management API.
Contentful uses the HTTP response status code of the webhook call to decide whether it succeeded or failed:
Success: The webhook responded with an HTTP status code < 300.
Error: The webhook responded with an HTTP status code >= 300.
In case of an error with a status code 429 or >= 500, the webhook periodically retries its request.
To learn more about webhooks retry policy, refer to
Retry policy.
Filtering webhook callsIn many use cases receiving a webhook call for all the events taking place in Contentful leads to complex filtering logic that has to be implemented on the receiving side. There are two mechanisms allowing webhook calls to be triggered conditionally:
selection of a triggering event(s)
filtering based on properties of a webhook payload
An event is a combination of the entity type and an action performed on it. For example the Entry.save
event happens when an entry is updated with the CMA. ContentManagement.Entry.save
is the topic for this event.
To select which events should trigger a webhook call set the topics
property of a webhook to one or more event names. You can use a wildcard character *
to indicate all entity types or actions should cause a webhook call. Please note that if you use the wildcard character and we add new entity types or actions your webhook receiver will be called with new events.
Possible values for topics
are:
["*.*"]
will trigger a webhook call for all the supported events in Contentful
["Entry.*"]
will trigger a webhook call for all actions performed on entries
["*.save"]
will trigger a webhook call when any of supported entities is saved
["Entry.save", "Entry.delete"]
will trigger a webhook call only for the events listed
Once triggering events are selected webhook calls can be narrowed down further using webhook payload filtering.
There are 6 webhook payload properties you can filter on:
sys.id
- entity ID
sys.environment.sys.id
- environment ID
sys.contentType.sys.id
- content type ID (applicable only to Entry events)
sys.createdBy.sys.id
- User ID, created by (not applicable to Unpublish and Delete events)
sys.updated.sys.id
- User ID, updated by (not applicable to Unpublish and Delete events)
sys.deletedBy.sys.id
- User ID, deleted by (only applicable to Unpublish and Delete events)
These properties can be used as doc
in the filters. There are 6 filters available:
{"equals": [will trigger only for entity with ID
{"doc": "sys.id"},
"main_nav"
]}
main_nav
negation of equality
{"not": {"equals": [will trigger for all entities but entity with ID
{"doc": "sys.id"},
"main_nav"
]}}
main_nav
inclusion
{"in": [will trigger if environment of an entity is either
{"doc": "sys.environment.sys.id"},
["qa", "staging"]
]}
qa
or staging
negation of inclusion
{"not": {"in": [will trigger if environment of an entity is neither
{"doc": "sys.environment.sys.id"},
["qa", "staging"]
]}}
qa
nor staging
regular expression
{"regexp": [will trigger for all environments prefixed with
{"doc": "sys.environment.sys.id"},
{"pattern": "^ci-.+$"}
]}
ci-
negation of regular expression
{"not": {"regexp": [will trigger for all environments that are not prefixed with
{"doc": "sys.environment.sys.id"},
{"pattern": "^ci-.+$"}
]}}
ci-
The filters
property of a webhook holds zero or more filters in an array. While deciding if a webhook should be called all filters are joined with logical AND
.
If there is no value for the filters
property (or it is null
) then by default a webhook will be triggered only in the master
environment.
Combining all the mechanisms together we can achieve fine-grained webhook calls. The following webhook will be triggered only for (un)publish actions performed on entries with IDs main_nav
or footer_nav
in all environments prefixed with test-
:
{
"name": "My webhook",
"url": "https://my-webhook-receiver.com/ping",
"topics": [
"Entry.publish",
"Entry.unpublish"
],
"filters": [
{"in": [{"doc": "sys.id"}, ["main_nav", "footer_nav"]]},
{"regexp": [{"doc": "sys.environment.sys.id"}, {"pattern": "^test-.+$"}]}
]
}
Transforming webhook calls
By default every webhook call:
uses the POST
HTTP method
has the Content-Type
header set to application/vnd.contentful.management.v1+json
does not have the Content-Length
header set
sends a predefined body depending on the triggering event (as described above)
If you control the code of your webhook receiver you can respond to the default webhook call appropriately by implementing a custom logic in your receiver. The situation is different if you try to call an API you don't control with a webhook: most likely the default webhook payload won't adhere to the format expected by the API.
The transformation
property of a webhook definition allows you to define:
custom HTTP method
custom Content-Type
header
if automatically computed Content-Length
header should be included
custom webhook call body that may or may not make use of the default webhook body
The transformation
property is an optional object containing the following properties. None of these properties is required.
transformation.method
can be one of:
POST
(default)
GET
PUT
PATCH
DELETE
transformation.contentType
can be one of:
application/vnd.contentful.management.v1+json
(default)
application/vnd.contentful.management.v1+json; charset=utf-8
application/json
application/json; charset=utf-8
application/x-www-form-urlencoded
application/x-www-form-urlencoded; charset=utf-8
Using the last two options will convert the JSON body to URL encoded form data.
transformation.includeContentLength
can be either true
or false
. If true
the Content-Length
header will be present with its value set to automatically computed byte length of the request body.
transformation.body
can be any arbitrary JSON data structure. It will be used as the webhook call body. It's possible to use values from the original webhook payload in the transformed body. This can be achieved by JSON pointers in the body JSON. Depending on the referenced data either whole values or strings are resolved.
Webhook URLs can contain JSON pointers that are resolved to values from the original payload. The value resolution always follow the string template resolution logic. For example a webhook defined as follows will result in a call to https://my-webhook-endpoint.com/my-entry-1
for an entity with ID my-entry-1
.
{
"name": "Dynamic URL",
"url": "https://my-webhook-endpoint.com/{ /payload/sys/id }"
}
Custom header values can contain JSON pointers as described below. This is achieved by JSON pointers that are resolved using the string template resolution logic.
The following header definition
{
...,
"headers": [
{ "key": "X-CTFL-Entity-ID", "value": "{ /payload/sys/id }" },
{ "key": "X-CTFL-Entity-Type", "value": "{ /payload/sys/type }" }
]
}
will result in two headers on the actual webhook call if the webhook was triggered by a change of entry 42:
X-CTFL-Entity-ID: 42
X-CTFL-Entity-Type: Entry
Secret and HTTP Basic Auth headers are not transformed. Also, the header keys are not transformed.
Resolution strategies Resolving a whole valueIn your data structure introduce a string value that starts with {
and ends with }
in a place you want to resolve the original value
In between curly braces put an absolute JSON pointer to the property you want to resolve; the original webhook body is stored in the payload
top-level namespace
The string value containing a pointer will be replaced with the resolved value
For example, given the following original payload:
{
"sys" {
"id": "entry-id"
"type": "Entry"
},
"fields": {
"title": {
"en-US": "hello world"
}
}
}
And the following body transformation:
{
"entryId": "{ /payload/sys/id }",
"title": "{ /payload/fields/title }"
}
Your webhook will be called with:
{
"entryId": "entry-id",
"title": {
"en-US": "hello world"
}
}
Using string templates
In your data structure introduce a string value that contains an absolute JSON pointer to the property you want to resolve, wrapped with {
and }
The original webhook body is stored in the payload
top-level namespace
JSON pointers will be replaced with resolved values
All values, including complex ones, will be stringified
For example, given the following original payload:
{
"sys": {
"id": "entry-id",
"type": "Entry"
},
"fields": {
"title": {
"en-US": "hello world"
}
}
}
And the following body transformation:
{
"entityInfo": "Entity of type { /payload/sys/type } with ID { /payload/sys/id }",
"title": "Entity title is { /payload/fields/title/en-US }",
"stringified": "Let's try to stringify an object: { /payload/fields/title }"
}
Your webhook will be called with:
{
"entityInfo": "Entity of type Entry with ID entry-id",
"title": "Entity title is hello world",
"stringified": "Let's try to stringify an object: {\"en-US\":\"hello world\"}"
}
Helpers
In some cases, there can be a need for trimming or slightly modifying certain fields before delivering them to the webhook target. Contentful provides numerous helpers that can be used at the transformation level. For example:
{
"content": "{ first-paragraphs 2 /payload/fields/content/en-US }",
}
Above-mentioned example limits the content
field with first 2 paragraphs. If the content field has more than two paragraphs, the rest of the text gets trimmed. In addition to trimming content, helpers are useful in modifying or refining content. Following is another transformation, using the strip-stop-words
helper:
{
"keywords": "{ strip-stop-words /payload/fields/content }",
}
The above transform filters out stop words and delivers only the keywords in the content
field. For example:
Art is the expression or application of human creative skill and imagination
becomes:
art expression application human creative skill imagination
Refer the table below to see the full list of available functions:
Helper name Parameter Description first-chars Number Selects firstn
characters of the field.
Example:
{ first-chars 6 /payload/fields/content/en-US }
first-words Number Selects first n
words of the field.
Example:
{ first-words 12 /payload/fields/content/en-US }
first-paragraphs Number Selects first n
paragraphs of the field.
Example:
{ first-paragraphs 3 /payload/fields/content/en-US }
strip-stop-words - Strip out stop words (only English is supported).
Example:
{ strip-stop-words /payload/fields/content/en-US }
strip-markdown - Strip out Markdown markup.
Example:
{ strip-markdown /payload/fields/content/en-US }
stringify - Stringify the value pointed to.
Example:
{ stringify /payload/fields }
Transformation context
When transforming request body, URL, and headers, JSON pointers are used to resolve values. Values are resolved from a context object. Currently the context object has three properties:
payload
contains the original webhook payload
topic
is a string containing the webhook topic
user
is a stripped-down version of a User
entity for the user who executed action triggering the webhook call; the only available pointer is { /user/sys/id }
AWS offers over 100 cloud services, with the most prominent examples being data stores (S3, DynamoDB), serverless engines (Lambda), queuing systems (SQS) and many more.
Most AWS services use AWS Signature Version 4 to authenticate requests to their APIs. Computing the signature requires a prepared canonical request (a request without the Authorization
header) and AWS credentials. They are all used as the input for the AWS Signature Version 4 algorithm. Its output is a value that should be used as the Authorization
header.
The headers and body values of a webhook are likely changing between individual requests. This means the canonical request will differ and previously computed Authorization headers will be invalid. The proxy that the AWS Webhook Integration is using will re-compute the signature for every request.
If you have AWS Webhook Integration enabled you can use its proxy within a webhook so all requests will be automatically signed. You can either start from a webhook template or follow these steps to configure it manually:
Create a webhook performing a canonical AWS request to the service you use. All standard features available for webhooks are available.
Replace amazonaws.com
with awsproxy.contentful.com
in the webhook URL.
Set a X-Contentful-AWS-Proxy-Key-Id
header with your AWS Access Key ID.
Set a X-Contentful-AWS-Proxy-Secret
header with your AWS Secret Access Key. Make sure it's marked as secret!
Optionally you can set a X-Contentful-AWS-Proxy-Content-Type
header to a custom Content-Type
value if a service being called requires it (for example application/x-amz-json-1.0
).
The proxy will automatically sign all requests using the credentials provided. Credentials have to have IAM role assigned allowing to perform selected action.
The proxy is an internal system and cannot be accessed from the public Internet. Only our webhook system can use this proxy.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4