Developers
Restaurant order integration
Connect your point-of-sale or kitchen system to Sela so orders taken by the assistant reach you by webhook and you approve them through the API.
Last updated: 2026-09-29
If a restaurant takes orders through the Sela assistant, your own system (a point-of-sale, kitchen display or delivery app) can receive each order, decide whether to accept it, and report when it arrives. The integration uses two pieces described elsewhere: webhooks to hear about orders, and the REST API to act on them.
For the business-owner side of orders, see Orders and payment links.
How the flow works#
- A customer chats with the assistant and confirms an order.
- Sela records the order as pending approval and sends an
order.createdwebhook to your endpoint. The dashboard also notifies your team. - Your system verifies the signature, deduplicates on
X-Sela-Delivery, and stores the orderidand details. - Your system calls
POST /api/v1/orders/actionwithapproveorreject, optionally attaching your ownexternal_order_id. - Sela messages the customer about the decision and sends
order.approvedororder.rejected. - When the food is delivered or handed over, call the same endpoint with
mark_arrived. Sela sendsorder.arrived. - At any time, reconcile with
GET /api/v1/orders?status=pending.
Staff can also approve or reject in the dashboard. Both paths update the same order and send the same webhook events.
Prerequisites#
- The Scale or Enterprise plan and API and webhook access enabled for your workspace (contact support if needed). See Plans and billing.
- The assistant's food-order tool enabled for the workspace, so orders exist to integrate.
- An admin to create the API key and webhook endpoint.
Step 1: create an API key#
In API Keys, create a key with the orders:read and orders:write permissions. Copy it once and store it on your server as SELA_API_KEY.
Step 2: create a webhook endpoint#
In Integrations, Webhooks, add your HTTPS receiver URL and subscribe to order.*, or to the four events individually: order.created, order.approved, order.rejected and order.arrived. Save the whsec_ signing secret shown once.
Step 3: receive and verify orders#
Verify the X-Sela-Signature header as shown in Webhooks, then read the order from data.
import express from "express";
import { verify } from "./verify.mjs"; // see the Webhooks page
const app = express();
const seen = new Set(); // use a database in production
app.post("/sela/orders", express.raw({ type: "application/json" }), async (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(raw, req.headers, process.env.SELA_WEBHOOK_SECRET)) return res.sendStatus(401);
const deliveryId = req.headers["x-sela-delivery"];
if (seen.has(deliveryId)) return res.sendStatus(200);
seen.add(deliveryId);
const { event, data } = JSON.parse(raw);
if (event === "order.created") {
await saveOrderToKitchenSystem(data); // data.id, data.orderSummary, data.customer ...
}
res.sendStatus(200);
});
app.listen(8080);The order object has these fields:
| Field | Meaning |
|---|---|
id | Sela's order id. Use it for every API call. |
status | pending, approved, arrived, rejected, failed or cancelled. |
createdAt, updatedAt | Unix milliseconds. |
conversationId | The chat the order came from, or null. |
customer | id, name, phone, email, each possibly null. |
fulfillment | type (delivery, pickup or null) and address. |
orderSummary | A text summary of the items. |
specialInstructions | Customer notes, or null. |
details | The raw order data collected by the assistant. |
externalOrderId | Your reference, once you set it. |
Step 4: approve, reject or complete#
# Approve, and attach your own order number
curl -X POST "https://jovial-bison-730.convex.site/api/v1/orders/action" \
-H "Authorization: Bearer $SELA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"kx7...","action":"approve","external_order_id":"menu-123"}'
# Reject
curl -X POST "https://jovial-bison-730.convex.site/api/v1/orders/action" \
-H "Authorization: Bearer $SELA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"kx7...","action":"reject"}'
# Mark as arrived (only after approve)
curl -X POST "https://jovial-bison-730.convex.site/api/v1/orders/action" \
-H "Authorization: Bearer $SELA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"order_id":"kx7...","action":"mark_arrived"}'| Action | Valid from | Result |
|---|---|---|
approve | pending | Status approved, customer is told, order.approved is sent. |
reject | pending | Status rejected, customer is told, order.rejected is sent. |
mark_arrived | approved | Status arrived, order.arrived is sent. |
Calling mark_arrived on a pending order returns 409 invalid_state. Repeating an action that already happened returns 200 with changed: false. The full transition table is in REST API.
Step 5: reconcile#
Webhooks can be missed if your server is down for longer than the retry window. Run a periodic job that lists pending orders and processes any you do not know:
curl "https://jovial-bison-730.convex.site/api/v1/orders?status=pending&limit=100" \
-H "Authorization: Bearer $SELA_API_KEY"Follow meta.next_cursor until meta.is_done is true. The status filter is applied per page, so a page may be short or empty before the last one.
Avoid echo loops#
When you approve an order through the API, Sela sends an order.approved webhook to your own endpoint too. Ignore events for state changes you just made, for example by comparing data.externalOrderId with the one you set, or by checking your own records. A no-op call (changed: false) sends no event.
Test the integration#
- Create the key and a webhook endpoint in the
testenvironment pointing at a public tunnel to your receiver. - Place an order through the assistant, for example from the web widget.
- Confirm
order.createdarrives with a valid signature. - Approve it with the curl command above and confirm
order.approvedarrives and the customer sees the message. - Mark it arrived and confirm the final event.
If you are building a receiver from scratch, a quick local option is a small server that prints headers and the raw body, then check the signature with the code in Webhooks.
Troubleshooting#
| Symptom | Cause |
|---|---|
No order.created arrives | The endpoint is disabled (after 10 exhausted deliveries), not subscribed to order.*, or the plan lacks webhooks. Check the delivery log. |
401 unauthorized from the API | Key revoked, expired or copied wrongly, or the plan no longer includes API access. |
403 forbidden on orders/action | The key lacks orders:write. |
409 invalid_state | The order is not in a state that allows the action, for example mark_arrived before approve. |
404 not_found | The order_id is wrong or belongs to another workspace. |
| Duplicate events | Delivery is at least once. Deduplicate on X-Sela-Delivery. |
Can't find what you need? Contact support.