/
Event Notification API

Event Notification API

5.15.0

The Event Notification API is designed to push outbound notifications to configured endpoints (webhooks, REST endpoints, or OAuth-secured endpoints) based on specific conditions. It retrieves endpoint configurations from an underlying system configuration (in this example, the BNS module), evaluates conditions, and dispatches payloads to multiple targets.  

The Event Notification API can be enabled via setup utilizing the Tenant Configuration APIs.

This documentation describes:

  1. Key Components and Classes:
    • OutboundNotificationHandler
    • WebhookEndpointProcessor
    • Other endpoint processors (REST, OAuthRest)
  2. Configuration Structure
  3. OAuth Security


1. Key Components and Classes

1.1 OutboundNotificationHandler

The OutboundNotificationHandler class is the main entry point for pushing outbound notifications. It:

  • Retrieves entity-specific configuration using getConfigForEntity(entityId).
  • Filters and iterates over configured endpoints (e.g., PushEndpoints, FileUploadPushEndpoints, InventoryPushEndpoints, etc.).
  • Applies conditional checks (through EndpointProcessor) to determine if a given payload matches the endpoint’s criteria.
  • Sends the payload to the endpoint using the configured processor.
  • Captures, logs, and returns success or failure details, including whether a retry can be attempted if a failure occurs.

The core method is pushOutbound(entityId, payload, op, endpointsCfg, timeout). When invoked:

  1. Loads the BNS configuration for the given entityId.
  2. Identifies which set of endpoints should be used (e.g., PushEndpoints, FileUploadPushEndpoints, or InventoryPushEndpoints).
  3. Processes each endpoint:
    • Skips if Enabled is false.
    • Evaluates conditions (Expr-eval or JSONPath).
    • Dispatches the payload if conditions pass.
    • Returns either a SuccessResponse or a FailureResponse.
  4. Aggregates all endpoint results into a single response.

Note: The handler can optionally apply a timebox (timeout) to each endpoint request, after which it throws a TimeoutError. This is useful to avoid blocking calls.

1.2 Endpoint Processors

OutboundNotificationHandler delegates actual HTTP (or other protocol) logic to various EndpointProcessorimplementations. Three are mentioned in the code:

  1. RestEndpointProcessor (Deprecated).
  2. OAuthEndpointProcessor (Deprecated).
  3. WebhookEndpointProcessor (handles general-purpose webhook calls, possibly with or without OAuth authentication).

This approach allows for modular extension—if you have new service types (e.g., gRPC, MQTT), you can implement a new EndpointProcessor.

1.3 WebhookEndpointProcessor

The WebhookEndpointProcessor handles any endpoints whose ServiceType is "Webhook". Its key responsibilities are:

  1. Condition Checks: Uses either expr-eval or JSONPath-based logic to determine if the endpoint should be invoked.
  2. Authentication: If OAuth2 is configured, it obtains tokens from a cache (supplied in the constructor), or fetches new tokens as necessary.
  3. Request Dispatch: Substitutes variables in the request definition (url, headers, etc.) using the provided payload. Then it sends the request with axios.
  4. Response Handling: Returns a standardized StandardResponse containing status, body, and headers.

2. Configuration Structure

The configuration for BNS typically lives under something like bnsCfg.Modules.BNS. Below is an example configuration snippet (from your code example) showing four main sub-keys that hold arrays of endpoints:

json

"BNS": {
"PushEndpoints": [
{
"Enabled": true,
"ServiceType": "Webhook",
"Request": {
"url": "https://so-dev.hostedservicepower.com/api/v1/work-order/webhook",
"headers": {
"Authorization": "--Redacted--"
}
},
"Condition": "$[?(@.WorkItemType==1)]"
},
{
"Enabled": true,
"ServiceType": "Webhook",
"Request": {
"url": "https://consumer-portal.hostedservicepower.com:9200/api/v1/webhook",
"headers": {
"Authorization": "--Redacted--"
}
},
"Condition": "$[?(@.WorkItemType==1)]"
},
{
"Enabled": true,
"ServiceType": "Webhook",
"Request": {
"url": "https://so-dev.hostedservicepower.com/api/v1/work-order/notification-webhook",
"headers": {
"Authorization": "--Redacted--"
}
},
"Condition": "$[?(@.OrderNum!=)]"
}
],
"FileUploadPushEndpoints": [
{
"Enabled": true,
"ServiceType": "Webhook",
"Request": {
"url": "https://8ea49172-5da9-4428-b117-282e67004502.mock.pstmn.io/v1/notification/file-upload"
},
"Authentication": {
"OAuth2": {
"ClientId": "--Redacted--",
"ClientSecret": "--Redacted--",
"TokenUrl": "https://mred-sp.eu.auth0.com/oauth/token",
"GrantType": "client_credentials",
"Supplements": {
"headers": {
"Content-Type": "application/json"
}
}
}
}
}
],
"InventoryPushEndpoints": [
{
"Enabled": true,
"ServiceType": "Webhook",
"Request": {
"url": "https://sb-dev.hostedservicepower.com/up/v5/bns/webhook",
"headers": {
"Authorization": "--Redacted--"
}
},
"Condition": ""
}
],
"Preference": {
"Email": {
"Provider": "Emarsys"
},
"SMS": {}
},
"SES": {}
}

2.1 Endpoint Field Descriptions
  • Enabled: Boolean indicating whether this endpoint is active.
  • ServiceType: The processor to use (e.g., Webhook, REST, OAuthRest).
  • Request:
    • url: The URL where the payload will be sent.
    • method: (Optional) Defaults to POST for Webhooks if not otherwise specified.
    • headers: An object of custom HTTP headers to include in the request.
  • Condition: Optional condition expression. Supports:
    • JSONPath conditions like "$[?(@.WorkItemType==1)]".
    • expr-eval syntax like "WorkItem.Id > 100".
  • Authentication:
    • Sub-object containing OAuth2 settings, if needed.
    • OAuth2 includes ClientId, ClientSecret, TokenUrl, GrantType, and an optional Supplements block for adding further headers/body configurations.


3. OAuth Security

3.1. Overview

The Event Notification API supports endpoints that require OAuth2-based authentication. When an endpoint’s configuration specifies OAuth2 parameters, the system automatically:

  1. Retrieves or generates access tokens through the defined OAuth2 grant type (e.g., client_credentials, authorization_code, etc.).
  2. Caches these tokens for their valid duration, reducing redundant calls to the OAuth server.
  3. Injects the token into the outgoing request’s authorization header before sending the payload.

3.2. Configuration

The configuration for OAuth2 is found under each endpoint’s Authentication object. For example:

{
"Authentication": {
"OAuth2": {
"ClientId": "<CLIENT_ID>",
"ClientSecret": "<CLIENT_SECRET>",
"TokenUrl": "https://<oauth-server>/oauth/token",
"GrantType": "client_credentials",
"Supplements": {
"headers": {
"Content-Type": "application/json"
}
}
}
}
}

Key Fields

  • ClientId: OAuth client ID
  • ClientSecret: OAuth client secret
  • TokenUrl: The URL where the system exchanges credentials for an access token
  • GrantType: Supported values include client_credentials, authorization_code, and (in some cases) none for no authentication
  • Supplements: An optional JSON object that allows you to provide additional headers or parameters to the token request

3.3. OAuth Grant Types

Currently, two primary OAuth2 flows are implemented:

  1. client_credentials

    • Used for machine-to-machine scenarios.
    • Requires both ClientId and ClientSecret.
    • Exchanges them at the TokenUrl for an access token.
  2. authorization_code

    • Used for user-interactive scenarios (not as common for webhooks, but supported if required).
    • Requires an AuthorizationCode and RedirectUri to exchange for an access token.
    • Typically relevant if an external system grants tokens via a user consent page.

Note: A third option, none, indicates no authentication is required and thus no token retrieval is performed.

3.4. Token Handling & Caching

Token retrieval is performed via an OAuth2Strategy instance (e.g., ClientCredentialsStrategy or AuthorizationCodeStrategy). Each instance is responsible for:

  1. Constructing the request to the TokenUrl.
  2. Parsing the response to extract the access_token and expires_in fields.

Once retrieved, the token is cached for the duration indicated by expires_in. If a request for the token occurs again within that window, the system uses the cached token

3.5. Injecting Tokens Into Requests

In the WebhookEndpointProcessor (or other processors supporting OAuth), an axios interceptor is applied using axios-token-interceptor. When an HTTP request is about to be sent:

  1. The token interceptor checks if an access token is cached.
  2. If not cached or expired, the interceptor retrieves a new token by calling the appropriate OAuth2 strategy.
  3. The token is added to the Authorization header (Bearer <token>) automatically.

All subsequent outbound requests reuse the same cached token until it expires, minimizing load on the token server.

3.6. Error Handling & Retries

When the system fails to obtain an OAuth token (e.g., invalid credentials, network error, expired refresh token, etc.):

  • It throws an exception that is captured as a FailureResponse in the OutboundNotificationHandler.
  • If the error is deemed retryable (e.g., network failures), the isRetriable flag is set, allowing you to implement retry logic at a higher level.
  • If the error is a client-side authentication error (4xx indicating invalid credentials), you may need to fix the configuration.

3.7. Extending OAuth Support

For more specialized or custom OAuth flows:

  1. Create a new class implementing the base OAuth2Strategy interface, handling specifics of your flow (e.g., custom header values, multiple token endpoints, refresh tokens, etc.).
  2. Update the processor (e.g., WebhookEndpointProcessor) to instantiate the new strategy if GrantType matches.
  3. Register any additional fields in the endpoint’s configuration (e.g., refresh token parameters, additional scopes) as needed.




Related content

REST APIs
Read with this
Event Notification Object
Event Notification Object
More like this
REST Notification Changed Items
REST Notification Changed Items
Read with this
REST Objects BNS
REST Objects BNS
More like this
Notification Service APIs
Notification Service APIs
Read with this
Parts UpdateOrder RESTified API
Parts UpdateOrder RESTified API
Read with this