OrderFlow MCP

A Model Context Protocol server that lets an AI chatbot connect to real restaurants — browse live menus, place and manage orders through the restaurant's point-of-sale.

Transport: Streamable HTTP Protocol: MCP Auth: OAuth 2.1 · Bearer API key

Overview

OrderFlow MCP exposes a set of tools an AI assistant can call to discover onboarded restaurants, read their menus and ordering policies, and create/confirm/track customer orders. All pricing is computed server-side from live inventory, and discounts/deals are only honored when the store's configuration authorizes them — the assistant cannot invent prices or unauthorized promotions.

Connection

Base endpoint
https://mcp.zappd.ai/mcp
Transport
MCP Streamable HTTP
Methods
POST /mcp (JSON-RPC messages) · GET /mcp (SSE server→client stream) · DELETE /mcp (tear down session)
Session
An initialize request opens a session; the server returns an Mcp-Session-Id header that must be sent on every subsequent request.
Health check
GET /mcp/health (unauthenticated) → { "ok": true }

Authentication

Every request to /mcp carries a bearer token. Two methods are accepted:

OAuth 2.1 (consumer login)

Interactive clients such as ChatGPT use OAuth 2.1. Each end-user signs in (via Firebase Auth) and receives an account-scoped access token; tools then only expose that user's own orders. This server is both the Authorization Server and the Resource Server, so discovery is automatic:

Static API key (programmatic)

Server-to-server consumers may instead send a pre-issued key:

Authorization: Bearer zmk_your_key_here

Scopes

Each key carries scopes; a tool call missing its required scope returns INSUFFICIENT_SCOPE.

ScopeGrants
stores:readList restaurants
menu:readRead a store's menu
config:readRead a store's AI config (hours, policies) and menu ontology
orders:writeCreate, update, confirm, and cancel orders
orders:self:readRead orders created by this key
orders:self:subscribeSubscribe to live updates for this key's orders

Tools

Discovery

ToolScopeInputDescription
list_all_active_storesstores:read—List active, non-archived stores that have a configured POS (browsable and orderable).
list_all_storesstores:read—List every store in the directory with active/archived/posType flags. Informational only.

Menu & configuration

ToolScopeInputDescription
get_menumenu:readstoreIdRead a store's menu: items, prices, and modifier groups (with minRequired/maxAllowed/singleSelect) plus combination rules to honor when ordering. Ask the customer to choose for every group with minRequired > 0 before ordering, using combinationRules.modGroupQuestions wording when present.
get_ai_configconfig:readstoreIdCustomer-facing AI config: interaction rules, hours, promises, and authorized policies (refunds/discounts/deals/guarantees).
get_ontologyconfig:readstoreIdMenu ontology mapping customer wording → canonical items/modifiers (itemAliases, modAliases). Consult it to resolve loose or alias wording to catalog IDs when a mention doesn't cleanly match get_menu.

Ordering

ToolScopeInputDescription
create_orderorders:writeidempotencyKey, storeId, items?, order metadataCreate a virtual order owned by this key. Items use catalog IDs from get_menu; prices are set server-side. Retries with the same idempotencyKey reuse the original order.
update_orderorders:writeidempotencyKey, orderId, items?, order metadataUpdate a virtual order before confirmation. items replaces the whole cart (empty array clears it); omitted fields are unchanged.
confirm_orderorders:writeidempotencyKey, orderId, customerName?, phoneNumber?Finalize a non-empty order and hand it to the store; notifies the cashier to set a pickup time and confirm. Does not auto-submit to the POS.
cancel_orderorders:writeidempotencyKey, orderIdSoft-cancel a virtual order. Refuses orders already submitted (or being submitted) to the POS.

Order tracking

ToolScopeInputDescription
get_orderorders:self:readorderIdRead one of your own orders.
list_my_ordersorders:self:read—List your orders (those created by this connection), newest first.
await_order_updatesorders:self:readorderId, afterCreatedAt?Long-poll (up to ~2 min) for the restaurant's next lifecycle update on a confirmed order, then relay it to the customer. Call once after confirm_order; pass afterCreatedAt = a prior call's latestCreatedAt to continue. Returns pending: true if nothing new arrived — a polling alternative to the SSE subscription for clients (e.g. ChatGPT) that cannot receive server pushes.
subscribe_order_updatesorders:self:subscribeorderId?Subscribe over the SSE stream to live order updates. With orderId, watches that order; omitted, watches your active orders. Emits notifications/zappd/order_update (status/pickup) and, for a single order, notifications/zappd/order_message (the restaurant's lifecycle messages). Requires the SSE connection.
unsubscribe_order_updatesorders:self:subscribesubscriptionIdCancel an active subscription.

Order item & metadata shape

Each entry in items:

{
  "itemId": "catalog-item-id",       // from get_menu
  "quantity": 1,                      // integer >= 1
  "modifierIds": ["mod-id-1", "..."], // optional; validated against menu rules
  "note": "no onions"                // optional
}

Order metadata (optional, on create_order/update_order): customerName, phoneNumber, pickupTime, and the policy-claim arrays discounts, deals, refunds, guarantees. Policy claims are only accepted if authorized in get_ai_config.policies; anything else is rejected with POLICY_NOT_AUTHORIZED.

Error codes

Tool errors ride inside a successful JSON-RPC response with an isError flag and a structured code. Common codes:

CodeMeaning
INSUFFICIENT_SCOPEKey lacks the scope the tool requires.
STORE_NOT_ONBOARDEDStore is inactive, archived, or has no configured POS.
ORDER_NOT_FOUNDNo order with that id owned by this key.
ORDER_CART_EMPTYTried to confirm an order with no line items.
INVALID_ITEM_ID / INVALID_MODIFIER_IDItem or modifier not in the store's catalog.
MODIFIER_GROUP_MIN / MODIFIER_GROUP_MAXModifier selection violates the group's min/max.
REJECTED_MODIFIER_COMBINATIONModifier is not allowed on that item per ontology rules.
POLICY_NOT_AUTHORIZEDClaimed a discount/deal/refund/guarantee the store does not authorize.
ORDER_ALREADY_IN_POS / POS_MATERIALIZATION_IN_PROGRESS / ORDER_CANCELLEDOrder can no longer be edited/cancelled.

Typical flow

  1. list_all_active_stores → pick a storeId.
  2. get_menu (and optionally get_ai_config / get_ontology) to build the cart. Use get_ontology to resolve loose/alias wording, and ask the customer for any required modifier choices (groups with minRequired > 0, using modGroupQuestions wording) before create_order.
  3. create_order with items → update_order to adjust.
  4. confirm_order with customer name/phone → the store is notified to set a pickup time.
  5. get_order / list_my_orders, or subscribe_order_updates for live status.

Access requirements