Engine EE 1.3+ Webhook Invocation in Actions
After a watched event on a monitored entity is registered, it is queued within the actions queue. The Actions worker waits for these events to be dispatched and triggers the corresponding webhooks configured for the events.
Batching
To optimize efficiency, events targeting the same webhook can be batched together, allowing for processing multiple events in a single invocation. By default, each batch contains a single event, and the event payload is wrapped in an array. However, you can adjust the batchSize
property in the webhook configuration to specify the maximum number of events per batch.
It's important to note that when processing a batch, all events within the batch are considered either successful or failed based on the HTTP response code. Currently, partial success for individual events within a batch is not supported. If any event in the batch fails, the entire batch is considered failed.
Fetching events
The worker identifies events ready for processing based on their visibility, selecting events with the lowest visibility first. It then groups together events with the same target to form a batch. The payload for each event is already constructed when it is saved to the queue, simplifying the worker's role to dispatching the batched payloads.
Payload Construction and Variable Replacement
In the webhook URL and header values, variables can be used using double curly braces ({{variableName}}
). These variables are replaced with their corresponding values at the time of webhook invocation. It provides flexibility and customization by allowing dynamic content based on the context of the
event.
By default, Contember appends the following headers to the webhook request:
User-Agent: Contember Actions
Content-Type: application/json
These headers identify the source of the webhook request and specify the format of the payload.
Request payload
When a batch of events is dispatched and the corresponding webhook is invoked, the payload sent to the webhook contains a field named events
. This field holds an array of event payloads representing the batched events. Each event payload follows a specific structure based on the type of event triggered.
Example: webhook body
{
"events": [
{
"id": "...",
"entity": "...",
"type": "watch",
// ...
},
/// other events
]
}
Even when a batch is configured to contain only a single event, it is still sent in the events
field as an array with a single item. This consistent structure allows for unified handling of batched events, ensuring consistent processing logic regardless of the number of events in the batch.
Watch Event Payload
A watch
event payload represents a change in the watched entity and provides detailed information about the change. Here is the structure of a watch
event payload:
id
(string or int): The unique identifier of the entity event.entity
(string): The entity type of the watched entity.events
(array of objects): An array of event payloads representing the changes in the watched entity. Each event payload has the following properties:id
(string): The unique identifier for the event.path
(array of string): The path to this entity relative to the watched entity.entity
(string): The entity type of the event.operation
(string): The type of operation performed on the entity, such ascreate
,update
,delete
,junction_connect
, orjunction_disconnect
.- other fields based on operation type
trigger
(string): The name of the trigger associated with the event.operation
(string): The operation type of the event, which is set towatch
for a watch event.selection
(object): Custom payload defined byselection
on a watch definitionmeta
(object): see event meta
Example: a body payload with a watch
event payload for a Book
entity:
{
"events": [
{
"id": "f4f0a97d-7850-4add-8946-a1ce016306ce",
"entity": "Book",
"events": [
{
"id": "f4f0a97d-7850-4add-8946-a1ce016306ce",
"entity": "Book",
"values": {
"title": "Sample Book Title"
},
"operation": "update"
}
],
"trigger": "book_updated_watch",
"operation": "watch",
"selection": {},
"meta": {
"eventId": "73bbf733-36a5-4e5c-8798-1ba1b7900b39",
"transactionId": "53e3790d-5485-490c-9420-a1e79b19b5d3",
"createdAt": "2023-07-25T15:30:17.937Z",
"lastStateChange": "2023-07-25T15:30:17.937Z",
"numRetries": 0,
"trigger": "book_updated_watch",
"target": "book_updated_watch_target"
}
}
]
}
In this example, the watch
event payload represents an update operation on a Book
entity. It includes the updated value of the title
property. The operation
is set to update
, and the associated trigger is book_updated_watch
. Additional information, such as the selection of fields, can be included in the selection
property if specified in the watch configuration.
Trigger Event Payload
Trigger event payloads represent payloads for events invoked by trigger
and contains individual basic events.
id
(string or int): The unique identifier for the entity.entity
(string): The entity type associated with the event.operation
(string): The type of operation performed on the entity. Possible values arecreate
,update
,delete
,junction_connect
, orjunction_disconnect
.selection
(object, optional): Additional information about the selected fields in the event, if specified in the event configuration.meta
(object): see event meta- other fields based on operation type
Here's an example of a basic event payload for an update
operation on a Book
entity:
Example: a body payload with a update
event payload for a Book
entity:
{
"events": [
{
"id": "f4f0a97d-7850-4add-8946-a1ce016306ce",
"entity": "Book",
"values": {
"title": "Updated Book Title",
"author": "John Doe"
},
"operation": "update",
"selection": {},
"trigger": "book_updated_watch",
"meta": {
"eventId": "73bbf733-36a5-4e5c-8798-1ba1b7900b39",
"transactionId": "53e3790d-5485-490c-9420-a1e79b19b5d3",
"createdAt": "2023-07-25T15:30:17.937Z",
"lastStateChange": "2023-07-25T15:30:17.937Z",
"numRetries": 0,
"trigger": "book_updated_watch",
"target": "book_updated_watch_target"
}
}
]
}
In this example, the basic event payload represents an update
operation on a Book
entity. It includes the updated values of the title
and author
properties. The operation
is set to update
, and the id
identifies the specific book entity. The selection
and path
properties are optional and provide additional context or information about the event within the entity graph.
Event Metadata
Each event within the webhook payload contains metadata that provides essential information about the event. The event metadata, available under the meta
field, includes the following properties:
eventId
(UUID string): A unique identifier for the event. This identifier can be used to track and reference the event throughout your system.transactionId
(UUID string): The identifier for the transaction associated with the event. This can be helpful for managing the event within a transactional context.createdAt
(ISO 8601 string): The timestamp indicating when the event was created. It represents the moment when the event was initially recorded.lastStateChange
(ISO 8601 string): The timestamp of the last state change for the event. It indicates when the event's state was last modified or updated.numRetries
(int): The number of times the event has been retried. This count can help you track the number of retry attempts made for the event.trigger
(string): The name of the trigger or watch that caused the event.target
(string): The name of target associated with the event.
Basic events
Basic event payloads represent individual operations performed on entities. They provide information about the specific operation and the changes made to the entity.
export type UpdateEvent = {
operation: 'update'
entity: string
id: PrimaryValue
values: JSONObject
old?: JSONObject
}
export type CreateEvent = {
operation: 'create'
entity: string
id: PrimaryValue
values: JSONObject
}
export type DeleteEvent = {
operation: 'delete'
entity: string
id: PrimaryValue
}
export type JunctionConnectEvent = {
operation: 'junction_connect'
entity: string
id: PrimaryValue
relation: string
inverseId: PrimaryValue
}
export type JunctionDisconnectEvent = {
operation: 'junction_disconnect'
entity: string
id: PrimaryValue
relation: string
inverseId: PrimaryValue
}
Processing timeout
A timeout is enforced for webhook completion to ensure timely processing. By default, the timeout is set to 30 seconds, but it can be adjusted in the webhook configuration (using timeoutMs
prop). If a webhook fails to respond within the specified timeout, all events in the batch are marked as "retrying."
Response Processing
When processing the response received from a webhook invocation, Contember follows specific rules to determine the success or failure of the batched events. Here's an overview of the response processing rules:
-
Not-OK Response Status: If the HTTP response status falls outside the 2xx range (i.e., it is not considered OK), the entire batch is considered unsuccessful. Detailed information about the failure is stored in the
log
field of each event. -
OK Response Status with Specific Non-Empty Body: If the HTTP response status is within the 2xx range (OK) and the response body contains a valid JSON object with a
failures
key, the response is further analyzed:- Invalid Structure or unknown
eventId
: If the structure of the response doesn't adhere to the required format (failures
must contain an array of objects containing aneventId
(UUID format) and optionally anerror
(string format)) or if theeventId
present in the response does not correspond with any event in the batch, the entire batch is considered unsuccessful. - Valid Structure and
eventId
: If the structure of the response is as expected and theeventId
in the response matches an event in the batch, only the events specified in thefailures
field are marked as unsuccessful, while the remaining events are deemed successful.
- Invalid Structure or unknown
-
OK Response Status with Empty or Other Non-Specific Body: If the HTTP response status is OK (2xx) and the response body is either empty or contains anything other than a valid JSON object with a
failures
key, the entire batch is treated as successful, ignoring the specifics of the response content.
Following the processing of the response, the standard retry mechanism is applied to the events that were marked as unsuccessful. This ensures that the unsuccessful events are retried according to the configured retry logic, allowing for subsequent attempts to process them successfully.
Example: webhook response payload with the failures
field indicating event failures:
{
"failures": [
{
"eventId": "f4f0a97d-7850-4add-8946-a1ce016306ce",
"error": "Invalid input"
},
{
"eventId": "a2b1c3d4-5678-90e1-2345-678f9g0h12i",
"error": "Service not available"
}
]
}
In this example:
- Two events within the batch are marked as failures.
- The
eventId
field uniquely identifies each failed event. This id matches theeventId
in ameta
. - The
error
field provides additional information about the cause of the failure, such as an error message or a specific reason for the event processing failure.
Retries
Contember follows a retry strategy for events marked as "retrying." By default, the initial repeat interval between retry attempts is set to 5,000 milliseconds (can be changed using initialRepeatIntervalMs
webhook prop), and it follows an exponential backoff strategy for subsequent retries. The interval between retries doubles with each attempt until the maximum number of attempts is reached. The maximum number of attempts is set to 10 (maxAttempts
prop in webhook configuration), meaning Contember will attempt to send the webhook request a maximum of 10 times before considering it as a failure.