What is a webhook?
A webhook is an asynchronous communication method that sends a one-way notification using an HTTP service. It is triggered by an event and sends information without an initial client or user request.
To function, a webhook must be subscribed to a specific topic. When the event occurs, the webhook receives and processes the information.
Example Use Case in OneStock: When an order changes state (e.g., from placed to shipped), instead of continuously polling an API, a webhook sends a notification of the state change directly to the user.
Implementing a Webhook in OneStock
OneStock OMS requires certain configurations and parameters to be set before a webhook can be used. A legitimacy check can also be added as a final step. This is optional, but highly recommended. We discuss each of these steps and requirements below.
In order to configure webhooks in OneStock you will need to complete the 3 following steps:
Configure your site for webhook notifications
Set up a webhook
Subscribe your webhook to a topic
1. Configure your site for webhooks
Your site configuration sets the following parameters:
The retry delay before a temporary or complete stop
A contact in case of an error
The name of the notification to send in the error message
A template that can be used for configuring at Order Management Center/Configurations/Outbound Messages/Webhooks
The default retry delay values are set as shown above in seconds. In this instance the webhook would make the first call and if no response or confirmation is received then it will retry 6 times. However, after the 3rd failed attempt, i.e. one with no response, it will send a notification of failure to the contact details in the configuration.
If a response or confirmation is received before the 6th call then the webhook will send another notification, as it is now classed as ‘recovered’. The retry counter will then be reset at 0.
2. Set Up a Webhook
Creating a webhook is mostly done via API (use the POST /webhooks
route) however some options can be set via our new configuration screens. These are shown later.
The configuration of the webhook defines important information such as which topic to subscribe to and the HTTP method to connect.
There are three mandatory fields required for webhooks with OneStock, they are:
Your Site ID
The HTTP method
The destination URL
Your OneStock contact will be able to provide with your Site ID if you do not know it. It will be a series of numbers following a lower case 'c', for example c404.
OneStock uses RESTful APIs so the HTTP method can be GET
XGET
POST
PATCH
PUT
or DELETE
.
The destination URL will be the address where the webhook will send its requests.
3. Subscribe your webhook to a topic
In order to subscribe your webhook to a topic, add the topic to the code, as shown in the example below on lines 5 and 6.
{ "site_id": "MySiteID", "webhook": { "http_method": "POST", "topics": [ "stock_import_error_occurred" ], "url": "https://myWebhookURL" } }
Information about the expected responses can be found in our documentation here.
Available standard topics
This is a list of all the events we currently send. We may add more at any time, so in developing and maintaining your code, you should not assume that only these topics exist.
Topic | Displayed Name | Description | Message structure | Example |
---|---|---|---|---|
buffer_import_completed | Buffer Import Completed | Notifies when an async buffer import using API or file is completed |
import_id: string status: string | |
12.4 buffer_import_error_occurred | Buffer Import Error Occurred | Notifies when a synchronous buffer import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_buffer_import_error_occurred | Asynchronous Buffer Import Error Occurred | Notifies when an asynchronous buffer import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
customer_import_completed | Customer Import Completed | Notifies when an async customer import using API or file is completed |
import_id: string status: string | |
12.4 customer_import_error_occurred | Customer Import Error Occurred | Notifies when a synchronous customer import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_customer_import_error_occurred | Asynchronous Customer Import Error Occurred | Notifies when a asynchronous customer import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
transfer_import_completed | Transfer Import Completed | Notifies when an async transfer import using API or file is completed |
import_id: string status: string | |
transfer_import_error_occurred | Transfer Import Error Occurred | Notifies when a synchronous transfer import using API POST method fails |
error: string invalid_transfers: string request_body: string date: int64 | |
12.4 async_customer_import_error_occurred | Asynchronous Transfer Import Error Occurred | Notifies when an asynchronous transfer import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
stock_import_completed | Stock Import Completed | Notifies when an async stock import using API or file is completed |
import_id: string status: string | |
stock_import_error_occurred | Stock Import Error Occurred | Notifies when a synchronous stock import using API POST method fails |
error: string error_content: string import_id: string request_body: string date: int64 | |
12.4 async_stock_import_error_occurred | Asynchronous Stock Import Error Occurred | Notifies when an asynchronous stock import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
line_item_group_state_changed | Line Item Group State Change | Notifies of a state change for line item groups |
order_id: string date: int64 old_state: string new_state: string order_item_id: string index_ranges: indexRanges quantity: int64
| |
order_state_changed | Order State Change | Notifies of a state change for orders |
order_id: string date: int64 old_state: string new_state: string
| |
parcel_state_changed | Parcel State Change | Notifies of a state change for parcels |
order_id: string date: int64 old_state: string new_state: string parcel_id: string
| |
entity_updated | Entity Update | Notifies of OMS entities information changes (parcels, line item groups and orders) POP (endpoint_orders, containers, piece_groups) and RM (return_parcels, and return_line_item_groups) |
site_id: string entity_id: string entity_type: string date: int64 diff: map[string]any | |
orchestration_rules_changed | Orchestration Rules Changed | Allows triggering of preparation order on time. When there is an orchestration rule change with a warehouse custom claim action triggered, line item groups are retrieved and a preparation order is sent to the warehouse. |
site_id: string order_id: string ruleset_id: string from: string to: string custom_actions: [] action: string rule_index: string line_item_index_ranges: indexRange rules_over: string
| |
candidates_added | Candidates Added | Notifies that an endpoint is candidate for an order. |
site_id: string order_id: string endpoint_ids: []string
| |
candidates_removed | Candidates Removed | Notifies when an endpoint is no longer candidate for an order. |
site_id: string order_id: string endpoint_ids: []string
| |
rules_over | Rules Over (custom notification) | Notifies of a rules over transition for an order. |
site_id: string order_id: string ruleset_id: string line_item_index_ranges: IndexRanges
site_id: string order_id: string ruleset_id: string
site_id: string order_id: string ruleset_id: string
| |
item_import_completed | Item Import Completed | Notifies of a completed item import |
import_id: string status: string | |
12.4 item_import_error_occurred | Item Import Error Occurred | Notifies when a synchronous item import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_item_import_error_occurred | Asynchronous Item Import Error Occurred | Notifies when an asynchronous item import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
psp_error_occurred | Payment Error | Notifies of errors during payment treatment |
psp: string request_route: string request_body: string response_body: string | |
container_state_changed operator_state_changed endpoint_order_state_changed piece_group_state_changed | Order Preparation Entity Change | Notifies of order preparation entity state changes (creation, update, removal) |
entity_id string entity_type string site_id string date string old_state string new_state string | |
return_parcel_created | Return Parcel Creation | Notifies of the creation of a return parcel |
order_id: string id: string user_id: string creation_date: int64 return_note_id: string document_id: string last_update: int64 returned_line_items: [] creation_date: string id: string last_update: string reason: string receiver_comment: string return_condition: string sender_comment: string item_id: string state: string information: map[string]any state: string delivery: origin: address: endpoint_id: string destination: address: id: string city: string contact: title: string first_name: string last_name: string company_name: string phone_number: string mobile_number: string email: string coordinates: lon: float64 lat: float64 information: map[string]string lines: []string regions: map[string] name: string code: string zip_code: string endpoint_id: string carrier: name: string option: string tracking_code: string tracking_link: string information: map[string]any
documents: map[string]string order_id: string id: string user_id: string creation_date: int64 last_update: int64 return_line_item_groups: [] id: string order_item_id: string creation_date: int64 last_update: int64 reason: string receiver_comment: string return_condition: string sender_comment: string quantity: int64 line_item_index_ranges: IndexRanges item_id: string state: string information: map[string]any state: string delivery: origin: address: endpoint_id: string destination: address: id: string city: string contact: title: string first_name: string last_name: string company_name: string phone_number: string mobile_number: string email: string coordinates: lon: float64 lat: float64 information: map[string]string lines: []string regions: map[string] name: string code: string zip_code: string endpoint_id: string carrier: name: string option: string tracking_code: string tracking_link: string information: map[string]any shipment: id: string document_id: string tracking_code: string tracking_link: string transporter: string carrier_data: string
| |
return_line_item_group_state_changed return_parcel_state_changed |
| Notifies of a state change for a return return_line_item_group or return_parcel |
entity_id: string entity_type: string site_id: string date: int64 old_state: string new_state: string
| |
line_items_reservations_updated | Line Items Reservations Update | Notifies of a reservation update on a line item group (global reservation to endpoint reservation, future stock to on hand, endpoint reservation removal) |
added_reservations: [] id: string order_id: string sales_channel: string global: string line_item_index_ranges: index_ranges quantity: int64 item_id: string endpoint_id: string eta_start: int64 eta_end: int64 stock_type: string purchase_order_number: string remaining_line_item_index_ranges: index_ranges removed_reservations: [] id: string order_id: string sales_channel: string global: string line_item_index_ranges: index_ranges quantity: int64 item_id: string endpoint_id: string eta_start: int64 eta_end: int64 stock_type: string purchase_order_number: string remaining_line_item_index_ranges: index_ranges
| |
stock_coverage_import_completed | Stock Coverage Import Completed | Notifies of a completed stock coverage import |
import_id: string status: string | |
12.4 stock_coverage_import_error_occurred | Stock Coverage Import Error Occurred | Notifies when a synchronous stock coverage import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_stock_coverage_import_error_occurred | Asynchronous Stock Coverage Import Error Occurred | Notifies when an asynchronous stock coverage import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
stock_disposition_import_completed | Stock Disposition Import Completed | Notifies of a completed stock disposition import |
import_id: string status: string | |
12.4 stock_disposition_import_error_occurred | Stock Disposition Import Error Occurred | Notifies when a synchronous stock disposition import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_stock_disposition_import_error_occurred | Asynchronous Stock Disposition Import Error Occurred | Notifies when an asynchronous stock disposition import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
endpoint_import_completed | Endpoint Import Completed | Notifies of a completed stock import |
import_id: string status: string | |
12.4 endpoint_import_error_occurred | Endpoint Import Error Occurred | Notifies when a synchronous endpoint import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_endpoint_import_error_occurred | Asynchronous Endpoint Import Error Occurred | Notifies when an asynchronous endpoint import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
user_import_completed | User Import Completed | Notifies of a completed user import |
import_id: string status: string | |
12.4 user_import_error_occurred | User Import Error Occurred | Notifies when a synchronous user import using API POST method fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
12.4 async_user_import_error_occurred | Asynchronous User Import Error Occurred | Notifies when an asynchronous user import fails |
import_id?: string errors: [] entity_id?: string message: string date: int64 | |
carrier_error_occurred | Carrier Error | Relays of errors during carrier communication |
carrier: string request_route: string request_body: string response_body: string | |
tracking_link_created |
| Notifies of the creation of a tracking link |
carrier: string tracking_link: string | |
shipment_created |
| Notifies of the creation of a shipment |
carrier: string endpoint_id: string order_id: string parcel_id: string shipment_id: string user_id: string | |
stock_export_completed | Stock Export Completed | Notifies the completion of a stock export by file (SFTP) | export_id string file_job_id string exported_lines_count int64 | |
import_error - custom topic | Notifies an error when importing entities async. All entities will be sent in through the same topic. |
object_id: string object_type: string params: {} content: string date: int error: string | { "object_id": "cd495ac9-2731-11ef-b4ec-02420a00011d", "object_type": "item_import", "params": { "content": "{\"failures\":[{\"id\":\"BUNDLE_0001\",\"errors\":[\"bundle must not contain another bundle\"]}]}", "date": 1718028036, "error": "Invalid item(s) detected" } } |
Custom topic subscriptions
If the event you wish to subscribe to is not listed above you can create a custom topic. The custom topic can either notify based on an existing OneStock event, or based on an event triggered in the workflow.
A custom topic can be created either via API, or via the Back Office Configuration screens.
It is easier to configure via the Back Office Configuration screens, found in Configuration > Outbound Messages > Notifications. A default configuration will be shown in JSON, which can be modified or added to in order to create a new topic notification.
In order to configure a webhook via API the topic must be set using the route POST /webhook_topics
as shown below.
{ "site_id": "{{My_Site_ID}}", "token":"{{token}}", "webhook_topic": { "topic": "myCustomTopic", "ordered": false } }
Triggering custom notification from the workflow
Add a send notification event in the transition that must trigger the custom notification, an set the custom name, in our case, import_error
, but this could be any name you configur in your notifications.
4. Acknowledgment of Webhook Receipt
Upon receiving a message via webhook, it is essential to return a 202 Accepted
response promptly (within 15 seconds) after verifying the message’s legitimacy (optional but recommended). If this acknowledgment is not received in time, the system will retry sending the message according to the retry_intervals
configuration, if retries are not acknowledged promptly
Legitimacy Check
While optional, OneStock strongly recommends performing a legitimacy check to ensure that the webhook message is genuinely sent by the OneStock system.
Each webhook message includes a signature in the header with the following format: timestamp + "." + signature
.
During webhook setup (POST /webhooks
), a hash key is either provided by the client or generated by OneStock. This key, referred to as h0
, is used to encrypt the signature. OneStock retains the three most recent hash keys (h0, h1, h2) to support ongoing signature verification.
The signature format is:t=timestamp.h0=h[0],h1=h[1],h2=h[2]
, where:
timestamp
: The current timestamp.h[0]
: The signature encrypted using the latest hash key.h[1]
: The signature encrypted using the previous hash key.h[2]
: The signature encrypted using the oldest hash key.
Examples of signature and verification code
Webhook Acknowledgment Best Practices
To avoid blocking the entire message queue when processing webhooks, it is crucial to focus on the signature validation first and handle any additional checks asynchronously.
Recommended Acknowledgment Process:
Signature Validation:
Objective: Ensure the message is legitimately sent by OneStock.
Action: If the signature is valid, immediately return an HTTP
202 Accepted
response.
Asynchronous Content Validation (if applicable):
Any additional checks (e.g., content validation) should be performed asynchronously after acknowledging the message. This prevents delays in processing subsequent webhooks and ensures a smooth message flow.
Key Benefits
Prevent Queue Blocking: By acknowledging based solely on signature validation, the message queue remains smooth and uninterrupted.
Efficient Processing: Additional content checks can be handled asynchronously, avoiding potential bottlenecks.
5. Retries and Error Management
If acknowledgment is not received within the configured time frame, the system will retry sending the message. Notifications regarding errors can be sent via SMS, email, or both.
Reponse Statuses:
on_failure
: Triggered after the maximum number of retries (retries_until_failure
) is reached without acknowledgment. This indicates that the message failed to be delivered.on_failure_recovered
: Triggered when a failed message is later acknowledged during a retry. The retry count resets to 0.on_deactivation
: Triggered when the maximum retry attempts are exhausted without recovery. The webhook is automatically disabled and must be manually reactivated.
To resolve these errors, ensure your web service is functional and restart the webhook by setting it’s status to enabled
with a PATCH /webhooks/:id/status.
PATCH /webhooks/:id/status { "site_id": "{{site_id}}", "token": "{{token}}", "status": "enabled" }
6. Webhook Statuses
enabled
: The default state after creation. Messages are processed normally, triggering the webhook.Paused:
Messages are stored while the webhook is in a paused state. The behavior of this state depends on the scenario:Manual pause: No messages will be sent until the webhook is manually re-enabled.
Automatic pause: This occurs when a message is not acknowledged by the receiver within 15 seconds. In this case, the message will be retried. If the message fails to be acknowledged after the configured number of retries (
retries_until_failure
), an on_failure notification will be triggered (via email and/or SMS, depending on configuration). If the maximum number of retries is reached without success, the webhook will bedisabled
. However, if the message is acknowledged before reaching the retry limit, the webhook will automatically resume sending messages (enabled
).
disabled
: The webhook stops functioning and storing messages after anon_deactivation
error. It must be manually reactivated.
7. Message storage
Messages are stored for a week; those older than a week are automatically deleted.
8. Integration best practices
1. Use a Queuing System to Receive Messages
To ensure smooth and reliable communication, it's essential to acknowledge webhook messages swiftly—ideally within 15 seconds—by returning a 202 response code. This approach ensures that OneStock knows your system has received the message, allowing the process to continue without delays.
Why Use a Queuing System?
Immediate Acknowledgment: By decoupling message reception from processing, you can instantly acknowledge the receipt of the message. The queue receives and stores the message, allowing your system to process it asynchronously without holding up the response.
Enhanced Reliability: A queuing system provides a buffer, ensuring that even if your system is temporarily down or under heavy load, messages are not lost. They will be processed once your system is ready.
Easier Troubleshooting: Queuing services often come with tools that allow you to view and manage received messages, making it easier to diagnose and resolve any communication issues.
Recommended Queuing Solutions:
AWS SQS (Simple Queue Service)
Google Cloud Pub/Sub
Apache Kafka
2. Log Message Reception
If you choose not to use a queuing system, it's vital to implement robust logging of all received messages. Logging should include the message content and the response sent back to OneStock.
Benefits of Logging:
Monitoring and Auditing: Detailed logs provide a trail of received messages, which is crucial for monitoring system performance and ensuring that all messages are accounted for.
Incident Response: In the event of an issue, logs enable you to quickly identify and address the problem, minimizing downtime and maintaining communication integrity.
Compliance and Debugging: Logs can also serve as an essential resource for compliance and debugging purposes, helping you ensure that your system meets all necessary requirements and functions as expected.