API · Integrations
Send your orders to TRAXO
Your website, Zapier, Make or n8n sends each order to a receiving URL. TRAXO creates the request, and your customer gets a link to say where to deliver.
Get started
First steps
- 1In TRAXO: Settings › Integrations › Add a source.
- 2Choose Zapier, Make, n8n or your website.
- 3Copy the receiving URL (with its token) and the signing secret. They are shown only once.
- 4Send a test (
"test": true), then open the Log.
WooCommerce store or FedaPay payment links: no code needed, see WooCommerce and FedaPay.
TRAXO format
Send an order
https://app.gettraxo.app/api/in/src_…/whk_…Header Content-Type: application/json · 512 KB at most
curl -X POST "$TRAXO_URL" \
-H "Content-Type: application/json" \
-d '{
"reference": "CMD-1042",
"customer": { "name": "Awa Dossou", "phone": "+229 01 95 12 34 56" },
"address": { "neighborhood": "Fidjrossè" },
"cash_to_collect": 15000
}'$TRAXO_URL: the full URL copied from TRAXO. Keep it in your server or automation tool, never in a public web page.
Full example
{
"reference": "CMD-1042",
"customer": {
"name": "Awa Dossou",
"phone": "+229 01 95 12 34 56",
"email": "[email protected]"
},
"address": {
"neighborhood": "Fidjrossè",
"landmark": "Behind the pharmacy"
},
"cash_to_collect": 15000,
"delivery_fee": 1500,
"items": [{ "name": "Pagne wax", "qty": 2, "unit_price": 6750 }],
"window": { "from": "2026-11-10T10:00:00+01:00", "to": "2026-11-10T13:00:00+01:00" },
"note": "Call before arriving"
}TRAXO format
Fields
Required: customer.phone or customer.email. Everything else is optional.
| Field | Type | Description |
|---|---|---|
reference | text, 80 chars | Your order number. A reference creates only one request; it is also used to cancel. Also: order_id, id. |
customer.name | text | Customer name. Also: customer_name. |
customer.phone | text | Phone number, preferably in international format (+229 …). Also: customer_phone. |
customer.phone_country | country code | Country of a number written without its calling code (BJ, TG, CI…). Default: your company’s country. |
customer.email | text | Email: TRAXO can send the customer’s link there. Also: customer_email. |
address.neighborhood | text | Neighbourhood or area. Also: address.zone, address.district or address as text. |
address.landmark | text | Landmark. |
cash_to_collect | integer, CFA francs | Amount to collect on delivery, fee included. |
delivery_fee | integer, CFA francs | Delivery fee, included in cash_to_collect. |
amount | integer, CFA francs | Amount of an order already paid: nothing to collect. |
items[] | list, 20 at most | Items: name, qty (1 to 999), unit_price. |
package_description | text | Parcel contents, if you do not send items. |
window | object or text | Preferred time slot: from and to in ISO 8601, or text. |
note | text | Delivery instruction, visible to the customer. |
test | boolean | true: test, no request created. |
cancel | boolean | true with reference: cancels the order. Also: "status": "cancelled". |
Amounts in whole CFA francs; 15000, "15 000 F" or "15000.00" are accepted.
TRAXO format
Authentication
| Method | URL and headers | Use |
|---|---|---|
| Token in the URL | https://app.gettraxo.app/api/in/src_…/whk_… | Simplest: Zapier, Make, n8n. |
| Token in a header | https://app.gettraxo.app/api/in/src_…X-Traxo-Token: whk_…or Authorization: Bearer whk_… | The URL no longer contains the token. |
| Signature | https://app.gettraxo.app/api/in/src_…headers webhook-* | Recommended for a website. Sign a request. |
With a token only, your team approves each request confirmed by the customer before a rider is sent (“To check”), and the link is not emailed unless you turn it on. A signed request is approved as soon as the customer confirms.
TRAXO format
Sign a request
Format Standard Webhooks: libraries for this format produce the same headers.
| Item | Value |
|---|---|
| Secret | whsec_…, shown once when the source is created. |
| Signed content | {webhook-id}.{webhook-timestamp}.{body} |
| Algorithm | HMAC-SHA256, key = secret without whsec_, base64-decoded; result in base64. |
webhook-id | Unique ID of the request. |
webhook-timestamp | Unix seconds; at most 5 minutes off. |
webhook-signature | v1,<signature> |
const crypto = require('crypto');
const secret = process.env.TRAXO_SECRET; // whsec_…
const url = process.env.TRAXO_SOURCE_URL; // https://app.gettraxo.app/api/in/src_…
const body = JSON.stringify(order);
const id = `msg_${crypto.randomUUID()}`;
const timestamp = Math.floor(Date.now() / 1000).toString();
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const signature = crypto.createHmac('sha256', key)
.update(`${id}.${timestamp}.${body}`)
.digest('base64');
await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'webhook-id': id,
'webhook-timestamp': timestamp,
'webhook-signature': `v1,${signature}`,
},
body,
});import base64, hashlib, hmac, json, os, time, uuid, urllib.request
secret = os.environ["TRAXO_SECRET"] # whsec_…
url = os.environ["TRAXO_SOURCE_URL"] # https://app.gettraxo.app/api/in/src_…
body = json.dumps(order).encode()
msg_id = f"msg_{uuid.uuid4().hex}"
timestamp = str(int(time.time()))
key = base64.b64decode(secret.removeprefix("whsec_"))
signed = f"{msg_id}.{timestamp}.".encode() + body
signature = base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
request = urllib.request.Request(url, data=body, method="POST", headers={
"Content-Type": "application/json",
"webhook-id": msg_id,
"webhook-timestamp": timestamp,
"webhook-signature": f"v1,{signature}",
})
urllib.request.urlopen(request)Sign exactly the bytes you send: do not reformat the JSON after signing.
TRAXO format
Responses
| Code | Meaning |
|---|---|
200{ "ok": true } | Notification received and verified. The outcome (request created, duplicate, ignored, set aside) shows in the Log. Do not resend. |
| 401 | Unknown source, invalid token or signature, or signature older than 5 minutes. |
| 413 | Body larger than 512 KB. |
| 429 | Too many requests: 120 per minute per source. Try again later. |
| 503 | Receiving is temporarily closed. Try again later. |
Only retry after a 429, a 503 or a network error, spacing out attempts. The same body sent twice is recorded once.
TRAXO format
Test
{
"test": true,
"reference": "ESSAI-1",
"customer": { "name": "Test", "phone": "+229 01 95 12 34 56" }
}Response 200. The Log shows “Ignored · Test received” with the name and phone it read. No request is created.
TRAXO format
Cancel
{ "reference": "CMD-1042", "cancel": true }As long as the delivery is not created, the request becomes “Declined”. After that, nothing changes automatically: the Log flags it for your team.
No code
WooCommerce
- 1TRAXO: Add a source › WooCommerce. Copy the delivery URL and the secret.
- 2WordPress: WooCommerce › Settings › Advanced › Webhooks › Add webhook.
- 3Name: TRAXO · Status: Active · Topic: Order created · copied URL and secret · API: WP REST API v3.
- 4Add a second identical webhook, topic Order updated.
| Order | In TRAXO |
|---|---|
| Processing (paid), or cash on delivery | Request created |
| Not paid yet, local pickup, older than the connection | Ignored |
| Cancelled, refunded, failed or deleted | Request declined |
Amounts: total minus shipping = items; shipping = delivery fee. Cash on delivery: the total is to be collected.
No code
FedaPay
- 1TRAXO: Add a source › FedaPay. Copy the webhook URL.
- 2FedaPay: Webhooks › Create a webhook with this URL and the
transaction.approvedevent. - 3Paste into TRAXO the secret key shown by FedaPay.
Each approved payment creates a request that is already paid: nothing to collect. Details read: the FedaPay customer (name, phone, email) and the quartier / neighborhood, repere / landmark metadata.
You also take payments with no delivery? Set a keyword in the source settings: only payments whose description contains it create a request.
No code
Excel or CSV import
Excel (.xlsx) or CSV, up to 2 MB and 500 rows; the first row holds the headers. Templates: Settings › Integrations › Import a file.
| Data | Recognized headers (examples) |
|---|---|
| Reference | Référence, Commande, N° commande, Order |
| Name | Nom, Nom du client, Client, Destinataire, Name |
| Phone or email | Téléphone, Tel, Numéro, Mobile, WhatsApp, Phone · E-mail, Mail, Courriel |
| Neighbourhood | Quartier, Zone, Adresse, Lieu, Ville, Commune, Address |
| Landmark | Repère, Point de repère, Indications, Précisions, Landmark |
| Note | Note, Commentaire, Remarque, Instructions, Message |
| Amount to collect | Montant à encaisser, Montant, Total, Prix, Cash |
| Delivery fee | Frais de livraison, Livraison, Frais, Shipping |
| Items | Articles, Produits, Colis, Contenu, Description, Items |
Case, accents and punctuation do not matter. Rows to fix are shown before importing; a reference already imported is not created again.
Monitor
Rules and limits
- A reference creates a single request; the same body sent twice is recorded once.
- Limit per source: 100 requests per 24 hours by default, adjustable from 1 to 1,000. Beyond it, the notification is set aside: raise the limit, then replay it from the Log.
- 10 sources at most per company; 120 requests per minute per source.
- The customer’s link stays valid for 7 days.
- Email to the customer: at most 2 per address per 24 hours, 300 per company. Otherwise the link waits in “To send”, with a WhatsApp button.
- Notification content kept 7 days; log kept 30 days.
Monitor
Log
Settings › Integrations › Log: the last 50 notifications, filtered by source and outcome.
| Outcome | Meaning |
|---|---|
| Request created | The request awaits the customer’s confirmation. |
| Already received | This reference already exists. |
| Ignored | Nothing to deliver: test, order not paid yet, local pickup, unreadable content… |
| Set aside | Limit reached, source paused, customer with no phone or email, suspended workspace. Can be replayed. |
| Failed | Processing error after several attempts. Can be replayed. |
Monitor
Security
- Secrets and tokens are shown only once, at creation. After that, neither the screen, the API nor the logs show them.
- Signing secrets are encrypted (AES-256-GCM); tokens are kept only as a hash (SHA-256).
- Lost or exposed? Source settings › New secret: the old one stops working at once.
- The company is always the source’s: no field you send can target another company.
- TRAXO never asks you for a secret by email or WhatsApp.