Webhook Endpoint Integration

Webhook Endpoint Integration

This documentation outlines the integration details for the webhook version of the policy execution endpoint, designed for immediate response and background processing.

Endpoint Configuration

Endpoint Target Address: As found in the decision management portal
HTTP Method: POST
Request Content-Type: application/json
Response Content-Type: application/json
Charset: UTF-8

Request Headers

HeaderValueDescription
X-API-KEYYOUR_ENDPOINT_API_KEYYour unique API key for authenticating with the Decisimo platform. This is mandatory.
Content-Typeapplication/jsonSpecifies that the request body is in JSON format.
Return-AnyHeaderNameAnyHeaderValueOptional. You can include any custom headers prefixed with Return- in your request. These headers will be processed by the webhook and included in the immediate 202 Accepted response, as well as forwarded as headers in the subsequent callback request. For example, Return-ClientRef: ABC123 will result in ClientRef: ABC123 being present in the responses.
X-Result-KeyYOUR_RESULT_KEYOptional. A secret of your choosing. When present, the result is also stored encrypted under it so you can retrieve it later by polling, which is the only way to recover a callback that could not be delivered. It is never stored on the Decisimo platform.

Some names are reserved. A Return- header with one of these names is dropped, and the rest of the request is processed as normal. The check ignores case.

  • Framing and connection: Host, Content-Length, Content-Type, Content-Encoding, Transfer-Encoding, Connection, Keep-Alive, Upgrade, TE, Trailer, Expect
  • Credentials and cookies: Authorization, Cookie, Set-Cookie, WWW-Authenticate, and any name starting Proxy-
  • Redirects and browser policy: Location, Strict-Transport-Security, and any name starting Access-Control-
  • Set by the platform: User-Agent, and any name starting X-Decisimo- or X-Webhook-

HTTP Response Codes

Response CodeDescription
202 AcceptedWebhook request successfully received and background processing initiated. The response body will contain a request_id for tracking the asynchronous execution and an optional callback_url.
400 Bad RequestThe body was missing or did not parse. message says which: No content or Malformed JSON.
401 UnauthorizedNo API key on the request. message is Missing API Key.
404 Not FoundThe specified endpoint (name) was not found on the platform, or the api_key did not match an existing endpoint. The two are indistinguishable on purpose, so check both.
429 Too Many RequestsRate limit reached. Nothing is executed. The body carries "error": "rate_limited". See Rate Limits below.
501 Not ImplementedThe request was not a POST. The body is empty.

Anything other than 202 means the submission did not start: nothing will be executed, delivered or stored for it. Branch on the status code rather than on the body.

Rate Limits

On shared endpoints, each endpoint accepts up to 50 requests per second and runs up to 10 requests at the same time, unless your contract sets different limits. A request over either limit gets 429 Too Many Requests with a Retry-After: 1 header and the error code rate_limited. A refused request does not run the decision flow and is not billed. Wait the number of seconds in Retry-After, then send it again.

A webhook request counts as running from the moment it is accepted until its background run finishes.

Execution Time Limit

The webhook endpoint responds immediately with a 202 Accepted status to acknowledge receipt of the request. The actual policy execution then proceeds in the background with no time limit enforced on the background process. This makes it suitable for long-running operations like LLM service calls or complex data enrichments without blocking your client application.

Integration Workflow

The webhook integration follows an asynchronous pattern:

1. Generate Payload

Prepare your data as a valid JSON object.

2. Call Webhook Endpoint

Send a POST request to the webhook endpoint, including your API key and the JSON payload. Optionally, include a callback_url in your JSON payload or as a GET parameter if you wish to receive the execution result.

3. Receive Immediate Acceptance

Your client application will receive an immediate 202 Accepted response. This response confirms the request has been received and provides a request_id for tracking. Your application should not wait for the full execution at this point.

4. Process Immediate Response

Handle the 202 Accepted response and its request_id.

5. Await Callback (if configured)

If a callback_url was provided in the initial request, Decisimo will send a POST request to that URL once the background policy execution is complete (or fails). This callback will contain the execution results, status, and the original request_id.

Callback Mechanism

The system will attempt to send the callback up to 3 times if the previous attempt does not return a 2xx or 3xx HTTP status code. Three attempts leave two gaps: a 2 second pause after the first attempt and a 4 second pause after the second.

Callback Payload

The callback payload is a JSON object that includes:

FieldTypeDescription
statusstringcompleted or failed
request_idstringThe ID generated for the original webhook request
endpoint_namestringThe name of the executed policy
execution_time_msnumberThe duration of the policy execution in milliseconds
timestampstringWhen the callback was sent
resultobject/stringThe output of the policy execution, parsed as a JSON object if the policy's output is valid JSON, otherwise it will be the raw output
errorstringA code, never free text: step_failed when a step failed and the flow stopped, execution_failed when the engine itself failed. Absent when execution succeeded
successbooleanIndicates execution success

Inside result are two fields the platform always adds: __request_code, which identifies the execution, and __release_name, which says which release of the decision flow ran. __request_code is not request_id. request_id is the handle the poll endpoint accepts; __request_code is what to quote when you contact support about a run. Polling with __request_code answers 404.

Callback Headers

In addition to Content-Type: application/json and Content-Length, the callback request will include X-Webhook-Attempt and X-Webhook-Max-Attempts headers, along with any custom headers you specified with Return- in the initial request.

HTTP header names are case-insensitive, and proxies and HTTP/2 routinely rewrite their casing in transit. Match the callback headers case-insensitively on your side. A header you sent as Return-Webhook-Token may arrive as webhook-token, and a lookup that expects an exact casing will miss it even though the header is present. Normalise the header names to lower case before you look one up.

When your tenant has a callback credential configured, the callback is also signed. Three more headers ride along: X-Decisimo-Timestamp, X-Decisimo-Nonce and X-Decisimo-Signature. The signature is a hex HMAC-SHA256 over {timestamp}.{nonce}.{raw request body}, computed with the credential. Check that the timestamp is inside your replay window, then check the signature, and only then parse the body. Every attempt is signed afresh, so a retry carries a new timestamp and nonce. To turn signing on, create a credential named webhook_callback_secret in the decision management portal. Every callback for that tenant then arrives signed, and nothing else needs configuring.

When a Callback Cannot Be Delivered

How long the three attempts take depends on how your receiver fails. Each attempt allows 10 seconds to open the connection and 30 seconds in total, so a receiver that refuses outright ends the whole window in about six seconds, while one that accepts the connection and then hangs stretches it to about ninety. An outage lasting longer than that outlives the retries, so a result nobody could receive is kept for you to collect by polling instead.

This works only if you supplied a result_key. The result is encrypted with your key and we never keep the key, so without one there is nothing we can store and nothing you could read back. If you depend on callbacks, send a result_key on every call and poll for the ones that did not arrive.

A result collected this way carries the same Return-* headers the callback would have carried, returned as headers on the poll response.

Polling for Results

As an alternative (or addition) to the callback mechanism, you can retrieve the execution result by polling instead of receiving a POST callback. This is useful when your application cannot expose a publicly reachable callback_url.

Requesting Polling

Include a result_key in your JSON payload or as a GET parameter when you call the webhook endpoint. This is a secret of your choosing — it is never stored on the Decisimo platform; it is only used to encrypt the result so that only the holder of the key can read it back. When a result_key is supplied, the immediate 202 Accepted response will include "result_polling": true.

The key must be at least 16 characters long. A shorter key is set aside: the 202 Accepted response then carries "result_polling": false and "result_key_error": "result_key_too_short", and nothing is stored for polling.

Prefer the X-Result-Key request header over the payload field, so the key stays out of the URL and out of any stored copy of the request body. The payload field and the GET parameter are accepted as a fallback for callers that cannot send custom headers.

{
  "...": "your normal payload fields",
  "result_key": "a-secret-you-generate-and-keep"
}

Polling Endpoint

Endpoint Target Address: Same base path as your webhook endpoint, with /webhook/ replaced by /poll/ (as found in the decision management portal)
HTTP Method: GET or POST
Response Content-Type: application/json

ParameterDescription
nameThe endpoint name, same as used for the webhook call.
api_keyYour endpoint API key. Accepted via X-API-Key header, Basic auth, or as a parameter.
request_idThe request_id returned in the original 202 Accepted response.
result_keyThe same secret key you supplied to the webhook call. Accepted via the X-Result-Key header or as a parameter.

Polling Response Codes

Response CodeStatusDescription
200 OKcompleted/failedExecution has finished. The response body contains status, request_id, and result.
202 AcceptedpendingExecution has not finished yet. Poll again after a short delay.
400 Bad RequesterrorOne or more required parameters (name, api_key, request_id, result_key) are missing.
401 UnauthorizederrorThe endpoint name or api_key is invalid.
403 ForbiddenerrorThe result_key does not match the one used at execution time.
404 Not FounderrorThe request_id is unknown, the result has expired, or it belongs to another tenant or endpoint.

Security and Expiry

  • Results are encrypted at rest (AES-256-GCM) using a key derived from your result_key. The key itself is never persisted — only a one-way hash used to reject wrong-key poll attempts.
  • A delivered result is retained for 24 hours after execution completes.
  • A result that no callback could deliver is retained for 30 days, because it is the only copy that exists.
  • A collected result is deleted 24 hours after your first successful poll. Polling again inside that window returns the same result; only the first poll starts the clock.
  • Polling and the callback mechanism are independent and can be used together: supply both callback_url and result_key if you want the result pushed to you and available for polling as a fallback.

Payload Limitations

Decisimo has two limitations on the payload:

  • Must be a valid JSON object
  • Cannot be larger than 1MB

Best Practices

  • Set appropriate timeouts in your client application for the initial webhook call
  • Implement robust callback handling if using the callback mechanism
  • Use unique request identifiers to correlate requests with callbacks
  • Handle retry scenarios for callback failures
  • Monitor execution times using the provided metrics in callbacks

Related Topics: