Developers

PLUX API & webhooks

Connect your own website, accounting or other tools to your business on PLUX. Read your price list, place and follow orders, and get told the moment something happens.

Keys

The business owner creates keys in Settings → Developers. A key is shown once; PLUX keeps only a fingerprint of it. Send it on every request:

curl https://plux.africa/api/v1/me \
  -H "Authorization: Bearer plux_live_..."
catalog:readProducts, prices and availability.
orders:readOrders with their items, status and totals.
orders:writePlace orders, priced by PLUX from your price list.
customers:readCustomer names, phone numbers and credit limits.
payments:readPayments and refunds recorded on orders.
stock:readStock items and quantity on hand per branch.
events:readThe event feed — the same events webhooks deliver.

Requests and answers

Endpoints

GET/api/v1/me
any key
The business, its branches and what this key may do.
GET/api/v1/products
catalog:read
The price list: products, prices, options and availability.
GET/api/v1/menu
catalog:read
A branch's menu for a storefront: categories in order, photos, options, availability, tax rate and what the business has switched on (ordering, online payment, coupons). Filter: branch_id.
POST/api/v1/orders/quote
orders:write
The exact price of a cart (tax, options, an optional coupon_code) without keeping anything: no order number, no customer, no coupon use.
GET/api/v1/orders
orders:read
Orders, newest first. Filters: status, branch_id, created_after, updated_after.
GET/api/v1/orders/{id}
orders:read
One order with its items, totals, stage and payment status.
POST/api/v1/orders
orders:write
Place an order. PLUX prices it from your price list. Optional coupon_code is applied after the order exists and never blocks it.
POST/api/v1/orders/{id}/payment
orders:write
Start online payment for an order your key placed; send the customer to checkout_url. return_url must be one of the business's allowed sites.
POST/api/v1/orders/{id}/payment/verify
orders:write
Ask the provider (through PLUX) what happened to a payment reference of that order.
GET/api/v1/customers
customers:read
Customers. Filter: phone.
GET/api/v1/payments
payments:read
Payments and refunds. Filters: order_id, created_after.
GET/api/v1/stock
stock:read
Stock items with quantity on hand per branch.
GET/api/v1/events
events:read
The event feed, oldest first. Filter: type. Continue with after.

Placing orders

Send product ids and quantities. PLUX works out the prices, charges and total from your price list — a price in the request is ignored. The order then follows the business's own rules, exactly like an online order: it is sent on automatically or waits for staff, and large orders can wait for a manager.

curl -X POST https://plux.africa/api/v1/orders \
  -H "Authorization: Bearer plux_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "idempotency_key": "web-order-20481",
    "customer": { "name": "Chidi Okafor", "phone": "08030001111" },
    "lines": [ { "product_id": "PRODUCT_ID", "quantity": 2, "option_ids": [] } ],
    "notes": "Call on arrival"
  }'

Webhooks

Add an https URL in Settings → Developers and choose the events you want. PLUX POSTs each event to it as JSON within a few seconds. Answer with any 2xx status within 8 seconds.

{
  "id": "evt_cmg1x...",
  "type": "order.ready",
  "created_at": "2026-10-02T11:04:31.512Z",
  "business_id": "...",
  "branch_id": "...",
  "entity": { "type": "order", "id": "..." },
  "order_id": "...",
  "payment_id": null,
  "channel": "WEB",
  "amount_kobo": null,
  "reason": null,
  "actor": { "kind": "staff", "role": "MANAGER" },
  "data": {}
}

Check the signature

Every delivery carries PLUX-Signature: t=<unix seconds>,v1=<hex>. Compute HMAC-SHA256 of `${t}.${rawBody}` with your signing secret and compare. Reject anything older than five minutes.

import { createHmac, timingSafeEqual } from "node:crypto";

function fromPlux(rawBody, header, secret) {
  const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const mine = createHmac("sha256", secret).update(t + "." + rawBody).digest("hex");
  return mine.length === v1.length && timingSafeEqual(Buffer.from(mine), Buffer.from(v1));
}

Events

Event names never change once published. New fields may be added to an event; ignore the ones you don't use.

Orders

  • order.created A new order or job was placed, from any channel.
  • order.confirmed Staff (or the customer, where approval is asked) confirmed the order.
  • order.items_added Items were added to an open order.
  • order.item_voided An item was removed from an order, with a reason.
  • order.custom_item_added A one-off item that isn't on the price list was sold.
  • order.price_overridden Staff sold an item at a price different from the list price.
  • order.discount_applied A discount was applied to or removed from an order.
  • order.handed_over The order was served, collected or delivered.
  • order.completed The order is finished and fully settled.
  • order.purged A PLUX Super Admin permanently removed a test, duplicate or corrupt order that never carried money (data.number, reason). The order's earlier events stay in the history.
  • order.marked_test The owner confirmed this order was a test/training transaction (reason). It stays on record in full but is left out of live sales, VAT, targets and vendor settlements.
  • order.unmarked_test A test classification was withdrawn (reason): the order counts as live business again.
  • order.cancelled The order was cancelled, with a reason.

Production

  • order.sent_to_production Items were sent to be prepared or produced.
  • order.stage_changed The work moved to another stage (data.from, data.to).
  • order.production_started Someone started working on the order.
  • order.items_prepared A batch of items was finished.
  • order.ready The order is ready for the customer.
  • order.assigned Someone was made responsible for the order (data.selfTaken, data.reassigned).
  • order.unassigned The order no longer has a person responsible.

Payments

  • payment.initiated An online payment was started.
  • payment.received Money was received and verified (amount_kobo).
  • payment.failed An online payment did not go through.
  • payment.refunded A payment was refunded (amount_kobo).
  • transfer.received A bank transfer arrived in a customer's account and was confirmed (data.appliedKobo, data.unappliedKobo). Each order it paid also gets payment.received.

Invoices

  • invoice.issued An invoice was issued for an order.
  • invoice.voided An invoice was voided; a new one may replace it.

Stock

  • stock.received Stock was received into a branch.
  • stock.used Stock was used.
  • stock.sold A sale took stock (data.items).
  • stock.transferred Stock moved from one branch to another.
  • stock.adjusted A count changed the quantity on hand (data.before, data.after).
  • stock.wasted Stock was written off as waste.

Money

  • expense.recorded An expense was recorded (amount_kobo).
  • expense.voided An expense was voided, with a reason.
  • cash_shift.cash_moved Cash was put into or taken out of a till during a shift.
  • credit.extended An order was put on a customer's account (data.customerId, data.dueAt).
  • cash_shift.opened A cash shift was opened with a float.
  • books.month_closed A month was closed: its figures can no longer change (data.month, amount_kobo = sales).
  • books.month_reopened A closed month was reopened, with a reason.
  • cash_shift.closed A cash shift was counted and closed (amount_kobo = variance).

Approvals

  • approval.requested An action is waiting for a second person.
  • approval.granted A request was approved and carried out.
  • approval.refused A request was refused, with a reason.

People

  • employee.clocked_in A staff member clocked in (data.method qr|office code|pin, data.distanceM, data.flagged when it waits for a manager's review).
  • employee.clocked_out A staff member clocked out (data.hours; data.auto when PLUX closed the shift at the business's maximum length).

Learning

  • recommendation.shown PLUX showed a suggestion learned from the business's own records (data.kind, data.confidence, data.suggestedKobo).
  • recommendation.accepted The person used the suggestion as it was.
  • recommendation.modified The person used the suggestion but changed it (data.chosenKobo vs data.suggestedKobo).
  • recommendation.rejected The person dismissed the suggestion.

Value

  • offer.created A coupon or other offer was created (data.code, data.discountType, data.valueBps / valueKobo, data.campaign).
  • offer.status_changed An offer was paused, reactivated or ended (data.from, data.to).
  • offer.granted A customer was given their own code for an offer by a person (data.customerId, data.source segment:away | manual, data.last4). Delivery, if any, is a separate message.sent.
  • coupon.applied A coupon was applied to an order (amount_kobo = the discount; data.code).
  • coupon.released A coupon came off an order (removed, or the order was cancelled; data.why).
  • gift_card.issued A gift card was issued (amount_kobo = its value — a liability until redeemed; data.funding PAID|COMPLIMENTARY, data.last4).
  • gift_card.redeemed A gift card paid part or all of an order (amount_kobo; data.remainingKobo). Not new money: the matching payment.received has method GIFT_CARD.
  • gift_card.credited A refund of a gift-card payment went back onto the card (amount_kobo; data.balanceKobo).
  • gift_card.voided A gift card was cancelled by the business (amount_kobo = the balance written off).

Partners

  • partner.thread_opened A business partner opened a comment, explanation request or review request (data.threadId, data.kind, data.subjectType). Message text is never included.
  • partner.message_added A message was added to a partner thread (data.threadId, data.side). Message text is never included.
  • partner.thread_status_changed A partner thread moved to another status (data.threadId, data.from, data.to).

Vendors

  • vendor.attribution_resolved A person decided who is responsible for a sold item that had no vendor assignment at its branch (data.vendorId, null = the host; reason).
  • vendor.history_posted Past sales were attributed to a vendor at a branch after review and approval (data.vendorId, data.branchId, data.basis VERIFIED|ESTIMATED, data.lines, data.chargedKobo, data.postingId).
  • vendor.history_reversed A historical attribution posting was reversed before any statement covered it (data.vendorId, data.branchId, data.postingId, reason).
  • vendor.status_changed A vendor was suspended, reactivated or made inactive (data.from, data.to, data.vendorId).
  • vendor_agreement.activated A vendor commission agreement was approved and took effect (data.vendorId, data.version, data.effectiveFrom, data.commissionBps). A change to the terms is always a new version.
  • vendor_agreement.ended A vendor agreement ended, with a reason (data.vendorId, data.version).
  • vendor_settlement.approved A vendor statement was approved (amount_kobo = net payable; data.vendorId, data.number, data.salesKobo, data.commissionKobo). Not a payment.
  • vendor_settlement.voided An approved vendor statement was voided, with a reason; its sales return to the next statement (data.vendorId, data.number).
  • vendor_settlement.settled A vendor period was settled: paid in full and every payment reconciled by a second person. It is never reopened (amount_kobo = net; data.vendorId, data.number).
  • vendor_payment.recorded The host paid a vendor against a statement (amount_kobo; data.vendorId, data.number, data.method). This is money out, not a customer payment.
  • vendor_payment.reconciled A payment to a vendor was matched to the bank or cash record (amount_kobo; data.vendorId, data.number).
  • vendor_payment.reversed A payment to a vendor was reversed, with a reason (amount_kobo; data.vendorId, data.number).

Purchasing

  • purchase_order.placed A purchase order was placed with a supplier (amount_kobo = expected value, when known).
  • purchase_order.received Stock arrived against a purchase order (data.complete, data.received).
  • purchase_order.cancelled A placed purchase order was cancelled, or closed with part of it not delivered.

Messages

  • message.sent A WhatsApp or SMS update was sent to a customer (channel, data.template).