Skip to main content

Overview

Webhooks deliver asynchronous notifications when events occur in the Return Helper system — for example, when a label is generated or a shipment arrives at the warehouse. Your server must expose an HTTPS endpoint to receive these POST requests.
Expect new fields. Return Helper adds fields to webhook payloads without prior notice. Do not strictly validate payload schemas — ignore fields you do not recognise and still respond with 2xx.

Setting Up Your Webhook Endpoint

Endpoint Requirements

Before registering your endpoint, make sure it meets the following requirements:
  • Publicly accessible — the URL must be reachable from the internet (no VPN, localhost, or internal-only addresses)
  • HTTPS — the endpoint must be served over HTTPS with a valid TLS certificate
  • HTTP POST — the endpoint must accept POST requests with a application/json body
  • Respond with 2xx — your server must return a 2xx status promptly after receiving the request; any other status code or a timeout is treated as a failed delivery
  • Fast response — process the event asynchronously if needed; do not perform heavy work before responding, to avoid delivery timeouts
  • Accept events you don’t handle — return 2xx for any category / action your handler doesn’t process. New events are added over time and are sent to every registered endpoint.

Registering Your Endpoint

Register and manage your webhook endpoints yourself through the API: Deploy your handler before you register it. Return Helper sends a signed verification request to the URL, and the endpoint is stored only if it responds with a 2xx status within 30 seconds. Each registered endpoint receives every event for your account. Filter on the category and action fields on your side. Sandbox and production are registered separately. Call the API in each environment you want notifications from, using that environment’s credentials.

Event Delivery

Timing — Events may arrive a few seconds after the triggering action, and in rare cases up to a few minutes later. Duplicate events — Your endpoint may receive the same event more than once. Track processed notificationId values to deduplicate; notificationId is unique per event and present on every webhook notification, so it is a stable idempotency key on its own. Event ordering — Delivery order is not guaranteed. Design your handler to process events in any order. For example, inventoryCreated may arrive before warehouseMarkShipmentArrivedV2. Use the API to fetch any objects referenced in events you receive out of sequence. Each event includes an eventTime field in ISO 8601 format.

Notification Headers

Every webhook request includes these headers:

Legacy headers

These headers are still sent on every notification and are not being removed. New integrations should read the headers above instead.
HTTP header names are case-insensitive, and some frameworks normalise them. The casing above is what Return Helper sends on the wire; if your framework lowercases incoming header names, match them case-insensitively.

Signature Verification

Always verify the signature before processing any payload. Use the raw request body — any transformation (e.g. by a framework that re-serialises JSON) will cause verification to fail.
Your signing key is provided by Return Helper (it is Base64-encoded). Store it securely and never expose it. You can find it in the User Portal on the same screen as your API key and token — see the screenshot under Authentication — or read it programmatically with Get signing key.

Worked Example

Given the following incoming request: Headers:
Body (raw JSON, must not be re-serialised):

Step-by-Step Verification

Step 1 — Extract the signature from the ReturnHelper-Signature header (for comparison at the end):
Step 2 — Extract the timestamp from the RETURNHELPER-TRIGGERED-AT header:
Step 3 — Build the string_to_sign Concatenate these four values in order (no separator):
  1. HTTP method: POST
  2. Your notification endpoint URL: https://s2024-01-12.free.beeceptor.com
  3. The RETURNHELPER-TRIGGERED-AT value from Step 2
  4. The raw JSON body
The resulting concatenated string:
Then Base64-encode the entire concatenated string. The result is the string_to_sign:
Step 4 — Compute the HMAC-SHA256 signature Using the example signing key (your actual key will be different):
Operations:
  1. Decode the string_to_sign (from Step 3) from Base64 → byte array
  2. Decode your signing key from Base64 → byte array
  3. Compute HMAC-SHA256 using the signing key bytes over the string_to_sign bytes → signature byte array
  4. Base64-encode the signature byte array
Expected result:
Step 5 — Compare signatures Compare the signature computed in Step 4 with the one extracted in Step 1. Use a constant-time string comparison to prevent timing attacks. Additional security: Reject events where eventTime differs from your system clock by more than 15 minutes (replay attack protection).

Sample Code

Retry Mechanism

Respond with a 2xx HTTP status code to acknowledge receipt. Non-2xx responses trigger retries. After 10 consecutive failures, notification delivery to your endpoint is suspended for 24 hours.

Common Body Fields

All notification bodies share these top-level fields:

Notification Event Reference


Notification Payloads

Label Result

Sent when a return label request completes (success or failure).
Always use shipmentId to match labels to shipments in your system — do not use labelId. In rare cases a carrier failure causes a new label (with a new labelId) to be issued for the same shipmentId.
category: labelGenerated / action: labelGenerated Key fields in label:

Regional label URLs

regions gives other URLs for the same label file as labelUrl. Use one when the label is downloaded or printed in that region.
  • Each value points to the same PDF as labelUrl. Only the host is different.
  • Every account gets it. No setup is needed.
  • Only the labelGenerated event has it. Other label URLs, in API responses and other webhook events, use the labelUrl host only. If you need the regional URL, save it from this event.
  • regions is {} when labelUrl is null (the label request failed).
  • Ignore keys you don’t recognise. If the key you need is missing, use labelUrl.
Success example:
Failure example:

Warehouse Shipment Arrived (v2)

Sent when a warehouse marks a shipment received. Always followed by one or more Inventory Created events.
This event echoes your sellerReferenceNumber and is the primary reconciliation point between your order records and Return Helper’s identifiers. The V202207 default carries SRN at all three layers in one bundled payload; V202407 carries only the Shipment-layer SRN. See Seller Reference Number for the full reconciliation workflow and version differences.
category: rsl / action: markShipmentArrive / version: 202407 Key fields in shipment:

Inventory Created

Sent after a shipment is received (or a VAS split occurs) to notify that a new return inventory record has been created. One event is sent per inventory item — a single shipment may produce multiple events if multiple packages were received under the same label.
No sellerReferenceNumber in this payload. Neither returnInventory nor shipment carries sellerReferenceNumber; only shipment.referenceNumber is present, and that field echoes the orderNumber you supplied — not the seller reference. If you rely on sellerReferenceNumber to reconcile inventory back to your own records, do not subscribe to newInventoryCreated alone. Pair it with Warehouse Shipment Arrived (carries SRN at all three layers in V202207) or Inventory Handling Complete (carries the Line-Item SRN on returnInventory.sellerReferenceNumber). See Seller Reference Number for the full reconciliation strategy.
category: newInventoryCreated / action: newInventoryCreated Key fields in returnInventory:

Image Updated

Sent when images are added, changed, or removed for a return inventory line item. category: rrli / action: changeLineItemImage Top-level payload fields: Key fields in returnRequestLineItem:
URLs in imageUrlList are publicly fetchable, do not expire, and are safe to cache client-side. Persist them as-is. Empty state: imageUrlList: [].

Unknown Shipment Assigned

Sent when a shipment with no prior return request is identified and assigned to a seller. category: rsl / action: assignUnknown / version: 202407 Key fields in returnInventory: Key fields in unknownShipment:

Recall Status Update

Recall is retiring soon. To send inventory to the Return Helper Hong Kong warehouse, create a consolidate shipping order with destinationType: RH_WAREHOUSE.
Sent when a recall tracking number is updated or pick-up status changes. category: recall / action: recallUpdateStatus recallUpdateTypeStatus values:

Resend Status Update

Sent when a resend tracking number is updated or the resend completes or fails. category: resend / action: updateResendStatus Top-level payload fields: Key fields in resend: Key fields in each resendShipmentList entry:
resendShipmentList always contains exactly one entry — a resend has one and only one resend shipment. Read it as resendShipmentList[0].
trackingNumber is not a top-level field. It lives on the resend shipment: resendShipmentList[0].trackingNumber.
Check resend.resendStatusCode:
  • 3 — completed (read resendShipmentList[0].trackingNumber)
  • 4 — failed (read resendShipmentList[0].error)
Use resendShipmentList[0].sellerReferenceNumber to match the event to your own order record without storing Return Helper’s resendId.
sellerReferenceNumber only carries a value when the resend was created by an Enterprise account through Create resend by SKU with a reference supplied. It is null for every other resend. To look a resend up on demand by this value, use Search resend by seller reference number.

VAS Update

Sent when a value-added service completes. category: rrliv / action: vasUpdated Each item in updateVasList:
URLs in imageUrlList are publicly fetchable, do not expire, and are safe to cache client-side. Persist them as-is. Empty state: imageUrlList: [] (some VAS types do not produce photos; the entry remains in updateVasList[] with an empty array).

Inventory Handling Complete

Sent when a handling instruction (dispose, resend, recall, etc.) is completed by the warehouse. category: rinv / action: completeInventoryHandling Key fields in returnInventory: handlingCode values: handlingStatusCode values:

Inventory Recalibrated

Sent when a warehouse updates the measured dimensions or weight of a return inventory. category: completeRecalibrate / action: completeRecalibrate Key fields in recalibrateSupplement:

Inventory Meta Updated

Sent when a warehouse or user adds or updates metadata on a return inventory. category: updateReturnInventoryMeta / action: updateReturnInventoryMeta The payload contains returnInventory with the same structure as Inventory Created, including the updated returnInventoryMetaList. metaType values:
  • usr — user-supplied meta
  • whs — warehouse-supplied meta

RMA Updated

Sent when a warehouse corrects an incorrect RMA assignment. category: notifyUserRmaSwapped / action: notifyUserRmaSwapped Key fields in payload:

SKU Updated

Sent when a seller updates the SKU of a return inventory. category: userUpdateReturnInventorySku / action: userUpdateReturnInventorySku The payload contains returnRequest and returnInventory. Key fields in returnRequest: The returnInventory object follows the same structure as Inventory Handling Complete, with the updated sku field.

Split Line Item

Sent when a VAS operation splits a parcel into multiple inventories. Contains the new line item and inventory records for each resulting parcel. category: lineItemVasReturnInventoryLineItem / action: splitLineItem Top-level payload fields: Each item in splitLineItemAndReturnInventoryList contains:

Warehouse Remarks Updated

Sent when a warehouse updates remarks on a return request. category: warehouseUpdateWarehouseRemarks / action: warehouseUpdateWarehouseRemarks The payload contains three objects:
  • returnRequest — the return request (same structure as SKU Updated → returnRequest)
  • shipment — the shipment record with full address details, dimensions, weight, cost, and customFieldMap
  • returnInventory — the affected inventory (same structure as Inventory Handling Complete), with the updated warehouseRemarks field

Buyer Return Label Generated

Sent when a buyer creates a return in the Branded Return portal and a label is generated.
Only applicable to customers integrated with the Return Helper Branded Return service.
category: buyerReturnRrLabel / action: buyerReturnLabelGenerated Check buyerReturn.labelRequestStatusCode for "success" or "fail". Key fields in buyerReturn: Each item in buyerReturnLineItemList:

Shopify Buyer Return Created

Sent when a buyer creates a return request via Shopify integration. category: shopifyBuyerCreateReturn / action: shopifyBuyerCreateReturn Key fields in shopifyReturn: Each item in shopifyReturnLineItemList:

Consolidate Shipping Cost Updated

Sent when the shipping cost of a consolidated shipping order is updated. category: consolidateShippingOrderShippingFeeUpdated / action: consolidateShippingOrderShippingFeeUpdated Key fields in order:

Consolidate Shipping All Packed

Sent when a warehouse has packed all inventories into boxes for a consolidated order. category: consolidateShippingOrderInventoryAllPacked / action: consolidateShippingOrderInventoryAllPacked Key fields in order: Each item in shipmentList: Each item in boxList: Each item in consolidateShippingInventoryList:
A box can hold RMA lines, FNSKU lines or both. A box with no RMA lines is still listed.

Consolidate Shipment Sent

Sent when a warehouse dispatches a consolidated shipment to a carrier. category: consolidateShippingShipmentSent / action: consolidateShippingShipmentSent Key fields in shipment:

Consolidate Shipping AWB Updated

Sent when the Air Waybill number for a consolidated shipment is updated. category: consolidateShippingShipmentShipped / action: consolidateShippingShipmentShipped The shipment object follows the same structure as Consolidate Shipment Sent, with the updated awb field. For RH_WAREHOUSE orders, this event is sent when the warehouse records the AWB. consolidateShippingShipmentStatus is IN_TRANSIT, and nothing is charged yet: the shipment is charged when it arrives, reported by Consolidate shipment arrived.

Consolidate Shipment Arrived

Sent once per shipment of an RH_WAREHOUSE order, when the shipment arrives at the Return Helper warehouse and its shipping fee is charged. category: consolidateShippingShipmentArrived / action: consolidateShippingShipmentArrived / version: 202609
Version 202609 is opt-in. Contact Return Helper customer service to receive it.
An order with three shipments holding items sends three events: 1/3, 2/3, 3/3. Fields in order: shippingMethod is not in version 202609. Each item in shipmentList: Each item in consolidateShippingInventoryList:

Consolidate Order Completed

Sent when every item in a consolidated order has shipped. For RH_WAREHOUSE orders, every shipment holding items must also have arrived; the event follows the order’s last Consolidate shipment arrived. category: consolidateShippingOrderCompleted / action: consolidateShippingOrderCompleted The order object follows the same structure as Consolidate Shipping All Packed, including the full shipmentList with boxList and inventory details.

Consolidate Order Cancelled

Sent when a warehouse force-cancels a consolidated shipping order. category: consolidateShippingOrderCancelled / action: consolidateShippingOrderCancelled Key fields in order:

Transaction Added

Sent once for every transaction posted to your account: charges, refunds, recharges, and top-ups.
apiTransaction.notes usually describes the action the transaction refers to, so you don’t need to resolve headId to understand a transaction.
category: transaction / action: addTransaction / version: 202610 Key fields in apiTransaction: Key fields in apiBalance:
balance is the value when this transaction was posted, not your current balance. Events can arrive out of order. For the current figure, call Get API account balance.

What headId refers to

headId is the ID of the record a transaction was booked against. Which ID it holds depends on transactionType. headId is always a string. A refund or recharge has the same headId as the charge it adjusts. Every transactionType, what it is for, and which ID its headId holds: ¹ rsl refunds a label that failed. headId is the labelId, or the returnRequestId when the failed label was a buyer return label.
  • Every transaction is sent; you can’t subscribe to individual types. Branch on transactionType and the sign of amount.
  • New transactionType values may be added. For a type you don’t recognise, still record amount and balance; don’t reject the event.
  • A shipment can have more than one label, so it can have more than one csl. Use Label result to map labelId to shipmentId.
  • createOn is when the transaction was posted; eventTime is when the notification was sent.