Skip to main content

restaurant-default (v1)

The default JobSchema for restaurant orders. Supports two order types — single (one person's order) and group (multi-person order with embedded children).

Metadata​

FieldValue
namerestaurant-default
verticalrestaurant
version1
sourceapiV20

Single vs. group orders​

The orderType field on orderData selects between two structurally-different shapes. Pick the one that matches how the order arrived from your delivery partner / POS.

When to use each​

  • single — one customer, one bag. The classic delivery or pickup ticket: a single person ordered, possibly with multiple line_items. One ticket prints.
  • group — one host/coordinator with N individual sub-orders attached. Common for corporate lunches, office orders, group catering, and any flow where each person picks their own items but everything is paid for together. The customer at the root of orderData is the host (e.g. the company or the person organizing); the per-person details live on each children[] entry.

Where line items live​

singlegroup
Root line_itemsRequired — every item on the orderNot used (omitted)
Root customerThe customer placing the orderThe host / organizer (often a company)
Each children[] entryn/aHas its own customer, line_items, and per-person totals

A group order with three sub-orders is one logical Job — one POST, one Job record, one printed receipt (or N receipts when cut_between_children is set; see below).

Totals semantics​

  • single — totals is the whole order. Required: subtotal, tax, total, quantity.
  • group — root totals is the parent summary across all children. Required: subtotal, quantity (only). tax and total are optional at the root because the host typically settles up later. Each children[] entry also has a totals block with its own subtotal and quantity for that one person.

Required-field differences at a glance​

Order typeRequired top-level orderData fields
singleorderType, id, customer, restaurant, line_items, totals, fulfillment, payment
grouporderType, id, customer, restaurant, totals, fulfillment, payment, children

Both shapes additionally require delivery_service + delivery_address + delivery_instructions when fulfillment.method === "delivery". Pickup and dine-in orders omit those.

webhook_url + webhook_method are required on every job regardless of orderType — they live at the envelope of the POST body (alongside printerId), not inside orderData.

The same template renders both shapes — the layout adapts:

  • single — one continuous receipt: header, customer block, line items, totals, footer.
  • group — header + parent totals at the top, then each child rendered as its own labeled section (name, items, per-person subtotal). The default is one continuous receipt; setting cut_between_children: true at the root of orderData instructs the printer to emit a PARTIAL_CUT between every child sub-order, so each person's items come off as their own ticket. cut_between_children is silently ignored on single orders.

Lifecycle differences​

  • A group order is one Job regardless of child count. State transitions, webhooks, scheduled-print timing, copies, and cancellation all apply at the parent level.
  • The personName attribute on the Job record is the host's name for groups and the customer's name for singles.

Shape​

orderData: object
├── orderType: "single" | "group" # required
├── id: string # required (external order identifier, minLength 1)
├── customer: { name, phone?, company? } # required
├── restaurant: { name?, phone?, address? } # required
├── delivery_service: { name?, support_phone?, support_instructions? }
│ # required when fulfillment.method="delivery"
├── line_items: [LineItem, ...] # required for single
├── totals: {
│ subtotal, tax?, tip?, delivery_fee?,
│ discount?, total?, quantity
│ } # required
├── delivery_address: {
│ street, city, state, zip_code,
│ country?, apartment?, floor?,
│ entrance?, on_arrival?, subdivision?
│ } # required when fulfillment.method="delivery"
├── delivery_instructions: string # required when fulfillment.method="delivery"
├── fulfillment: {
│ method: "pickup" | "delivery" | "dine-in",
│ promised_time?, is_future_order?, pickup_type?
│ }
├── payment: { method?, is_prepaid? }
├── special_instructions: string
├── confirmation_code: string
├── cut_between_children: boolean # group orders only (default false)
└── children: [Child, ...] # required for group

LineItem​

{
quantity: integer (≥ 0), # required
name: string, # required
price: number, # required
subtotal: number, # required
category?: string,
comment?: string,
options?: [{ name, quantity, price, comment? }]
}

Child (group orders only)​

Each child is a self-contained sub-order for one person.

{
customer: { name, phone? }, # required
line_items: [LineItem, ...], # required
totals: {
subtotal: number, # required
quantity: integer # required
}
}

Required fields by order type​

Base required fields (all orders):

Order typeRequired top-level orderData fields
singleorderType, id, customer, restaurant, line_items, totals, fulfillment, payment
grouporderType, id, customer, restaurant, totals, fulfillment, payment, children

Note: webhook_url and webhook_method are also required on every job creation, but live at the root of the POST body (alongside printerId), not inside orderData. See the Create job page.

Additional fields required when fulfillment.method is delivery (both single and group):

  • delivery_service
  • delivery_address
  • delivery_instructions

Pickup (fulfillment.method: "pickup") and dine-in (fulfillment.method: "dine-in") orders may omit all three.

For single orders, totals must include subtotal, tax, total, and quantity. For group orders (and each embedded child), totals requires only subtotal and quantity.

Date-time fields (e.g. fulfillment.promised_time) must be ISO 8601 timestamps that include a timezone (2026-04-15T18:15:00Z or 2026-04-15T13:15:00-05:00).

Per-child cuts (group orders)​

cut_between_children is optional. When set to true on a group order, each child is printed as its own ticket — useful when the individual orders are being handed out separately. Ignored on single orders.

Printed output​

The template associated with restaurant-default (v1) produces a thermal-receipt layout from orderData. The same template renders both single and group orders. The previews below show the template applied to the example payloads in the next section.

Single order​

Single order printed output

Rendered from the single order example below.

Group order​

Group order printed output

Rendered from the group order example below.

Example — single order​

{
"orderType": "single",
"id": "38086725",
"restaurant": {
"name": "MD Pizza Factory",
"phone": "800-709-7191",
"address": "4706 N Midkiff Rd, Midland, TX 79705"
},
"delivery_service": {
"name": "delivery.com",
"support_phone": "800-709-7191"
},
"customer": {
"name": "John Smith",
"phone": "432-894-7532"
},
"line_items": [
{
"quantity": 1,
"name": "Pepperoni Pizza",
"category": "pizza",
"price": 18.00,
"subtotal": 18.00,
"options": [
{ "name": "Extra Cheese", "quantity": 1, "price": 2.00 }
]
},
{
"quantity": 2,
"name": "Garlic Knots",
"category": "appetizer",
"price": 3.50,
"subtotal": 7.00,
"comment": "Extra garlic butter on the side"
}
],
"totals": {
"subtotal": 27.00,
"tax": 2.23,
"tip": 5.00,
"delivery_fee": 3.99,
"discount": 0,
"total": 38.22,
"quantity": 3
},
"delivery_address": {
"street": "123 Main St",
"city": "Springfield",
"state": "PA",
"zip_code": "19000",
"apartment": "4B",
"entrance": "Ring the doorbell twice"
},
"delivery_instructions": "Leave at door, no contact",
"fulfillment": {
"method": "delivery",
"promised_time": "2026-04-15T18:15:00Z",
"is_future_order": false
},
"payment": {
"method": "Pre-Paid",
"is_prepaid": true
}
}

Example — group order​

{
"orderType": "group",
"id": "38086901",
"restaurant": {
"name": "MD Pizza Factory",
"phone": "800-709-7191",
"address": "4706 N Midkiff Rd, Midland, TX 79705"
},
"delivery_service": {
"name": "delivery.com",
"support_phone": "800-709-7191"
},
"customer": {
"name": "Acme Corp",
"phone": "432-555-0199",
"company": "Acme Corp"
},
"totals": {
"subtotal": 62.00,
"tax": 5.12,
"tip": 10.00,
"delivery_fee": 3.99,
"discount": 0,
"total": 81.11,
"quantity": 6
},
"delivery_address": {
"street": "500 Corporate Way",
"city": "Springfield",
"state": "PA",
"zip_code": "19000",
"floor": "12",
"on_arrival": "Check in with reception on 12th floor"
},
"delivery_instructions": "Deliver to front desk, ask for Jane",
"fulfillment": {
"method": "delivery",
"promised_time": "2026-04-15T12:30:00Z",
"is_future_order": true
},
"payment": {
"method": "Pre-Paid",
"is_prepaid": true
},
"children": [
{
"customer": {
"name": "Jane Doe",
"phone": "432-555-0111"
},
"line_items": [
{
"quantity": 1,
"name": "Margherita Pizza",
"category": "pizza",
"price": 16.00,
"subtotal": 16.00,
"options": [
{ "name": "Gluten-Free Crust", "quantity": 1, "price": 3.00 }
]
},
{
"quantity": 1,
"name": "Caesar Salad",
"category": "salad",
"price": 9.00,
"subtotal": 9.00,
"comment": "Dressing on the side"
}
],
"totals": {
"subtotal": 28.00,
"quantity": 2
}
},
{
"customer": {
"name": "Bob Lee",
"phone": "432-555-0122"
},
"line_items": [
{
"quantity": 1,
"name": "Pepperoni Pizza",
"category": "pizza",
"price": 18.00,
"subtotal": 18.00,
"options": [
{ "name": "Extra Cheese", "quantity": 1, "price": 2.00 }
]
},
{
"quantity": 2,
"name": "Garlic Knots",
"category": "appetizer",
"price": 3.50,
"subtotal": 7.00
}
],
"totals": {
"subtotal": 27.00,
"quantity": 3
}
},
{
"customer": {
"name": "Priya Patel",
"phone": "432-555-0133"
},
"line_items": [
{
"quantity": 1,
"name": "Veggie Calzone",
"category": "calzone",
"price": 12.00,
"subtotal": 12.00,
"comment": "No olives please"
}
],
"totals": {
"subtotal": 12.00,
"quantity": 1
}
}
]
}