(V5.5.4 and later) Content Platform Engine event webhooks

A webhook is a way for the Content Engine to provide near real-time information to other interested applications or services. When the subscribed event occurs, the Content Engine makes an HTTP POST request to the URL that is configured for the webhook.

Overview

Webhooks are user-defined HTTP callbacks made with HTTP POST. They provide a loosely coupled means of integration between different services. The Content Engine supports making such callbacks when triggered by some event in the Content Engine, such as the check-in of a document to a repository or the update of a property on a Content Engine object.

In order for a webhook implementation to receive these event notifications, it must be registered with the Content Platform Engine object store that is the source of the intended events. Registration includes providing a callback URL, the events that the webhook is interested in, and a secret token. When the specified event happens on Content Platform Engine, it makes an HTTP request to the Webhook URL with the event payload as JSON and security tokens.

Content Platform Engine webhook

The Content Platform Engine event framework is extended to provide the webhook support for Content Platform Engine events. Webhook support extends to all the event types the Content Platform Engine can generate, including both system-defined events and custom events.

Content Platform Engine metadata

In V5.5.4, Content Platform Engine includes an optional system addon, the Event-Driven External Service Invocation Extension. This add-on extension provides the metadata that is required for the webhook feature in Content Platform Engine. The addon primarily implements a EevWebhookExternalEventAction metadata class to hold the properties that are related to a webhook URL (EevExternalEventReceiverURL), the credentials (EevEventReceiverCredentials), and the receiver registration ID (EevReceiverRegistrationIdentifier). The metadata class property progId is permanently set to the webhook implementation class.

The webhook URL property (EevExternalEventReceiverURL) specifies the URL where the webhook is actively listening for the POST requests from Content Platform Engine. The Content Platform Engine server makes an HTTP POST call when an event that is subscribed to by the webhook is generated. The Content Platform Engine server’s event queue retries the request until it receives the HTTP OK (200) status or it exhaust the number of retries that are configured in Content Platform Engine. You can configure the number of retries for this webhook post in the Administration Console for Content Platform Engine by navigating to Domain > Asynchronous Processing Subsystem > Retry count.

The credentials (EevEventReceiverCredentials) property holds the JSON content that contains the credential type and credential secret. This property is protected and can only be read by trusted code running on the server, such as the event handler. Content Platform Engine uses this JSON to secure the payload.

The receiver registration ID (EevReceiverRegistrationIdentifier) is any string that uniquely identities the webhook. This is a convenient property for the webhook receiver application to query by using the GraphQL API to retrieve the information regarding its subscriptions, and so on.

Payload

After the event occurs in Content Engine, the Content Platform Engine server looks at the current subscriptions. If there is any subscription for the event, the server queues the event for the Asynchronous subsystem to process these queue items. When the background thread processes these queue items, the event framework pulls the webhook event action handler that is configured through GraphQL API. The framework also serializes the event into JSON and sends it as an HTTP POST payload to the webhook receiver.

The JSON payload depends on the type of event that is being serialized. At the high level every JSON contains the following elements:
eventDateTime
Date and time of the event generated
receiverRegistrationId
The receiver registration identifier uniquely identifies the webhook receiver. This identifier is supplied during webhook registration.
objectStoreId
ID of the object store where the event happened. The webhook can use this ID to communicate back with the Content Platform Engine server.
sourceObjectId
The event source object ID. Webhook can use this ID to retrieve the object that caused this event.
eventType
The type of the Content Platform Engine event that generated the payload. This value is a simple JAVA class name of the Content Platform Engine event.
subscriptionId
The ID of the subscription that created the event.
initiatingUser
The user who initiated the event.
properties
The event object properties.
id
The ID of an event. The webhook can use this ID to check for duplicate receipts of the event payload.

Other information in the properties are dependent on the type of the event that is being generated.

For ObjectChangeEvents the properties element at the minimum contains the SourceClassId, SourceObject, SourceObjectId. Other elements exist in the event based on the published properties of the event object. All elements at this level correspond to Content Engine properties. The following list shows the additional elements in the JSON based on the event type:
  • PublishRequestEvent
  • CancelCheckoutEvent, CreationEvent, DeletionEvent
    • VersionSeriesId
  • ChangeClassEvent, CheckinEvent, CheckoutEvent, ClassifyCompleteEvent, DemoteVersionEvent, FreezeEvent, LockEvent, PromoteVersionEvent, PublishCompleteEvent, PublishRequestEvent, TakeFederatedOwnershipEvent, UnlockEvent, UpdateEvent, UpdateSecurityEvent
    • ModifiedProperties
    • OriginalObject
    • VersionSeriesId
  • ChangeStateEvent
    • LifecycleOperation
    • ModifiedProperties
    • OriginalObject
    • VersionSeriesId
  • CmMarkForDeletionEvent, CmRecoverEvent
    • CmRecoveryItem
    • VersionSeriesId
  • CmMoveContentEvent
    • CmSourceStorageArea
    • CmTargetStorageArea
    • ModifiedProperties
    • OriginalObject
    • SourceContentRetained
    • VersionSeriesId
  • FileEvent, UnfileEvent
    • ContainmentName
For RetrievalEvent, the properties element contains, at a minimum, the SourceClassId. Other elements exist in the event based on the published properties of the event object. The following list shows the additional elements in the JSON based on the event type:
  • GetContentEvent
    • ElementSequenceNumber
  • GetObjectEvent
    • SourceObjectId
  • QueryEvent
    • QueryText
Sample JSON:
{
  "eventDateTime":  ,
  "receiverRegistrationId": "/ip_address:webhook:receiver",
  "objectStoreId": "{C7832BF4-2BA0-4DDC-8C12-F4EB682BD813}",
  "sourceObjectId": "{E7D08881-742F-486B-AA6F-5AC80081FA8E}",
  "eventType": "CreationEvent",
  "subscriptionId": "{808B136E-0000-C014-9108-AF5E3E00BB0F}",
  "initiatingUser": "CEAdmin",
  "properties": {
    	"Id": ,
    	"SourceClassId": ,
    	"SourceObject": 
    	"SourceObjectId": ,
    	"VersionSeriesId": ,
  }
}

Security and validation

The webhook request and response happen asynchronously. Sometimes the webhook might be waiting for the desired event for a long time. When Content Platform Engine sends the event response to webhook, the server must secure the payload so that the integrity of the data is verifiable and the source of the data is confirmed.

Content Platform Engine uses the Hash-based Message Authentication Code, or HMAC to provide both message integrity and authentication. HMAC is based on sharing a private secret key between the webhook and the Content Platform Engine server. During the webhook registration the credentials (EevEventReceiverCredentials) property is supplied with the JSON that contains the credential type and credentialSecret. The JSON looks something like the following example:
{
CredentialType:HMAC,                       CredentialSecret:eb497ac4891d6009d8ef601bfdf6c3f5
}
When the Content Platform Engine works on the event queue it generates the HMAC code as shown below and adds the code into the HTTP header HMAC. The payload for the webhook request is in plain text. The webhook, upon receiving the POST request from Content Platform Engine, can read the HTTP header with the HMAC name and compute the HMAC, the JSON Payload, and the CredentialSecret that are shared with the Content Platform Engine server.
public static String calculateHMAC(String jsonPayload, String key) throws java.security.SignatureException, java.security.NoSuchAlgorithmException, java.security.InvalidKeyException {
        String HMAC_SHA1_ALGORITHM = "HmacSHA1";
javax.crypto.spec.SecretKeySpec signingKey = 
new javax.crypto.spec.SecretKeySpec(key.getBytes(), HMAC_SHA1_ALGORITHM);
        javax.crypto.Mac mac = javax.crypto.Mac.getInstance(HMAC_SHA1_ALGORITHM);
        mac.init(signingKey);
        return javax.xml.bind.DatatypeConverter.printBase64Binary(mac.doFinal(jsonPayload.getBytes()));
    }

If the computed HMAC is different from the HMAC that was included in the header, it implies that the data is being tampered with or is compromised, and the webhook can discard the message and send the appropriate HTTP status back. The Content Platform Engine server is going to retry posting the event until it exhaust the retries or receives the 200 status code from webhook.

Using content event webhooks

To make the webhook capability available for use, you register and install the webhook extension as an add-on for your Content Platform Engine server. For more information, see Add-on extensions.

You can use the Content Services GraphQL API to create even actions and subscriptions for a content event webhook. For details, see Content Services GraphQL API development.

Webhook sample

You can learn more about implementing webhooks in your application by using the webhook sample application.