Connect your salon to your own platform. Product CRUD, order reads, real-time event push on every status change.
Every request needs an API key in the Authorization header. Generate keys from Dashboard → Settings → Integrations.
Authorization: Bearer apx_live_abc123… Content-Type: application/json
At creation time you pick which operations the key allows. Least-privilege — only the chosen scopes work.
products:readGET your products + variants.products:writePOST / PATCH / DELETE products.orders:readGET orders for your salon.orders:writePATCH status (ready / picked up / cancelled)./api/v1/productsproducts:readReturns up to 500 products for the key's salon, newest first. Each product includes its active variants.
curl https://api.apexloge.com/api/v1/products \ -H "Authorization: Bearer $APX_KEY"
/api/v1/productsproducts:writeCreates a product. variants is optional — without it the product is sold as a single SKU.
curl https://api.apexloge.com/api/v1/products \
-X POST \
-H "Authorization: Bearer $APX_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Argan oil shampoo",
"description": "Sulfate-free, 250ml",
"price": 24.90,
"category": "hair_care",
"subcategory": "shampoo",
"image_urls": ["https://cdn.example.com/argan-1.jpg"],
"stock_count": 30,
"variants": [
{ "axis_type": "size", "name": "250ml", "price_eur": 24.90, "stock_count": 30 },
{ "axis_type": "size", "name": "500ml", "price_eur": 39.90, "stock_count": 15 }
]
}'/api/v1/products/{id}products:readReturns one product by id (404 if it doesn't belong to the key's salon).
/api/v1/products/{id}products:writePartial update — only sent fields change.
curl https://api.apexloge.com/api/v1/products/abc-123 \
-X PATCH \
-H "Authorization: Bearer $APX_KEY" \
-H "Content-Type: application/json" \
-d '{ "stock_count": 12, "in_stock": true }'/api/v1/products/{id}products:writeSoft delete (is_active = false). Hard delete is not exposed via API to preserve order history.
/api/v1/ordersorders:readList up to 500 orders. Supports ?status= (paid|ready_for_pickup|picked_up|cancelled|expired|refunded) and ?limit=.
curl "https://api.apexloge.com/api/v1/orders?status=paid&limit=50" \ -H "Authorization: Bearer $APX_KEY"
/api/v1/orders/{id}orders:readSingle order with full variant snapshot columns.
/api/v1/orders/{id}orders:writeStatus change. cancelled requires cancel_reason — it lands in the customer's email.
# Mark ready for pickup
curl https://api.apexloge.com/api/v1/orders/abc-123 \
-X PATCH \
-H "Authorization: Bearer $APX_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "ready_for_pickup" }'
# Cancel + trigger refund
curl https://api.apexloge.com/api/v1/orders/abc-123 \
-X PATCH \
-H "Authorization: Bearer $APX_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "cancelled", "cancel_reason": "Out of stock" }'Allowed transitions: paid → ready_for_pickup → picked_up. From paid or ready_for_pickup you can also go to cancelled (triggers refund). picked_up is terminal.
Real-time push to your URL on every meaningful order change. Configure from Dashboard → Settings → Webhooks. Every request carries a signature header for verification:
X-Apexloge-Signature: t=1698765432,v1=<hex> X-Apexloge-Event: product_order.paid X-Apexloge-Delivery-Id: <uuid>
Verify by computing HMAC-SHA256 of the string {timestamp}.{raw_body} with your secret, then compare against v1.
# Node.js verification
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(rawBody, header, secret) {
const [tPart, v1Part] = header.split(",");
const t = tPart.split("=")[1];
const v1 = v1Part.split("=")[1];
const expected = createHmac("sha256", secret)
.update(`${t}.${rawBody}`)
.digest("hex");
return timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}400Bad Request — missing or invalid field.401Unauthorized — missing or revoked key.403Forbidden — the key lacks the required scope.404Not Found — resource doesn't exist or doesn't belong to the salon.429Too Many Requests — rate limit. Wait Retry-After seconds.500Internal Server Error — our fault. Retry or report it.