Endpoints
All paths start with https://api.xatern.com/v1. Send a key with each call. Every response is JSON with "ok": true on success. For errors see Errors and limits.
Full machine readable version: openapi.yaml.
| Method and path | Scope |
|---|---|
GET /health | None |
GET /me | Any key |
GET /catalog | catalog:read |
POST /orders | orders:create |
POST /orders/{number}/payment-proof | orders:create |
GET /orders/{number} | orders:create with token, or orders:read |
GET /orders/{number}/waybill | orders:create |
GET /orders | orders:read |
GET /customers | customers:read |
POST /subscribers | subscribers:create |
POST /leads | leads:create |
/webhooks (nine calls) | webhooks:manage |
Basics
GET/health
Scope: none. Check the API is up. No key needed.
{ "ok": true }GET/me
Scope: any key. Shows which company and scopes the key has.
curl https://api.xatern.com/v1/me -H "Authorization: Bearer KEY"{
"ok": true,
"company": "Example Shop Co",
"kind": "publishable",
"scopes": ["catalog:read", "orders:create", "subscribers:create", "leads:create"]
}Catalog
GET/catalog
Scope: catalog:read. Visible products with prices and stock. Cache the answer for about a minute.
curl https://api.xatern.com/v1/catalog -H "Authorization: Bearer KEY"{
"ok": true,
"catalog": {
"currency": "PHP",
"at": "2026-11-02T08:00:00Z",
"products": [
{
"name": "Rose Mist",
"category": "Mists",
"tags": ["new"],
"allow_preorder": false,
"discount": null,
"sizes": [
{ "sku": "SKU-ROSE-50", "label": "50 ml", "price": 450, "sale_price": null, "available": 24 }
]
}
]
}
}Orders
POST/orders
Scope: orders:create. Place an order. Send the same idempotency_key again and you get the first order back, never a second one. The reply has the order number and a secret token. Keep the token to show the shopper their order. See Orders for every field.
curl -X POST https://api.xatern.com/v1/orders -H "Authorization: Bearer KEY" \
-H "Content-Type: application/json" \
-d @order.json{
"order": {
"idempotency_key": "6b1c2f5e-0f1a-4b9e-9d6b-2f4e8c1a7d30",
"payment_method": "bank",
"contact": { "name": "Maria Santos", "phone": "09171234567", "email": "maria@example.test", "social": null },
"delivery": { "method": "jnt", "region": "NCR", "city": "Quezon City", "street": "12 Sample St", "landmark": "Blue gate" },
"lines": [
{ "product_id": "SKU-ROSE-50", "name": "Rose Mist 50 ml", "qty": 2, "unit_price": 450, "line_total": 900, "preorder": false }
],
"freight": 120,
"promo_code": null,
"promo_saving": 0,
"sale_saving": 0,
"total": 1020,
"due_today": 1020,
"balance": 0
}
}{
"ok": true,
"order": {
"number": "NT-482913",
"token": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6",
"status": "awaiting_payment",
"payment_status": "unpaid",
"total": 1020,
"due_today": 1020,
"balance": 1020
}
}POST/orders/{number}/payment-proof
Scope: orders:create. Upload the shopper payment screenshot as multipart form data. Fields: token, reference (up to 60 characters, from the receipt) and file (JPG, PNG, WebP or HEIC, up to 8 MB). The order number comes from the path.
curl -X POST https://api.xatern.com/v1/orders/NT-482913/payment-proof -H "Authorization: Bearer KEY" \
-F token=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 \
-F reference=GC123456789 \
-F file=@receipt.png{
"ok": true,
"order": { "number": "NT-482913", "status": "proof_received", "payment_status": "checking" }
}GET/orders/{number}
Scope: orders:create with ?token=, or orders:read without. With the order token, returns the shopper view: status, amounts, lines, payment results and timeline. With a secret key that has orders:read and no token, returns the full order object.
# Shopper view
curl "https://api.xatern.com/v1/orders/NT-482913?token=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" -H "Authorization: Bearer KEY"
# Staff view, secret key
curl https://api.xatern.com/v1/orders/NT-482913 -H "Authorization: Bearer KEY"{ "ok": true, "order": {
"number": "NT-482913",
"status": "awaiting_payment",
"payment_status": "unpaid",
"payment_method": "bank",
"placed_at": "2026-11-02T08:15:00Z",
"updated_at": "2026-11-02T08:15:00Z",
"currency": "PHP",
"total": 1020,
"due_today": 1020,
"paid": 0,
"balance": 1020,
"freight": 120,
"sale_saving": 0,
"promo_code": null,
"promo_saving": 0,
"contact": { "name": "Maria Santos", "phone": "+639171234567", "email": "maria@example.test", "social": null },
"delivery": { "method": "jnt", "region": "NCR", "city": "Quezon City", "street": "12 Sample St", "landmark": "Blue gate", "courier": null, "tracking_number": null },
"lines": [
{ "product_id": "SKU-ROSE-50", "name": "Rose Mist 50 ml", "qty": 2, "unit_price": 450, "line_total": 900, "preorder": false }
],
"payments": []
} }GET/orders/{number}/waybill
Scope: orders:create. A link to the waybill photo. It works for 10 minutes. url is null when there is no waybill yet. Needs ?token=.
curl "https://api.xatern.com/v1/orders/NT-482913/waybill?token=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6" -H "Authorization: Bearer KEY"{ "ok": true, "url": "https://files.example.test/waybill/NT-482913.jpg" }GET/orders
Scope: orders:read. List orders, newest first. Query: status, payment_status, since (placed after, ISO 8601), updated_since, limit (1 to 100, default 25), cursor. To get the next page, pass next_cursor as cursor. When next_cursor is null there are no more.
curl "https://api.xatern.com/v1/orders?payment_status=paid&limit=2" -H "Authorization: Bearer KEY"{
"ok": true,
"orders": [ { "number": "NT-482913", "status": "awaiting_payment", "payment_status": "unpaid", ... ],
"next_cursor": "eyJwIjoiMjAyNi0xMS0wMlQwODoxNTowMFoifQ"
}Customers
GET/customers
Scope: customers:read. List customers. Query: q (matches name, phone or email), limit, cursor.
curl "https://api.xatern.com/v1/customers?q=maria" -H "Authorization: Bearer KEY"{
"ok": true,
"customers": [
{
"id": "9e1c5a52-3a0c-4d0a-bb3e-1f6d2c7a8b40",
"name": "Maria Santos",
"phone": "+639171234567",
"email": "maria@example.test",
"social": null,
"orders_count": 3,
"total_spent": 3060,
"first_order_at": "2026-09-10T03:20:00Z",
"last_order_at": "2026-11-02T08:15:00Z",
"created_at": "2026-09-10T03:20:00Z"
}
],
"next_cursor": null
}Mailing list and enquiries
POST/subscribers
Scope: subscribers:create. Add an email to the mailing list. source is footer (default) or checkout. Send it only after the person ticked a consent box. The answer is the same whether the email was new or not.
curl -X POST https://api.xatern.com/v1/subscribers -H "Authorization: Bearer KEY" \
-H "Content-Type: application/json" \
-d '{"email":"maria@example.test","source":"footer"}'{ "ok": true, "subscribed": true }POST/leads
Scope: leads:create. Send a website enquiry into the CRM. The body is the same as the lead form accepts today: the contact details and the message.
curl -X POST https://api.xatern.com/v1/leads -H "Authorization: Bearer KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Jon Reyes","email":"jon@example.test","phone":"09181234567","message":"Do you do bulk orders?"}'{ "ok": true }Webhooks
All webhook calls need webhooks:manage, so use a secret key. Read Webhooks first. A company can have up to 10 endpoints. The URL must start with https, be at most 500 characters, and must not point to localhost or a private address.
An endpoint looks like this:
{
"id": "3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11",
"url": "https://hooks.example.test/xatern",
"events": ["order.created", "order.paid"],
"description": "Fulfilment system",
"enabled": true,
"disabled_at": null,
"disabled_reason": null,
"created_at": "2026-11-02T08:00:00Z"
}GET/webhooks
Scope: webhooks:manage. List your endpoints.
curl https://api.xatern.com/v1/webhooks -H "Authorization: Bearer KEY"{ "ok": true, "endpoints": [ ... ] }POST/webhooks
Scope: webhooks:manage. Make an endpoint. The reply includes its signing secret once. Save it now.
curl -X POST https://api.xatern.com/v1/webhooks -H "Authorization: Bearer KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.test/xatern","events":["order.created","order.paid"],"description":"Fulfilment system"}'{
"ok": true,
"endpoint": {
"id": "3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11",
"url": "https://hooks.example.test/xatern",
"events": ["order.created", "order.paid"],
"description": "Fulfilment system",
"enabled": true,
"disabled_at": null,
"disabled_reason": null,
"created_at": "2026-11-02T08:00:00Z"
},
"secret": "whsec_0123456789abcdef0123456789abcdef0123456789abcdef"
}PATCH/webhooks/{id}
Scope: webhooks:manage. Change url, events, description or enabled. Send only what changes. Setting enabled to true turns a disabled endpoint back on.
curl -X PATCH https://api.xatern.com/v1/webhooks/3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11 -H "Authorization: Bearer KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":false}'{ "ok": true, "endpoint": { ... } }DELETE/webhooks/{id}
Scope: webhooks:manage. Remove an endpoint.
curl -X DELETE https://api.xatern.com/v1/webhooks/3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11 -H "Authorization: Bearer KEY"{ "ok": true }POST/webhooks/{id}/test
Scope: webhooks:manage. Send a ping event to the endpoint now.
curl -X POST https://api.xatern.com/v1/webhooks/3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11/test -H "Authorization: Bearer KEY"{ "ok": true }GET/webhooks/{id}/deliveries
Scope: webhooks:manage. The delivery log for an endpoint, newest first. Query: limit.
curl "https://api.xatern.com/v1/webhooks/3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11/deliveries?limit=10" -H "Authorization: Bearer KEY"{
"ok": true,
"deliveries": [
{
"id": "c7d2f0a4-1b3e-4c55-9a8d-6e2f1b0a3c77",
"event_id": "evt_0123456789abcdef01234567",
"type": "order.paid",
"status": "succeeded",
"attempt": 1,
"last_status": 200,
"last_error": null,
"last_ms": 184,
"created_at": "2026-11-02T08:20:00Z",
"delivered_at": "2026-11-02T08:20:01Z"
}
]
}POST/webhooks/deliveries/{delivery_id}/resend
Scope: webhooks:manage. Send a delivery again.
curl -X POST https://api.xatern.com/v1/webhooks/deliveries/c7d2f0a4-1b3e-4c55-9a8d-6e2f1b0a3c77/resend -H "Authorization: Bearer KEY"{ "ok": true }POST/webhooks/{id}/roll-secret
Scope: webhooks:manage. Make a new signing secret. The old one stops working at once. The new secret is shown once.
curl -X POST https://api.xatern.com/v1/webhooks/3f0a6c1e-6c1d-4a6e-8a0c-0d5a7a1b9e11/roll-secret -H "Authorization: Bearer KEY"{ "ok": true, "secret": "whsec_fedcba9876543210fedcba9876543210fedcba9876543210" }Xatern One API v1. Example data on these pages is made up.