Send orders through the API channel
Send orders to a Hyperfox environment through its API channel when your system, such as a webshop or an ordering portal, should place orders with a supplier who works with Hyperfox: you post each order as JSON to the channel's Webhook URL, and the order appears in the supplier's Hyperfox for validation.
Before you begin
Ask the supplier's Hyperfox admin for:
- the Webhook URL of the API channel;
- the field names of their orders and order lines, and which fields are required;
- whether the channel checks a signature, and if so the token and the name of the signature header.
Send an order
Post the order as JSON to the Webhook URL:
POST <Webhook URL>
Content-Type: application/json
Accept: application/json
{
"data": {
"customer_reference": "PO-2026-0412",
"notes": "Deliver before noon"
},
"attachments": []
}
Send Accept: application/json on every request, so that errors come back as JSON. The request needs no other authentication than the optional signature. A 200 response means the supplier's Hyperfox has received the order.
Request body
| Key | Required | Value |
|---|---|---|
data | yes | One order as an object, or several orders as a list of objects. |
attachments | yes | A list of files, which may be empty. See Attach files. |
custom_order_id | no | A UUID that you choose as the ID of the inquiry in Hyperfox, for example to find it back later. |
Inside an order, each key is a field name of the supplier's orders. A key that is not a field name fills no field. A list field, such as the order lines, takes a list of objects with the field names of a line:
{
"data": {
"customer_reference": "PO-2026-0412",
"lines": [
{ "item_code": "A-100", "quantity": 4 },
{ "item_code": "B-220", "quantity": 1 }
]
},
"attachments": []
}
The field names in these examples are illustrations. Use the names the supplier gives you.
Send several orders in one request
Send data as a list. Every object in the list becomes its own order, and the supplier validates them together:
{
"data": [
{ "customer_reference": "PO-2026-0412" },
{ "customer_reference": "PO-2026-0413" }
],
"attachments": []
}
An empty list is refused.
Attach files
Each item in attachments holds either a url or a content, never both:
| Key | Value |
|---|---|
url | A web address of the file. Hyperfox downloads it. A file that cannot be downloaded is skipped without an error. |
content | The file, base64-encoded. |
filename | Optional, with content. Without it, Hyperfox generates a file name with an extension that matches the type. |
contentType | Optional, with content, for example application/pdf. Without it, Hyperfox detects the type from the content. |
"attachments": [
{ "url": "https://shop.example.com/orders/PO-2026-0412.pdf" },
{ "content": "JVBERi0xLjcK...", "filename": "PO-2026-0412.pdf", "contentType": "application/pdf" }
]
Sign the request
When the channel has a token, sign every request:
- Compute the HMAC-SHA256 of the exact request body, byte for byte as you send it, with the token as the key.
- Write the result as lowercase hexadecimal.
- Send it in the signature header, by default
X-HF-Signature, unless the supplier gave you another header name.
Sign the body after you serialise it, and send exactly those bytes: a change to the whitespace or the key order breaks the signature.
BODY='{"data":{"customer_reference":"PO-2026-0412"},"attachments":[]}'
SIGNATURE=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$TOKEN" | sed 's/^.* //')
curl -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-H "X-HF-Signature: $SIGNATURE" \
--data-raw "$BODY"
import hashlib, hmac, json, requests
body = json.dumps({"data": {"customer_reference": "PO-2026-0412"}, "attachments": []}).encode()
signature = hmac.new(token.encode(), body, hashlib.sha256).hexdigest()
requests.post(webhook_url, data=body, headers={
"Content-Type": "application/json",
"Accept": "application/json",
"X-HF-Signature": signature,
})
Responses
| Status | Body | Meaning |
|---|---|---|
200 | {"status": "processed", "message": "Webhook processed successfully", "api_order_id": "<id>"} | The order is received. |
401 | {"error": "Missing signature", ...} | The channel has a token and the signature header is missing. |
401 | {"error": "Invalid signature", ...} | The signature does not match the body. |
403 | {"error": "API channel inactive", ...} | The supplier has paused the channel. Hyperfox created no order; send the request again once the supplier has switched the channel back on. |
400 | {"error": "Unsupported channel type", ...} | The Webhook URL does not belong to an API channel. |
404 | The channel does not exist, for example because the supplier deleted it. | |
422 | Validation errors per key | A key is missing or invalid, or an order breaks a rule of the supplier's orders. |
422 | {"error": "Webhook processing failed", ...} | The request could not be processed. |
For a single order, a validation error names the field, such as customer_reference. For a list, it names the position and the field, such as data.1.customer_reference.
Things to know
- Every request creates new orders. Hyperfox does not recognise a request you send twice, so do not resend a request that received a
200. - Send each
custom_order_idonly once. - Whether an order waits for the supplier's validation or goes on to their ERP at once is the supplier's choice.