Understanding Orders
Orders in the Scoffable API contain rich information about customer purchases, payments, and any modifications that occur during fulfilment. This page explains the key concepts and data structures you'll encounter.
Overview
Each order is a complete snapshot containing:
- Items purchased (with options)
- Payment information
- Any modifications (substitutions, price adjustments)
- Status and version tracking
Orders evolve over time through versioning — each change creates a new version with updated information.
Order Versioning
Every modification to an order creates a new version. Each version is a complete snapshot of the order at that moment, not just the changes.
- Version 1: Created when the order is placed
- Version 2+: Created for any subsequent change
New versions are created when:
- Order status changes (accepted, rejected, cancelled)
- Items are substituted
- Prices are adjusted
- Customer or vendor modifies the order
- Internal processing updates occur
Note that version numbers may increment even without visible changes due to internal processing. Older versions remain unchanged, preserving the historical state of the order at each point in time.
Order Lifecycle
Orders progress through different statuses during their lifecycle:
placed ───> accepted <──> cancelled
│ ▲
└────────────┘
rejected
Order Statuses
placed: The initial status when an order is createdaccepted: The vendor has accepted the order for fulfilmentrejected: The vendor declined the order or failed to acknowledge itcancelled: The order was initially accepted but later cancelled by either the vendor or customer
Key Differences
rejectedvscancelled: Rejected orders were never accepted, while cancelled orders were accepted first- Processing implications: Most integrations skip
placedandrejectedorders, only processingacceptedorcancelledorders
Status Transitions
Important transition rules:
- No return to
placedorrejected: Once an order leaves these states, it can never return to them rejected→accepted: Can occur if Scoffable initially failed to receive vendor acknowledgement, or if the vendor changes their mindcancelled→accepted: Rare, but possible if cancellation was in error or due to communication issues
The placed status only occurs at order creation, and rejected can only happen before acceptance. Once accepted, an order can only move between accepted and cancelled.
Understanding these transitions is important for handling edge cases in your integration.
Order Items
The items array contains everything ordered, including products, discounts, and adjustments. Each item has a type field:
product: Physical items ordered by the customeroffer: Discounts or promotions (e.g., "buy 2 get 1 free")voucher: Vouchers applied by the customeradjustment: Post-order modifications (refunds, price corrections)
Quantity Fields
Every item tracks two quantities:
quantityOrdered: What the customer originally requestedquantityFulfilled: What was actually delivered
These typically match, but differ for substitutions or out-of-stock items.
Option Categories and Options
Many items can be customised through option categories. The optionCategories array in the order shows only the customer's selections, not all available options:
{
"name": "Set meal for 2",
"type": "product",
"price": {"amount": 1999, "currency": "GBP"},
"total": {"amount": 2149, "currency": "GBP"},
"optionCategories": [
{
"name": "Starters",
"selectedOptions": [
{"name": "Spring Rolls"},
{"name": "Prawn Toast"}
]
},
{
"name": "Mains",
"selectedOptions": [
{"name": "Sweet and Sour Chicken"},
{
"name": "Crispy Shredded Duck",
"optionPrice": {"amount": 150, "currency": "GBP"}
}
]
}
]
}
In this example, the menu item might have offered many starter choices (Spring Rolls, Prawn Toast, Spare Ribs, Soup, etc.), but the order only shows what the customer selected. Categories with no selections won't appear at all.
Key Points
- Only selected options appear in the order
- Unselected option categories are omitted entirely
- Each category shows the customer's specific choices
- Standard options have no additional cost
- Premium options include
optionPricefor the extra charge - The item's base
priceincludes standard options but not premium additions
Price Adjustments
Price corrections appear as separate adjustment items linked to the original products:
Adjusted Product
{
"id": "2d4f50ed-de8e-4296-b7c5-9b5704cc6240",
"name": "Set meal for 2",
"type": "product",
"price": {"amount": 1999, "currency": "GBP"},
"priceAdjustmentDetails": {
"relatedPriceAdjustment": "18bcee32-48d7-444b-a374-866f1caa6d02"
}
}
Adjustment Entry
{
"id": "18bcee32-48d7-444b-a374-866f1caa6d02",
"name": "Price match",
"type": "adjustment",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": -400, "currency": "GBP"},
"priceAdjustmentDetails": {
"itemsAdjusted": ["2d4f50ed-de8e-4296-b7c5-9b5704cc6240"]
}
}
Complex Example: Partial Adjustment
When a customer orders multiple quantities and only some need adjustment, the items are split:
// Originally ordered as quantity 2, now split into separate items
// Undamaged pizza (no adjustment)
{
"id": "c3d4e5f6-7890-1234-5678-90abcdef1234",
"name": "Margherita Pizza",
"type": "product",
"quantityOrdered": 1,
"quantityFulfilled": 1,
"price": {"amount": 1299, "currency": "GBP"},
"total": {"amount": 1299, "currency": "GBP"}
}
// Damaged pizza (with adjustment)
{
"id": "e5f6a7b8-9012-3456-7890-bcdef1234567",
"name": "Margherita Pizza",
"type": "product",
"quantityOrdered": 1,
"quantityFulfilled": 1,
"price": {"amount": 1299, "currency": "GBP"},
"total": {"amount": 1299, "currency": "GBP"},
"priceAdjustmentDetails": {
"relatedPriceAdjustment": "d4e5f6a7-8901-2345-6789-0abcdef12345"
}
}
// Adjustment for the damaged pizza
{
"id": "d4e5f6a7-8901-2345-6789-0abcdef12345",
"name": "Compensation for damaged item",
"type": "adjustment",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": -1299, "currency": "GBP"},
"total": {"amount": -1299, "currency": "GBP"},
"priceAdjustmentDetails": {
"itemsAdjusted": ["e5f6a7b8-9012-3456-7890-bcdef1234567"]
}
}
In this example, the customer originally ordered 2 pizzas. When one was damaged, the order splits into two separate items so the adjustment can link to only the affected item.
Substitutions
When ordered items are unavailable, vendors may substitute them with alternatives. The substitution system is flexible and supports various scenarios including one-to-one, many-to-one, one-to-many, and many-to-many substitutions.
Simple One-to-One Substitution
The most common case - one item replaced with another:
Original Item (Not Delivered)
{
"id": "2d4f50ed-de8e-4296-b7c5-9b5704cc6240",
"name": "Galaxy 200g",
"type": "product",
"quantityOrdered": 1,
"quantityFulfilled": 0,
"price": {"amount": 399, "currency": "GBP"},
"substitutionDetails": {
"substitutedBy": ["5114d08f-bb16-4fad-b992-f43682665b40"]
}
}
Substitute Item (Delivered Instead)
{
"id": "5114d08f-bb16-4fad-b992-f43682665b40",
"name": "Dairy Milk 200g",
"type": "product",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": 399, "currency": "GBP"},
"substitutionDetails": {
"substitutedFor": ["2d4f50ed-de8e-4296-b7c5-9b5704cc6240"]
}
}
Complex Substitutions
The substitutedBy and substitutedFor fields are arrays, enabling complex substitution patterns:
Many-to-One Example
Four small chocolate bars replaced with one large bar:
// Four small bars (not delivered)
{
"id": "8f2e3d4c-9a1b-4567-8901-23456789abcd",
"name": "Small Chocolate Bar 50g",
"quantityOrdered": 4,
"quantityFulfilled": 0,
"substitutionDetails": {
"substitutedBy": ["a1b2c3d4-e5f6-4789-0123-456789abcdef"]
}
}
// One large bar (delivered instead)
{
"id": "a1b2c3d4-e5f6-4789-0123-456789abcdef",
"name": "Large Chocolate Bar 200g",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"substitutionDetails": {
"substitutedFor": ["8f2e3d4c-9a1b-4567-8901-23456789abcd"]
}
}
One-to-Many Example
One item replaced with multiple different items:
// Original variety pack (not delivered)
{
"id": "7c8d9e0f-1234-5678-90ab-cdef12345678",
"name": "Chocolate Variety Pack",
"quantityOrdered": 1,
"quantityFulfilled": 0,
"substitutionDetails": {
"substitutedBy": ["3f4e5d6c-7890-1234-5678-90abcdef1234", "9a8b7c6d-5432-1098-7654-321098765432"]
}
}
// Multiple different substitutes (delivered instead)
{
"id": "3f4e5d6c-7890-1234-5678-90abcdef1234",
"name": "Milk Chocolate Bar 100g",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"substitutionDetails": {
"substitutedFor": ["7c8d9e0f-1234-5678-90ab-cdef12345678"]
}
},
{
"id": "9a8b7c6d-5432-1098-7654-321098765432",
"name": "Dark Chocolate Bar 100g",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"substitutionDetails": {
"substitutedFor": ["7c8d9e0f-1234-5678-90ab-cdef12345678"]
}
}
Note: If substituting with multiples of the same item (e.g., two Regular Pack 200g), this would appear as a single item with quantityFulfilled: 2, not as separate items.
Many-to-Many Example
Multiple wine bottles replaced with a different combination due to availability:
// Original wine order (not available)
{
"id": "4e5f6a7b-8901-2345-6789-abcdef123456",
"name": "Pinot Grigio 750ml",
"quantityOrdered": 2,
"quantityFulfilled": 0,
"price": {"amount": 899, "currency": "GBP"},
"substitutionDetails": {
"substitutedBy": ["1a2b3c4d-5678-9012-3456-789012345678", "2b3c4d5e-6789-0123-4567-890123456789"]
}
},
{
"id": "5f6a7b8c-9012-3456-7890-bcdef1234567",
"name": "Sauvignon Blanc 750ml",
"quantityOrdered": 1,
"quantityFulfilled": 0,
"price": {"amount": 999, "currency": "GBP"},
"substitutionDetails": {
"substitutedBy": ["1a2b3c4d-5678-9012-3456-789012345678", "2b3c4d5e-6789-0123-4567-890123456789"]
}
}
// Substitute wines (delivered instead)
{
"id": "1a2b3c4d-5678-9012-3456-789012345678",
"name": "Mixed White Wine Case (6x250ml)",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": 1899, "currency": "GBP"},
"substitutionDetails": {
"substitutedFor": ["4e5f6a7b-8901-2345-6789-abcdef123456", "5f6a7b8c-9012-3456-7890-bcdef1234567"]
}
},
{
"id": "2b3c4d5e-6789-0123-4567-890123456789",
"name": "Chardonnay 750ml",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": 899, "currency": "GBP"},
"substitutionDetails": {
"substitutedFor": ["4e5f6a7b-8901-2345-6789-abcdef123456", "5f6a7b8c-9012-3456-7890-bcdef1234567"]
}
}
In this example, the customer ordered 3 bottles of wine (2 Pinot Grigio + 1 Sauvignon Blanc). Due to availability, the vendor substituted with a mixed case of smaller bottles plus a full-size Chardonnay, providing equivalent volume. Both substitutes reference all original items as they collectively replace the entire wine order.
Complex Example: Substitution with Price Adjustment
A common pattern is an item being substituted and then price-adjusted to match the original:
// Original item ordered (not delivered)
{
"id": "b5c6d7e8-9012-3456-7890-abcdef123456",
"name": "Standard Chocolate 100g",
"type": "product",
"quantityOrdered": 1,
"quantityFulfilled": 0,
"price": {"amount": 299, "currency": "GBP"},
"total": {"amount": 299, "currency": "GBP"},
"substitutionDetails": {
"substitutedBy": ["f1e2d3c4-5678-9012-3456-789012345678"]
}
}
// Premium substitute delivered
{
"id": "f1e2d3c4-5678-9012-3456-789012345678",
"name": "Premium Chocolate 100g",
"type": "product",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": 499, "currency": "GBP"},
"total": {"amount": 499, "currency": "GBP"},
"substitutionDetails": {
"substitutedFor": ["b5c6d7e8-9012-3456-7890-abcdef123456"]
},
"priceAdjustmentDetails": {
"relatedPriceAdjustment": "a9b8c7d6-3456-7890-1234-567890abcdef"
}
}
// Price adjustment to match original price
{
"id": "a9b8c7d6-3456-7890-1234-567890abcdef",
"name": "Substitution price match",
"type": "adjustment",
"quantityOrdered": 0,
"quantityFulfilled": 1,
"price": {"amount": -200, "currency": "GBP"},
"total": {"amount": -200, "currency": "GBP"},
"priceAdjustmentDetails": {
"itemsAdjusted": ["f1e2d3c4-5678-9012-3456-789012345678"]
}
}
In this example:
- Customer ordered a £2.99 item
- Vendor substituted with a £4.99 premium item
- A -£2.00 adjustment ensures customer pays the original £2.99
- All three items appear in the order for complete tracking
Substitution Pricing Scenarios
When substitutes have different prices, the pricing can be handled in different ways:
Scenario 1: Price Matched (Via Adjustment)
If a £3.99 item is substituted with a £4.99 alternative at no extra cost:
- Original item shows
quantityFulfilled: 0 - Substitute item shows
quantityFulfilled: 1with its actual price (£4.99) - An adjustment item of -£1.00 is added to maintain the original price
Scenario 2: Customer Pays Higher Price
If the customer agrees to a premium substitution:
- Original item shows
quantityFulfilled: 0at £3.99 - Substitute shows
quantityFulfilled: 1at £4.99 - No price adjustment needed
- Scoffable may collect the additional £1.00 from the customer
- The order total increases accordingly
Scenario 3: Scoffable Goodwill
In some cases, Scoffable may absorb the cost difference:
- Substitute is delivered at higher price
- Customer still pays original total
- Scoffable covers the difference as goodwill
Processing Substitutions
- Check
quantityFulfilledto identify what was actually delivered - Use
substitutionDetailsto link original and replacement items - Check for price adjustments to understand the pricing approach
- Compare order total with sum of fulfilled items to identify goodwill scenarios
- The final customer payment may differ from the original order value
Common Price Adjustment Scenarios
- Adjusting substitute prices to match original items
- Price matching competitors
- Compensation for damaged items
- Correction of pricing errors
- Partial refunds
Customer Payments
The customerPayments array shows how the order was paid and who collected the funds:
"customerPayments": [
{
"type": "cash",
"collectedBy": "vendor",
"payment": {"amount": 1899, "currency": "GBP"}
},
{
"type": "voucher",
"collectedBy": "scoffable",
"payment": {"amount": 500, "currency": "GBP"}
}
]
Payment Types
- online: Card or digital payment
- cash: Physical cash payment
- voucher: Scoffable credit redemption (promotional or purchased gift vouchers)
Collection Responsibility
- scoffable: Payment processed through the platform
- vendor: Payment collected directly by the vendor
This distinction is critical for financial reconciliation — it determines who holds the funds and who owes whom.
Vouchers and Promotions
Vouchers can appear in two different places, representing different types of promotions:
Vendor-Funded Promotions (Item)
When a voucher appears as an item, it's a promotion funded by the vendor:
{
"name": "10% off promotion",
"type": "voucher",
"quantityOrdered": 1,
"quantityFulfilled": 1,
"price": {"amount": -500, "currency": "GBP"}
}
The vendor bears the cost of this discount.
Scoffable Vouchers (Payment)
When a voucher appears in customerPayments, it's a Scoffable-issued voucher used as payment:
{
"type": "voucher",
"collectedBy": "scoffable",
"payment": {"amount": 500, "currency": "GBP"}
}
Scoffable has already collected payment for this voucher (e.g., from a corporate client or previous purchase) and will settle with the vendor.
Understanding the Distinction
- Item voucher: Vendor gives discount, receives less money
- Payment voucher: Customer uses Scoffable credit, vendor receives full payment (from Scoffable)
Delivery Information
Orders with type: "delivery" include additional delivery-related fields:
Delivery Provider
The deliveryProvider field indicates who handles the delivery:
vendor: The vendor manages their own deliveryuber: Delivery handled through Uber's courier network
Delivery Tracking
Delivery tracking availability depends on the provider:
- Uber delivery: Usually provides tracking updates and
deliveredAttimestamps - Vendor delivery: May not provide tracking or
deliveredAttimes if the vendor doesn't use tracked deliveries
This means for vendor-managed deliveries, you might never receive a deliveredAt timestamp, and should plan your integration accordingly.
Financial Reconciliation
Key fields for reconciliation:
serviceFee: Fee charged to the customer on behalf of the vendordeliveryFee: Delivery charge (when applicable)total: Final order amount the customer pays (includes service fee and delivery fee)customerPayments: How the order was paid and who collected it
Related Documentation
- Order Sync - Integration patterns for synchronising orders
- API Reference - Detailed endpoint specifications
- FAQ - Common questions about order statuses