Receiving Webhooks
Build the endpoint that receives GAINS webhooks: the request, how to answer, timeouts, duplicates, ordering and TLS.
This page is for whoever builds the endpoint that receives GAINS webhooks. It covers the request GAINS Connect sends, how to answer it, timeouts, duplicates, ordering and the TLS your endpoint must serve. To create the configuration that points at your endpoint, see Configuring Webhooks; to find out why a delivery failed, see Delivery & Recovery.
The request you receive
Every webhook is an HTTP/1.1 POST to the Url of a webhook configuration, with a JSON body:
POST /gains/webhooks HTTP/1.1
Host: erp.example.com
Content-Type: application/json
Content-Length: 1834
Authorization: Basic Z2FpbnMtd2ViaG9va3M6VnE3I3BMMm0tWHI5{
"DataType": "purchase_order",
"Data": [
{
"TrackingNumber": 4821,
"ReceivingLocation": "CHI01"
}
]
}The body is UTF-8 JSON in the envelope described in Payload format: DataType names the subject and Data holds the records. Its field names follow the configuration's FieldNamingConvention, except the keys inside ExtendedData, which are your API labels, and a rejected Record in a task payload, which is returned as it was submitted.
| Header | Value |
|---|---|
Content-Type | application/json |
Content-Length | The size of the body in bytes. |
Authorization | Depends on the configuration's Authorization scheme; see below. Absent when the configuration has none. |
The Authorization header for each scheme:
| Scheme | Authorization header |
|---|---|
Header | The Value you configured, exactly as given: Bearer 8f3c1d025a77. |
Basic | Basic followed by the base64 of Username:Password, UTF-8 encoded. |
OAuth2ClientCredentials, OAuth2PrivateKeyJwt | The token type and access token your identity provider issued, normally Bearer eyJhbGciOi…. See OAuth 2.0 Webhook Destinations. |
A webhook carries no signature, event ID or delivery ID header. Check the Authorization header on every request to decide whether to trust it, and identify the records from the payload itself (see Duplicates).
Answering a webhook
What your URL answers decides whether the delivery succeeded:
| Your answer | Recorded in history | What happens next |
|---|---|---|
2xx | success | The payload leaves the queue. The next one is sent PayloadInterval later. |
3xx | failed | The redirect is not followed, so the payload never reaches the address you redirect to. Retried as for any failure. |
401 | failed, unless the retry below succeeds | For an OAuth 2.0 scheme only, GAINS Connect first obtains a fresh token and sends the payload once more straight away. |
408 | no_response | Retried as for any failure. |
Any other 4xx or 5xx, including 429 and 503 | failed | Retried after RetryHandling.Gap, up to RetryHandling.Attempts times. |
- Answer
2xxonce you have stored the payload. Anything you answer after that is your own processing, not delivery. - Never answer with a redirect. Point
Urlat the exact address that accepts thePOST. - The response body is not read. The start of it, and your response headers, are kept so you can see them in webhook history.
How failed deliveries are retried, and how a failing payload holds back the ones behind it, is described in Retries.
Timeouts
Answer within 90 seconds. Otherwise the delivery has failed and is recorded with status code 504.
Respond fast and process asynchronously:
- Check the
Authorizationheader. - Store the body as received, for example on a message queue or in a staging table.
- Answer
200or202. - Process the stored payload separately.
A payload can hold up to PayloadMaxItems records, so a large order payload can take a few megabytes. Make sure any gateway or proxy in front of your endpoint accepts a body of that size and does not time out sooner than your own processing.
Duplicates
GAINS Connect delivers each payload at least once, not exactly once. You can receive the same records more than once when:
- You processed a payload but answered too late, or answered something other than
2xx. The payload is retried. - A payload is resent with
POST /webhook/resend, by you or by GAINS support.
Make processing a record twice harmless. Neither the request nor the payload carries a delivery ID, so key on the fields that identify each record:
DataType | Identify a record by | Recognise a newer version by |
|---|---|---|
purchase_order, transfer_order, work_order, production_order | TrackingNumber, and LineNumber within its LineItems | UpdatedTime |
inventory_policy | ItemCode and LocationCode | EventOccurredAt on the location |
forecast | ItemCode and LocationCode | EventOccurredAt on the location |
task | TaskId | StatusUpdatedOn |
Write records as inserts-or-updates on those keys rather than as plain inserts, and do not let an older version overwrite a newer one. The weekly and monthly forecast triggers share DataType forecast; tell them apart by the length of their buckets, as Webhook Payload Samples describes.
Test payloads are made up
POST /webhook/testsends sample records to your real URL. Make sure a test cannot create orders or change data in your production system, for example by testing against a non-production URL first.
Ordering
Do not rely on the order in which payloads arrive. A resent payload arrives after newer data for the same records, and several configurations pointing at the same URL send to it in parallel, so your endpoint must accept concurrent requests.
TLS and headers
- Serve
httpswith a certificate from a public certificate authority. Otherwise delivery fails, and webhook history records it as503. - Do not rely on headers other than those listed above.
The rules for the Url setting are in Destination URL.
Related
- Webhook Payload Samples: the payload of each trigger
- OAuth 2.0 Webhook Destinations: authenticating to your URL with an identity provider
- Delivery & Recovery: retries, webhook history and the status codes it records
- Errors & Retries: the error body of the webhook endpoints
Updated 8 days ago
