{"activeVersionTag":"latest","latestAvailableVersionTag":"latest","collection":{"info":{"_postman_id":"a8cc246c-c041-47ca-8dc2-36b12c910ca4","name":"Ingenuity API","description":"---\n\n## Welcome to THG Ingenuity API\n\n# Authentication\n\nAll THG Ingenuity APIs will require authentication. THG Ingenuity API is an OAuth 2.0 provider. It is highly recommended that you use one of the many OAuth 2.0 libraries to manage and do the heavy lifting for you.\n\nTo get started, you will need to get an ACCESS_TOKEN. In order to get an ACCESS_TOKEN, you will first need a registered application. If you've already registered, you would be supplied with a client ID and client SECRET. Many good OAuth libraries will be able to manage the ACCESS_TOKEN for you, you just need to supply the client ID and client SECRET.\n\n## Getting an Access Token\n\nIf you have a registered application, you will have been given a client ID and a client SECRET.  \nTo test that you are able to get the ACCESS_TOKEN you can hit the provider URL that you've been provided with\n\n``` javascript\n    client_secret= {client-secret}\n    client_id= {client-id}\n    grant_type = \"client_credentials\"\n\n ```\n\nYou should expect a response like below for a successful response\n\n``` json\n    {\n        \"access_token\": \"eyJhbGciOiJSUzI1NiIsjvsoiejh\",\n        \"expires_in\": 0, //seconds\n        \"refresh_expires_in\": 0, //seconds\n        \"token_type\": \"Bearer\",\n        \"not-before-policy\": 0,\n        \"scope\": \"profile email\"\n    }\n\n ```\n\nThe expiry of the access token will be provided so you can build your system around this to minimise any issues.\n\n## Connectivity Testing\n\nOnce you've establish you're able to get an ACCESS_TOKEN you can proceed to test the connectivity based on what was provided with your \"On boarding Pack\". Hit the URL provided with the ACCESS_TOKEN supplemented as a Bearer Token in the Authorization header. The cURL version of the request should look something like this:\n\n``` bash\n    curl --location --request GET 'https://api.thgingenuity.com/authentication/healthcheck' \\\n        --header 'Authorization: Bearer {ACCESS_TOKEN}'\n\n ```\n\nUpon successful authentication check you will receive a response like so:\n\n``` javascript\n    Http Response : 200\n    Body : Authentication Check Successful.\n\n ```\n\n## Using The Access Token\n\nAssuming you successfully tested your ACCESS_TOKEN with the connectivity test, it's time to test it with the API. Just like how you would supplement the ACCESS_TOKEN in the Authorization header during the connectivity test, you would do the same for every API call.\n\n``` bash\n    curl --l'Authorization: Bearer {ACCESS_TOKEN}'\n\n ```\n\nShould your token expire you will be presented with a 401 and you will either need to refresh the token or request a new one.\n\n---\n## THG to Client\n\nWe expect the Client to have protected endpoints that THG will be calling for the THG to Client flow. The expectation is that the endpoints are protected under one of the following authentication mechanism:\n\n- Bearer Authentication(OAuth 2.0)\n    \n- Basic Authentication\n    \n\n## Bearer Authentication\n\nA client id and client secret are exchanged for an access token that THG will pass in all calls via a request header.\n\n- Client should provide us with the authenticationURL and credentials for getting auth tokens.\n    \n- THG will call the authentication URL using these credentials to get an access token as shown below.\n    \n- THG will include this token in its subsequent requests to Client in an authorization header as a bearer token. The value field will leave a blank space between ‘Bearer’ and the access token.\n    \n- Once the token expires (specified in expires_in field) a new token is requested from the Client.\n    \n\nHeader example:\n\n``` json\n    Key: Authorization\n    Value: Bearer {token}\n\n ```\n\nThe request and response bodies below should be implemented as described below. Client authentication endpoint will be supplied by the Client and THG will call it to request the auth token.Authentication Request Model:Pass with header Content-Type: \"application/x-www-form-urlencoded”\n\n``` json\n    {\n        \"client_id\":\"{clientId}\",\n        \"client_secret\":\"{clientSecret}\",\n        \"grant_type\":\"client_credentials\"\n    }\n\n ```\n\nRequest example:\n\n``` bash\n    curl --location --request POST\n        'https://{authServerBaseUrl}/auth/realms/{realmName}/protocol/openid-connect/token' \n        --header 'Content-Type: application/x-www-form-urlencoded'\n        --data-urlencode 'grant_type=client_credentials'\n        --data-urlencode 'client_id={clientId}'\n        --data-urlencode 'client_secret={clientSecret}'\n\n ```\n\nAuthentication Response Model:\n\n``` json\n    {\n        \"access_token\": \"{token}\", \n        \"expires_in\": 3600\n    }\n\n ```\n\n## Basic Authentication\n\n**A username and password are provided by 3PL that THG will pass in all calls via a request header.**\n\n- 3PL should provide us with username and password.\n    \n- THG will include encoded key derived from username and password in its subsequent requests to 3PL in an authorization header as a basic token. The value field will leave a blank space between ‘Basic’ and the token\n    \n- Basic Authentication is not supported for 3PL integrations.\n    \n\n## Header example:\n\n``` json\n    Key: Authorization\n    Value: Basic {encoded token}\n\n ```\n\n---\n\n# Fulfil Sandbox\n\n### **THG Ingenuity Fulfil Sandbox Documentation**\n\nv.2026-08-16\n\n#### **1\\. Introduction to the THG Fulfil Sandbox**\n\n- Purpose of the Sandbox\n    - Provides an environment to **test integrations** with the THG Ingenuity Fulfil APIs.\n        \n    - Allows clients to simulate sending and receiving data without affecting production systems.\n        - \"Clients\" refer to those integrating with the API\n            \n- What the Sandbox Provides\n    - A collection of **mocked API endpoints** based on the THG Fulfil section of the Ingenuity API.\n        \n    - Predefined responses to simulate successful and unsuccessful API calls.\n        \n    - Simulation of key integration flows (e.g., Shipment or Order Creation, Shipment Dispatch, Returns Processing).\n        \n\n#### **2\\. Accessing and Authenticating with the Sandbox**\n\n- Sandbox Base URL\n    - The sandbox URL: [https://sandbox.api.thgingenuity.com](https://sandbox.api.thgingenuity.com)\n        \n- Authentication\n    - The **Authorization header** is required for authentication using simple **Bearer Token** authentication.\n        \n    - Bearer tokens are provided for clients to use; we haven't implemented a generic auth sequence to generate the sandbox token from the sandbox to prevent unknown access.\n        \n    - 401 Unauthorized response will return if the wrong token or no authorization is used\n        \n- Request Headers\n    - Use `Content-Type: application/json` as nearly all API endpoints return application/json and it's been keyed in some of the mocks.\n        - This header doesn't appear as required on many of the THG Fulfil API calls so we don't enforce it at the moment but will do if verified to be needed.\n            \n    - Other headers may be required and will be updated and communicated. These might be:\n        - `organisation` header, which in UAT and production specifies the client name agreed upon during onboarding\n            \n        - `client-order-reference` to specify a specific order\n            \n        - `X-THG-Channel` to specify a particular fulfilment channel that is often 1:1 with the client's `organization`\n            \n        - `X-THG-Client` to specify a particular client that is often 1:1 with the client's `organization`\n            \n\n#### **3\\. Understanding Sandbox Mock Behavior**\n\n- Differences from Production\n    - No actual processing of orders, shipments, returns, stock movements, or other entities occurs.\n        \n    - Inventory levels are not actually updated based on inbound/outbound calls.\n        \n    - Mocks provide predefined responses, not dynamic behavior based on complex logic or external systems.\n        \n- Predefined Responses\n    - Mocks return static or conditionally static responses based on the request.\n        \n    - Example: The `GET /v2/order/returns/paginated` mock specifically responds to a request with `pageSize=1` and `returnStatus=CREATED`.\n        \n- How to Identify and Use Specific Mocks\n    - Clients may need to send specific request data (e.g., specific product IDs, order numbers, query parameters) to trigger certain mock responses (e.g., success vs. error).\n        \n    - Consult the detailed mock configurations (if available) or trial-and-error based on expected API behavior.\n        \n- Simulating Flows\n    - THG APIs in production and UAT may receive a message and start a sequence of processing that results later (such as warehousing operations) in additional messages back to the customer. For the sandbox, only direct request-response sequences happen.\n        \n    - Outbound notifications (Order State Updates, Stock Adjustments, GRNs, Return status updates) can now be pushed for real to a URL you register with us - see **Section 3a** below - rather than only being returned in the triggering call's own response.\n        \n\n#### **3a. Outbound Webhooks - Receiving Calls From the Sandbox**\n\n- What the Webhooks or Callbacks are for:\n    - For the flows below, the sandbox can actually **POST a sample payload to an endpoint you control**, the same way production sends these notifications. The calls still contain mocked values, the key difference is that they are calls to your systems.\n        \n- Registering Your Callback URL\n    - Contact your THG integration point of contact with the HTTPS URL you want us to call. Also, if you want the calls authenticated, we need the exact `Authorization` header value we should send (e.g. `Bearer` or `Basic` ) - we send that value verbatim, so give us the full header, not just a raw token.\n        \n    - Until this is registered, the trigger endpoints below respond `501 Not Implemented` with `{\"error\": \"No webhook destination configured for this client. Contact THG to register a callback URL.\"}`.\n        \n    - Your callback endpoint must be reachable from the public internet; the sandbox cannot reach into a private network or localhost.\n        \n    - We send these messages from a static IP: 34.142.59.61.\n        \n- How a Call Works Once Registered\n    - You call the trigger endpoint (e.g. `POST /v1/sendOrderStateUpdateToClient`) as normal.\n        \n    - The sandbox responds immediately with `202 Accepted` and a short acknowledgement message (e.g. `{\"message\": \"Order state update will be sent to the client.\"}`) - **the sample payload itself is not in this response**.\n        \n    - Separately (typically within a few seconds), the sandbox sends its own `POST` request, with the sample payload as the body and your registered `Authorization` header attached, to the URL you registered.\n        \n    - The sample payload content is fixed per flow (the same shape as the docs' example), not derived from what you sent in the trigger request.\n        \n- Available Trigger Endpoints\n    - Order State Update: `POST /v1/sendOrderStateUpdateToClient` and `POST /v3/sendOrderStateUpdateToClient`\n        \n    - Stock Adjustment: `POST /stockAdjustmentSendToClient`\n        \n    - Goods Receipt Note (GRN): `POST /goods-in/asn/{asn}/grn`\n        \n    - Return Status Update: `POST /v2/order/return-update`\n        \n- Quick Start - Kick Off a Test\n    - Once your callback URL is registered (see above), any of these will trigger a real push to it. Replace `YOUR_TOKEN` with your sandbox bearer token.\n        \n    - Order State Update (`v1` shown - swap the path for `v3` to test that version instead, same headers):\n        \n        ```\n        curl -X POST https://sandbox.api.thgingenuity.com/v1/sendOrderStateUpdateToClient \\\n          -H \"Authorization: Bearer YOUR_TOKEN\" \\\n          -H \"client-order-reference: ClientOrder00001\"\n        \n         ```\n        \n    - Stock Adjustment:\n        \n        ```\n        curl -X POST https://sandbox.api.thgingenuity.com/stockAdjustmentSendToClient \\\n          -H \"Authorization: Bearer YOUR_TOKEN\" \\\n          -H \"Content-Type: application/json\" \\\n          -d '[{\"stock_adjustment_id\":123456,\"warehouse_reference\":\"whRef1234\"}]'\n        \n         ```\n        \n    - GRN:\n        \n        ```\n        curl -X POST https://sandbox.api.thgingenuity.com/goods-in/asn/abc123/grn \\\n          -H \"Authorization: Bearer YOUR_TOKEN\" \\\n          -H \"X-THG-Channel: Channel\"\n        \n         ```\n        \n    - Return Status Update:\n        \n        ```\n        curl -X POST https://sandbox.api.thgingenuity.com/v2/order/return-update \\\n          -H \"Authorization: Bearer YOUR_TOKEN\"\n        \n         ```\n        \n    - Each call responds `202 Accepted` immediately; the sample payload arrives at your registered URL as a separate request shortly after - check there (not the `202` response) to confirm the test worked.\n        \n\n#### **4\\. Handling Errors in the Sandbox**\n\n- Simulated HTTP Status Codes\n    - Mocks are configured to return various HTTP status codes to simulate production errors (e.g., 200 Success, 202 Accepted, 400 Bad Request, 403 Forbidden, 404 Not Found, 500 Internal Server Error).\n        \n- Simulated Error Messages/Payloads\n    - Mocks may return specific error codes and descriptions as defined in the API documentation\n        \n\n#### **5\\. Troubleshooting Common Sandbox Issues**\n\n- Common authentication errors (e.g., invalid or missing token).\n    \n- Requests not triggering expected mock responses (check required parameters and headers).\n    \n- Issues related to asynchronous notification simulation.\n    \n- Validation errors based on predefined sandbox constraints.\n    \n\n#### **6\\. Available Mocked API Endpoints (THG Fulfil Section)**\n\nThis section outlines the key API endpoints within the THG Fulfil scope that are available as mocks in the sandbox.\n\n- Examples from the API docs: [https://postman.developer.thgingenuity.com/#5510d46d-a0a2-4171-9816-8d920c145f23](https://postman.developer.thgingenuity.com/#5510d46d-a0a2-4171-9816-8d920c145f23)\n    \n- **D2C Orders**\n    \n    - Additional **Create Order** `PUT /v2/order` mocks:\n        - Repeated order lines for one product when the details are the same: Success 200\n            \n        - ClientOrderReference = ClientOrder99999 creates a 500 Server Error\n            \n    - **Cancel Order/Order Lines** `POST /v2/order/action` with updates/additions:\n        - 200 Success with correct body as in docs:\n            - client-order-reference=ClientOrder00001 (as in docs)\n                \n            - client-order-reference=ClientOrder00001&platform-order-reference=AB_1234\n                \n        - 404 Not Found with correct body as in docs:\n            - client-order-reference=ClientOrder00003\n                \n            - client-order-reference=ClientOrder11112233 (as in docs)\n                \n        - 410 Gone with correct body as in docs:\n            - client-order-reference=ClientOrder00002\n                \n            - client-order-reference=ClientOrder332211\n                \n        - 500 Server Error with correct body as in docs:\n            - client-order-reference=ClientOrder99999 (not 11112233 as in docs)\n                \n    - **Get Order Status** `GET /v2/order/status`:\n        - 200 Success:\n            - client-order-reference=ClientOrder00001\"\n                \n            - platform-order-reference=AB_1234\"\n                \n        - 200 Success split shipment (one dispatched, other cancelled):\n            - client-order-reference=ClientOrder00004\"\n                \n        - 400 Bad Request as in docs\n            \n        - 404 Not Found as in docs plus:\n            - client-order-reference=unknown\n                \n            - client-order-reference=ClientOrder00003\n                \n        - 410 Gone:\n            - client-order-reference=ClientOrder00002\n                \n        - 500 Server Error:\n            - client-order-reference=ClientOrder99999\n                \n    - **Preview Order State Update payload (legacy, no registration needed)** `POST /v1/sendOrderStateUpdate`:\n        - Returns the sample message directly in the response - use a REST client to retrieve it and inspect the shape, or point your own tooling at it manually. Does not push anywhere.\n            \n        - 200 Success Single shipment with body as in docs\n            \n        - 200 Success Split shipment with split shipment body as in docs with client-order-reference: \"ClientOrder00001\" or \"ClientOrder00004\"\n            \n        - 400 Bad Request as in docs or with a successful body but missing a header (client-order-reference, X-THG-Channel)\n            \n        - 404 Not Found with client-order-reference: \"ClientOrder00003\" - the example in the docs matches 200 Success so use this special client-order-reference\n            \n        - 500 Server Error as in docs or with client-order-reference: \"ClientOrder99999\" -the example in the docs matches a 200 Success so use this special client-order-reference\n            \n    - **Send Order State Update to client (real outbound push)** `POST /v1/sendOrderStateUpdateToClient` (also `POST /v3/sendOrderStateUpdateToClient`) - **Outbound, see Section 3a**:\n        - Requires a `client-order-reference` header and a registered callback URL (otherwise 501 - see Section 3a).\n            \n        - 202 Accepted acknowledgement in the response; the actual sample order state payload is POSTed to your registered URL separately.\n            \n- **Goods In** - Being tested at the moment, these may change\n    \n    - **Pre-Advice**\n        \n        - Creating a Pre-Advice `POST /goods-in/pre-advice` as in docs, plus:\n            - 400 Bad Request: \"warehouseID\": 90 or field missing\n                \n        - Querying Pre-Advice Status `GET /goods-in/pre-advice/{pre_advice}/pre-advice-approval` as in docs, plus:\n            - **note to use GET and not \"--data ''\"** (POST will return a message to use GET)\n                \n            - 200 Success: /goods-in/pre-advice/f798c408-260d-467c-82f4-5fc6f2944ef4/pre-advice-approval\n                - or any string not including unknown, reject, busy (case insensitive)\n                    \n            - 200 Success UKNOWN_PRODUCT response: /goods-in/pre-advice/advicerejected/pre-advice-approval\n                - or any string that includes unknown or reject (case insensitive)\n                    \n            - 200 Success WAREHOUSE_BUSY response: /goods-in/pre-advice/advicebusyresp/pre-advice-approval\n                - or any string that includes busy (case insensitive)\n                    \n            - 400 Bad Request: /goods-in/pre-advice//pre-advice-approval - missing {pre-advice} field\n                \n        - Get Pre-Advice Status `GET /goods-in/pre-advice/{pre_advice}` as in docs - note to use GET and not \"--data ''\"\n            \n    - **ASN (Advanced Shipment Notification)**\n        \n        - Creating ASN `POST /goods-in/asn` as in docs, plus:\n            - 400 Bad Request: \"warehouseID\": 90 or field missing\n                \n        - On all of the GET commands below - **note to use GET and not \"--data ''\"** (POST will return a message to use GET)\n            - Get Approval `GET /goods-in/asn/{asn}/approval` as in docs\n                \n            - Get Progress `GET /goods-in/asn/{asn}/progress` as in docs\n                \n            - Get GRN (Goods Receipt Note) `GET /goods-in/asn/{asn}/grn` as in docs\n                \n            - Get ASN `GET /goods-in/asn/{asn}/asn` as in docs\n                \n        - **Send GRN to client (real outbound push)** `POST /goods-in/asn/{asn}/grn` - **Outbound, see Section 3a** - same path as the GET above, different method and behaviour:\n            - Requires an `X-THG-Channel` header and a registered callback URL (otherwise 501 - see Section 3a).\n                \n            - 202 Accepted acknowledgement in the response; the actual sample GRN payload is POSTed to your registered URL separately.\n                \n- **Product**\n    \n    - **Create Product** `POST /v1/products`\n        - 201 Success as in doc examples\n            \n    - **Update Product** `PUT /v1/products/{{id}}`\n        - 200 Success as in doc examples\n            \n    - **Get Product** `GET /v1/products/{{id}}`\n        - 200 Success as in doc examples using 123456, for example\n            \n        - 404 Not Found example using id 99999\n            \n    - **Get THG Product ID** `GET /v1/products/{{id}}/mapping` - note: this is likely to change\n        - 200 Success as in doc examples using 123456, for example\n            \n        - 404 Not Found example using id 99999\n            \n    - **Get Multiple THG Product IDs** `POST /v1/products/mappings`\n        - 200 Success as in doc examples\n            \n    - **Get Multiple Client or THG ID Mappings** `POST /v1/products/idmaps`\n        - 200 Success as in doc examples\n            \n        - 400 Incomplete Request as in doc examples\n            \n        - 404 Not Found as in doc examples\n            \n        - 500 Internal Server Error as in doc examples\n            \n- **Returns**\n    \n    - **Create Return** `POST /v2/order/returns`\n        - 200 Success as in doc examples\n            \n        - 422 Unprecessable as in doc examples\n            \n        - 404 Not Found use \"orderNumber\": \"11112233\" with normal body\n            \n    - **Cancel Return**: `PUT /v2/order/returns/{returnShipmentNumber}/cancel`\n        - As in docs\n            \n    - **Get Paginated Returns**: Details on the `GET /v2/order/returns/paginated` endpoint mock.\n        - As in the docs:\n            - Get result set: `GET /v2/order/returns/paginated?pageSize=1&returnStatus=CREATED` with 200 Success\n                \n        - Except:\n            - Get empty result set: `GET /v2/order/returns/paginated` with 200 Success\n                \n            - Failed query - invalid query parameter: `GET http://localhost:8080/v2/order/returns/paginated?invalidParam=value` - example in the doc needs updating\n                \n    - **Get Return by Shipment Number** `GET /v2/order/returns/{returnShipmentNumber}` as in docs:\n        - 200 Success with returnShipmentNumber 612\n            \n        - 404 Not Found with returnShipmentNumber 613\n            \n        - 400 Bad Request with returnShipmentNumber 710\n            \n        - 409 Conflict with returnShipmentNumber 713\n            \n        - 401 Unauthorized with returnShipmentNumber 711 or without the correct Authorization header\n            \n        - 500 Internal Server Error with returnShipmentNumber 712\n            \n    - **Send Return Status Update to client (real outbound push)** `POST /v2/order/return-update` - **Outbound, see Section 3a**:\n        - Requires a registered callback URL (otherwise 501 - see Section 3a).\n            \n        - 202 Accepted acknowledgement in the response; the actual sample return status payload is POSTed to your registered URL separately.\n            \n- **Stock**\n    \n    - **Get Stock Availability** `GET /v1/availability/productId/{{productId}}`\n        - 200 Success example with productId: 12345678\n            \n        - 404 Not Found example with productId: x123\n            \n    - **Get Stock Adjustment by ID** `GET /v1/stock/adjustments/stockadjustmentid/{{stockAdjustmentId}}`\n        - 200 Success using stockAdjustmentId: 123456\n            \n        - 404 Not Found using stockAdjustmentId: 222\n            \n    - **Get Stock** `GET /v1/stock/product/{{productId}}`\n        - 200 Success example with productId: 1234567 - should be 7 digit productId\n            \n        - 404 Not Found example with productId: x123 - non-digits\n            \n        - 404 Not Found example with productId: 91123\n            \n        - 500/504 Server Error/Gateway Timeout with productId: 99093\n            \n    - **Preview Stock Adjustment payload (legacy, no registration needed)** `POST /stockAdjustment`:\n        - Returns the sample message directly in the response - use a REST client to retrieve it and inspect the shape. Does not push anywhere.\n            \n        - 200 Success /stockAdjustment using example in docs with stock_adjustment_id:123456 and warehouse_reference:whRef1234\n            \n        - 400 Bad Request /stockAdjustment using example in docs with stock_adjustment_id:91234\n            \n    - **Send Stock Adjustment to client (real outbound push)** `POST /stockAdjustmentSendToClient` - **Outbound, see Section 3a**:\n        - Requires `Content-Type: application/json`, a body containing `stock_adjustment_id`, and a registered callback URL (otherwise 501 - see Section 3a).\n            \n        - 202 Accepted acknowledgement in the response; the actual sample stock adjustment payload is POSTed to your registered URL separately.\n            \n    - Not available in production: **Get Stock Adjustments** (time range) `GET /v1/stock/adjustments/productId/{{productId}}/from/{{fromDate}}/to/{{toDate}}` - postponed\n        - 200 Success example: /v1/stock/adjustments/productId/1234567/from/2014-05-14T14:11:01Z/to/2015-05-14T14:11:01Z\n            \n        - 404 Not Found example: /v1/stock/adjustments/productId/91234/from/nineteen-dickety-six/to/2015-05-14T14:11:01Z\n            \n    - **Deprecated Stock Endpoint**: Mention the `GET /v2/stock/product/{productId}` endpoint is deprecated and superseded by the v1 endpoint.\n        - 200 Success /v2/stock/product/1234567 with status code 200\n            \n        - 404 Not Found /v2/stock/product/91234 with status code 404\n            \n        - 404 Not Found /v2/stock/product/invalidstringid with status code 404\n            \n        - 500 Internal Server Error /v2/stock/product/99093 with status code 500\n            \n\n---\n\n- v.2026-08-16 changes:\n    - Added Section 3a: outbound webhooks can now push real calls to a client-registered URL (Order State Update, Stock Adjustment, GRN, Return status update), rather than only returning the sample in the triggering call's response.\n        \n    - Relabelled the old outbound endpoints (`/v1/sendOrderStateUpdate`, `/stockAdjustment`) as legacy preview-only, and added the new `...ToClient`/`SendToClient`/`return-update` endpoints that trigger real delivery.\n        \n    - Added copy-pasteable `curl` examples for each webhook trigger endpoint in Section 3a.\n        \n- v.2025-05-21 changes:\n    - Highlight Outbound calls and need for clients to use RESTclient to call their systems\n        \n    - Update product endpoints\n        \n    - Add stock by product endpoint\n        \n- v.2025-05-12 changes:\n    - Clarify pre-advice approval success cases\n        \n- v.2025-07-31 changes:\n    - Added Multiple Client or THG ID Mappings\n        \n\n---\n\n# Rate Limiting\n\n### Inbound to the Ingenuity API\n\nCalls to the Ingenuity API are subject to rate limiting. The limit is configured on a per endpoint basis. The default limit is 250 requests per minute, this can be changed depending on the need and use case for calling the endpoint.\n\nWhen a rate limit is exceeded, the client will receive an HTTP 429 response. [429 Too Many Requests](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/429)\n\nOrdinarily, the rate limit is set per minute, therefore the call count resets every 60 seconds.\n\n### Outbound from the Ingenuity API\n\nCalls made outwards from the Ingenuity API are not rate limited by the calling API. If the API received an HTTP 429 response, the standard behaviour is to retry. For synchronous outbound calls, the behaviour is handed over to the calling service and the HTTP 429 is passed onto the caller to handle.\n\nThe standard retry mechanism (for asynchronous calls) is a back-off and retry process which will retry messages in bursts at varying intervals up to around 19 hours in total duration, after which the call is deemed as failed and sent to a failure topic for the calling service.\n\n---","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","isPublicCollection":false,"owner":"21093843","team":3506961,"collectionId":"a8cc246c-c041-47ca-8dc2-36b12c910ca4","publishedId":"Uz5AseFz","public":true,"publicUrl":"https://postman.developer.thgingenuity.com","privateUrl":"https://go.postman.co/documentation/21093843-a8cc246c-c041-47ca-8dc2-36b12c910ca4","customColor":{"top-bar":"FFFFFF","right-sidebar":"0d0d0d","highlight":"0065e0"},"documentationLayout":"classic-double-column","customisation":{"metaTags":[{"name":"description","value":""},{"name":"title","value":"THG Ingenuity Fulfil API Documentation"}],"appearance":{"default":"light","themes":[{"name":"dark","logo":"https://content.pstmn.io/21bf6784-d87c-42e6-9bf0-49468774970f/aWRTcWlRaDhmS18xNzQ2NTI2ODg3NDU5LmpwZWc=","colors":{"top-bar":"1a1a1a","right-sidebar":"0d0d0d","highlight":"0065e0"}},{"name":"light","logo":"https://content.pstmn.io/21bf6784-d87c-42e6-9bf0-49468774970f/aWRTcWlRaDhmS18xNzQ2NTI2ODg3NDU5LmpwZWc=","colors":{"top-bar":"FFFFFF","right-sidebar":"0d0d0d","highlight":"0065e0"}}]}},"version":"8.12.11","publishDate":"2025-05-12T10:43:49.000Z","activeVersionTag":"latest","documentationTheme":"light","metaTags":{"title":"THG Ingenuity Fulfil API Documentation","description":""},"logos":{"logoLight":"https://content.pstmn.io/21bf6784-d87c-42e6-9bf0-49468774970f/aWRTcWlRaDhmS18xNzQ2NTI2ODg3NDU5LmpwZWc=","logoDark":"https://content.pstmn.io/21bf6784-d87c-42e6-9bf0-49468774970f/aWRTcWlRaDhmS18xNzQ2NTI2ODg3NDU5LmpwZWc="}},"statusCode":200},"environments":[],"user":{"authenticated":false,"permissions":{"publish":false}},"run":{"button":{"js":"https://run.pstmn.io/button.js","css":"https://run.pstmn.io/button.css"}},"web":"https://www.getpostman.com/","team":{"logo":"https://res.cloudinary.com/postman/image/upload/t_team_logo_pubdoc/v1/team/024937105ad1b3b47bff98901f1eb7745164e8fea930f4e4d7473e3bd42f7bf5","favicon":"https://thgingenuity.com/favicon.ico"},"isEnvFetchError":false,"languages":"[{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"HttpClient\"},{\"key\":\"csharp\",\"label\":\"C#\",\"variant\":\"RestSharp\"},{\"key\":\"curl\",\"label\":\"cURL\",\"variant\":\"cURL\"},{\"key\":\"dart\",\"label\":\"Dart\",\"variant\":\"http\"},{\"key\":\"go\",\"label\":\"Go\",\"variant\":\"Native\"},{\"key\":\"http\",\"label\":\"HTTP\",\"variant\":\"HTTP\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"OkHttp\"},{\"key\":\"java\",\"label\":\"Java\",\"variant\":\"Unirest\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"Fetch\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"jQuery\"},{\"key\":\"javascript\",\"label\":\"JavaScript\",\"variant\":\"XHR\"},{\"key\":\"c\",\"label\":\"C\",\"variant\":\"libcurl\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Axios\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Native\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Request\"},{\"key\":\"nodejs\",\"label\":\"NodeJs\",\"variant\":\"Unirest\"},{\"key\":\"objective-c\",\"label\":\"Objective-C\",\"variant\":\"NSURLSession\"},{\"key\":\"ocaml\",\"label\":\"OCaml\",\"variant\":\"Cohttp\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"cURL\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"Guzzle\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"HTTP_Request2\"},{\"key\":\"php\",\"label\":\"PHP\",\"variant\":\"pecl_http\"},{\"key\":\"powershell\",\"label\":\"PowerShell\",\"variant\":\"RestMethod\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"http.client\"},{\"key\":\"python\",\"label\":\"Python\",\"variant\":\"Requests\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"httr\"},{\"key\":\"r\",\"label\":\"R\",\"variant\":\"RCurl\"},{\"key\":\"ruby\",\"label\":\"Ruby\",\"variant\":\"Net::HTTP\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"Httpie\"},{\"key\":\"shell\",\"label\":\"Shell\",\"variant\":\"wget\"},{\"key\":\"swift\",\"label\":\"Swift\",\"variant\":\"URLSession\"}]","languageSettings":[{"key":"csharp","label":"C#","variant":"HttpClient"},{"key":"csharp","label":"C#","variant":"RestSharp"},{"key":"curl","label":"cURL","variant":"cURL"},{"key":"dart","label":"Dart","variant":"http"},{"key":"go","label":"Go","variant":"Native"},{"key":"http","label":"HTTP","variant":"HTTP"},{"key":"java","label":"Java","variant":"OkHttp"},{"key":"java","label":"Java","variant":"Unirest"},{"key":"javascript","label":"JavaScript","variant":"Fetch"},{"key":"javascript","label":"JavaScript","variant":"jQuery"},{"key":"javascript","label":"JavaScript","variant":"XHR"},{"key":"c","label":"C","variant":"libcurl"},{"key":"nodejs","label":"NodeJs","variant":"Axios"},{"key":"nodejs","label":"NodeJs","variant":"Native"},{"key":"nodejs","label":"NodeJs","variant":"Request"},{"key":"nodejs","label":"NodeJs","variant":"Unirest"},{"key":"objective-c","label":"Objective-C","variant":"NSURLSession"},{"key":"ocaml","label":"OCaml","variant":"Cohttp"},{"key":"php","label":"PHP","variant":"cURL"},{"key":"php","label":"PHP","variant":"Guzzle"},{"key":"php","label":"PHP","variant":"HTTP_Request2"},{"key":"php","label":"PHP","variant":"pecl_http"},{"key":"powershell","label":"PowerShell","variant":"RestMethod"},{"key":"python","label":"Python","variant":"http.client"},{"key":"python","label":"Python","variant":"Requests"},{"key":"r","label":"R","variant":"httr"},{"key":"r","label":"R","variant":"RCurl"},{"key":"ruby","label":"Ruby","variant":"Net::HTTP"},{"key":"shell","label":"Shell","variant":"Httpie"},{"key":"shell","label":"Shell","variant":"wget"},{"key":"swift","label":"Swift","variant":"URLSession"}],"languageOptions":[{"label":"C# - HttpClient","value":"csharp - HttpClient - C#"},{"label":"C# - RestSharp","value":"csharp - RestSharp - C#"},{"label":"cURL - cURL","value":"curl - cURL - cURL"},{"label":"Dart - http","value":"dart - http - Dart"},{"label":"Go - Native","value":"go - Native - Go"},{"label":"HTTP - HTTP","value":"http - HTTP - HTTP"},{"label":"Java - OkHttp","value":"java - OkHttp - Java"},{"label":"Java - Unirest","value":"java - Unirest - Java"},{"label":"JavaScript - Fetch","value":"javascript - Fetch - JavaScript"},{"label":"JavaScript - jQuery","value":"javascript - jQuery - JavaScript"},{"label":"JavaScript - XHR","value":"javascript - XHR - JavaScript"},{"label":"C - libcurl","value":"c - libcurl - C"},{"label":"NodeJs - Axios","value":"nodejs - Axios - NodeJs"},{"label":"NodeJs - Native","value":"nodejs - Native - NodeJs"},{"label":"NodeJs - Request","value":"nodejs - Request - NodeJs"},{"label":"NodeJs - Unirest","value":"nodejs - Unirest - NodeJs"},{"label":"Objective-C - NSURLSession","value":"objective-c - NSURLSession - Objective-C"},{"label":"OCaml - Cohttp","value":"ocaml - Cohttp - OCaml"},{"label":"PHP - cURL","value":"php - cURL - PHP"},{"label":"PHP - Guzzle","value":"php - Guzzle - PHP"},{"label":"PHP - HTTP_Request2","value":"php - HTTP_Request2 - PHP"},{"label":"PHP - pecl_http","value":"php - pecl_http - PHP"},{"label":"PowerShell - RestMethod","value":"powershell - RestMethod - PowerShell"},{"label":"Python - http.client","value":"python - http.client - Python"},{"label":"Python - Requests","value":"python - Requests - Python"},{"label":"R - httr","value":"r - httr - R"},{"label":"R - RCurl","value":"r - RCurl - R"},{"label":"Ruby - Net::HTTP","value":"ruby - Net::HTTP - Ruby"},{"label":"Shell - Httpie","value":"shell - Httpie - Shell"},{"label":"Shell - wget","value":"shell - wget - Shell"},{"label":"Swift - URLSession","value":"swift - URLSession - Swift"}],"layoutOptions":[{"value":"classic-single-column","label":"Single Column"},{"value":"classic-double-column","label":"Double Column"}],"versionOptions":[],"environmentOptions":[{"value":"0","label":"No Environment"}],"canonicalUrl":"https://postman.developer.thgingenuity.com/view/metadata/Uz5AseFz"}