Create/Update Orders
Orders
Note: This replaces the
/receiptsendpoint./receiptsis being deprecated in favor of the
Orders API described below, along with its supporting Revenue Center, Sales Category, and Dining
Option endpoints.
This section focuses on:
- Structure of
PUTrequests to the/ordersendpoint, including inline payments and line items - Structure of
PUTrequests to the reference-data endpoints (/revenue_centers,
/sales_categories,/dining_options) that orders and line items refer to - Recommended ordering for sending reference data ahead of the orders that use it, and how
unresolved references are handled with placeholders - Historical order backfill expectations
- How
PUTbehaves as a full replacement of an order's payments and line items - Voiding, deleting, and refunding orders and line items (including partial refunds/voids)
- Reading orders and reference data back via the
GETendpoints
Sending sales data to 7shifts is handled through a PUT request to the 7shifts /orders endpoint.
An order is an aggregate: its payments and line items are submitted inline as part of the same
request, and it can reference reusable revenue center, sales category, and dining option records
by ID.
Request URL
curl --request PUT --url 'https://api.7shifts.com/api/v2/company/12345/location/7890/orders/order-123'An example PUT request to the /orders endpoint to create or update an order looks as follows:
Request body
{
"revenue_center_external_id": "bar",
"dining_option_external_id": "dine-in",
"order_date": "2023-12-31T15:45:23-06:00",
"net_total": 4399,
"gross_total": 4750,
"status": "closed",
"payments": [],
"line_items": []
}Here's an explanation of each of the required fields in the request body:
| Parameter | Type | Description |
|---|---|---|
location_id | integer | 7shifts Location ID (also present in the request URL) |
order_date | datetime | Timestamp the order was placed. UTC in ISO8601 format |
net_total | integer | Net total of the order, pre-tax, post-discounts, pre-tips. In cents |
gross_total | integer | Gross total of the order. In cents |
status | string | Order status. Must be one of the following: open closed voided deleted refund |
payments | array | Payment line items on the order. Required, may be an empty array |
line_items | array | Order line items. Required, may be an empty array |
There is no separate order_id field to invent — the external_id used to create and later
update the order is passed as part of the request URL itself: /orders/{external_id}.
The following fields are optional:
| Parameter | Type | Description |
|---|---|---|
revenue_center_external_id | string | ID of the revenue center this order belongs to |
dining_option_external_id | string | ID of the dining option this order belongs to |
external_user_id | string | ID of the user in your system who created the order |
closed_date | datetime | Timestamp the order was closed |
external_business_date | datetime | POS-provided business date, for daily sales roll-ups that can span past midnight |
tax | integer | Tax total. In cents |
discounts | integer | Discount total. In cents |
fees | integer | Fee total. In cents |
non_revenue_total | integer | Non-revenue total. In cents |
item_count | float | Number of items on the order |
external_created | datetime | Your system's creation timestamp for the order |
external_modified | datetime | Your system's last-modified timestamp for the order |
7shifts does not validate or recompute totals for you.
net_total/gross_totalon the order,
and the totals on eachpayment/line_item, are stored and reported exactly as sent. If your
line items don't add up to the order'snet_total/gross_total, or a payment total doesn't
match what was actually charged, 7shifts has no way to detect or correct that — it's on the
caller's integration to send consistent, correct numbers.
Send orders to 7shifts
Orders need to be created in 7shifts as soon as they are opened in your system, and updated as
they change, in order to ensure consistency between the two platforms.
As soon as an order is opened in your system, make a PUT request to 7shifts' /orders/{external_id}
endpoint following the request structure described above. As the order changes (new payments,
updated totals, closing), send another PUT request with the order's full current state — see
Order PUT is a full replacement below.
Reference-data-first ordering
Revenue centers, sales categories, and dining options are reusable records referenced by orders and
line items, rather than repeated inline on every order. Send them ahead of the orders that use them:
PUTrevenue centers for the location.PUTsales categories for the location.PUTdining options for the location.PUTorders, referencing the above byrevenue_center_external_id,dining_option_external_id,
and (on each line item)sales_category_external_ids.
Request URL
curl --request PUT --url 'https://api.7shifts.com/api/v2/company/12345/location/7890/revenue_centers/bar'Request body
{
"name": "Bar"
}| Parameter | Type | Description |
|---|---|---|
name | string | Display name of the revenue center |
Sales categories and dining options follow the same shape (name required), at
/sales_categories/{external_id} and /dining_options/{external_id} respectively. Dining options
additionally accept an optional type field (e.g. dine_in, delivery, take_out).
Placeholder behavior for unresolved references
Pushing reference data first keeps names accurate from the start, but an order is never rejected
just because a revenue center, sales category, or dining option external_id hasn't been pushed
yet. Instead:
- 7shifts creates a placeholder record and links the order (or line item) to it.
- The
PUT /orders/{external_id}response includes awarningsarray describing what was
unresolved:
{
"warnings": [
{
"code": "unresolved_revenue_center",
"message": "Revenue center has not been pushed yet; a placeholder was created.",
"external_id": "bar"
}
]
}Once you PUT the real reference data using the same external_id, the placeholder is updated in
place — no relinking is required on your end, and any orders already pointing at it pick up the
correct name automatically.
Historical order backfill
At least 90 days of historical sales history should be sent when an integration/location is first
connected, so 7shifts' forecasting has enough data to work with.
- Use the same
PUT /orders/{external_id}endpoint for backfill as for live orders — there is no
separate batch/bulk import endpoint. - Set
order_date(andexternal_business_date, if used) to the historical timestamps, not the
time the backfill request is actually sent. - Send your oldest historical order first. The first order sent for a location establishes that
location's cutover point for this API, and orders dated earlier than it may not be readable back
through it. - Throttle requests (no more than 10 requests per second per token) rather than bursting the full
90 days at once.
Order PUT is a full replacement
A PUT to /orders/{external_id} is not a patch. payments and line_items are required on
every request (they may be empty arrays) and represent the complete, current state of the order's
children — whatever you send replaces whatever was previously stored. If a payment or line item
existed on a prior write and is left out of the current one, it's removed from the order.
Always send the order's full current set of payments and line items on every update, not just the
ones that changed since the last request.
Child external IDs are optional
Payments and line items don't need their own external_id. If you have a stable ID for one in
your own system, include it (useful for reconciliation and so it round-trips on reads); if not,
omit it — 7shifts will still store it correctly.
Either way, nesting a payment or line item inside an order's payments/line_items array is what
associates it with that order — you never need to set an order_external_id on a child when
writing it. (order_external_id is populated for convenience when reading a payment or line item
back, but it's never required or interpreted on write.) Line items reference sales categories the
same way, via sales_category_external_ids, resolved server-side with the same placeholder
behavior described above.
Update orders in 7shifts
Just like sales data creation, orders need to be updated in 7shifts as soon as they change in your
system, in order to ensure consistency. Send another PUT /orders/{external_id} request using the
structure described above, with the order's full current state.
Order voiding
PUT does not hard-delete an order, so to void one, set the order status to voided:
{
"net_total": 4399,
"gross_total": 4750,
"status": "voided",
"payments": [],
"line_items": []
}Order DELETE
To remove an order entirely, use DELETE /orders/{external_id} rather than PUT with a deleted
status. It's a true soft-delete:
- It sets the order's
statustodeletedand cascades that same soft-delete to all of the
order's payments and line items — you don't need to delete children separately. - It's idempotent — calling
DELETEon an order that's alreadydeletedis a no-op success. - It returns
404 Not Foundif no order matches the external ID for that location.
Refunds
Both order status and line item status accept a refund value, distinct from
open/closed/voided/deleted, for representing a refund:
- Send the refunded amount as a negative
net_total/gross_totalon the order (and on any
affectedline_items) — sales totals are summed directly from whatever you send, so a negative
amount is what causes the refund to reduce reported sales rather than add to them. - You don't need to zero out or otherwise adjust
quantity/item_counton arefund-status
order or line item — 7shifts already excludesrefund-status records from item-count
calculations, regardless of the quantity value sent. - A refund can either update an existing order (
PUTthe sameexternal_idwithstatus: "refund"and the corrected/negative totals) or be sent as its own order with a unique
external_id, if the refund isn't tied to a specific previously-sent order.
For example, a $35 refund sent as its own order (not tied to a previously-sent order):
{
"order_date": "2023-12-31T18:20:00-06:00",
"net_total": -3500,
"gross_total": -3500,
"status": "refund",
"payments": [
{
"total": -3500,
"payment_type": "credit",
"status": "closed"
}
],
"line_items": [
{
"sales_category_external_ids": ["food"],
"quantity": 1,
"net_total": -3500,
"gross_total": -3500,
"is_revenue_item": true,
"status": "refund"
}
]
}Partial refunds and partial voids
status is set per line item, not just at the order level, so a partial refund or partial void
affecting only some items on an order doesn't require voiding/refunding the whole order. The
order's own status can stay closed (or whatever it already was) while only the affected line
items change status. Since PUT is a full replacement, resend the order's complete line_items
array with the affected item(s) updated and the rest unchanged, and update the order's own
net_total/gross_total to reflect the new state.
- Partial void — set
status: "voided"on just the voided line item(s).voidedline items
are excluded from sales totals entirely, so reduce the order'snet_total/gross_totalby that
item's original amount (rather than sending a negative amount for it). - Partial refund — set
status: "refund"on just the refunded line item(s), with that item's
net_total/gross_totalset to the negative refunded amount. Reduce the order's own
net_total/gross_totalby the same amount.
For example, an order with two line items where the second one ($10) is fully voided after the
order was already closed:
{
"order_date": "2023-12-31T15:45:23-06:00",
"net_total": 2000,
"gross_total": 2000,
"status": "closed",
"payments": [
{ "total": 2000, "payment_type": "credit", "status": "closed" }
],
"line_items": [
{
"sales_category_external_ids": ["food"],
"quantity": 1,
"net_total": 2000,
"gross_total": 2000,
"is_revenue_item": true,
"status": "closed"
},
{
"sales_category_external_ids": ["drink"],
"quantity": 1,
"net_total": 1000,
"gross_total": 1000,
"is_revenue_item": true,
"status": "voided"
}
]
}Note net_total/gross_total on the order reflect only the still-active $20 item; the voided $10
item is included in line_items (so it's visible on reads) but no longer counted toward the
order's own totals or 7shifts' sales totals.
Reading orders back
GET /orders/{external_id}returns a single order with its fullpaymentsandline_items
arrays included.GET /orders(list) returns orders matching your filters, each with its completepaymentsand
line_itemsarrays embedded. Pagination (limit+cursor) applies to the orders themselves,
not to the number of payments or line items nested inside any one order.GET /ordersis always bounded to the last 90 days oforder_date, regardless of any other
filters supplied, and additionally supportsorder_date_gte/order_date_lte,
modified_gte/modified_lte,modified_since(mutually exclusive with the
modified_gte/modified_ltepair),status, andexternal_user_id.- Revenue centers, sales categories, and dining options each expose the same pair of read
endpoints — a single-itemGET /{resource}/{external_id}(returns404 Not Foundif no match
exists) and a cursor-paginatedGET /{resource}list — scoped to the location.
Updated 23 days ago
