Order Confirmed
This event will be sent after Stream successfully injects a DSP order into the POS.
Customer Fields
Each order carries a customers array holding the customer details for the
order. For a single-customer order — the canonical case — the array contains
exactly one element.
The singular customer field is deprecated. It is still populated with the
first customer for backwards compatibility, but new integrations should read
customers instead.
| Field | Type | Description |
|---|---|---|
name | string | Customer display name |
phone | string | Customer phone number |
masked_phone_code | string (optional) | Phone masking access code, only provided when the platform uses phone masking |
provider_loyalty_id | string (optional) | Customer loyalty program identifier, when provided by the ordering platform |
provider_loyalty_id lets loyalty platforms reconcile points and rewards
against the ordering platform's own customer identity. It is only present when
the ordering platform supplies a loyalty identifier on the order — orders
without one simply omit the field.
{
"customers": [
{
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111",
"provider_loyalty_id": "user@example.com"
}
]
}
Item and Modifier IDs
Each item and modifier carries a stream_id — the Stream catalog ID for that
object. It matches the stream_id exposed on the DSP menu, so integrators
using both the DSP and Events products can reconcile order contents against
their stored menu data instead of matching by name. Items or modifiers that
share a name are always distinguishable by their stream_id.
The field is omitted in the rare case where an order line could not be matched to a Stream catalog object, so treat it as optional.
Modifiers
Each item carries a modifiers array. A modifier may itself contain a nested
modifiers array when the selected option has its own selected modifiers (for
example, a "Half & Half" topping that further splits into per-half choices).
Nesting can be several levels deep.
The nested modifiers field is only present when a modifier has its own
selected modifiers — leaf modifiers omit it entirely. Consumers that do not
need nested modifiers can safely ignore the field, so existing integrations
are unaffected.
{
"stream_id": "STREAM_ITEM_1",
"name": "Build Your Own Pizza",
"quantity": 1,
"price": 1500,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_1",
"name": "Large",
"quantity": 1,
"price": 300
},
{
"stream_id": "STREAM_MODIFIER_2",
"name": "Half & Half",
"quantity": 1,
"price": 0,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_3",
"name": "Left: Pepperoni",
"quantity": 1,
"price": 200,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_4",
"name": "Extra Pepperoni",
"quantity": 2,
"price": 100
}
]
},
{
"stream_id": "STREAM_MODIFIER_5",
"name": "Right: Mushroom",
"quantity": 1,
"price": 150
}
]
}
]
}
Unpaid Orders
Most orders arrive already paid through the ordering platform. When unpaid is
true, payment has not been collected and is due at fulfillment — drive_thru
is the common case.
For unpaid orders, cash_amount_due carries the amount to collect at
fulfillment, in the lowest denomination (e.g. cents). It may be less than
total when the ordering platform funds a promotion that reduces the cash owed
without appearing as a separate discount line, so collect cash_amount_due
rather than deriving the amount from total.
Both fields are optional and omitted on ordinary prepaid orders.
Delivery Order
{
"type": "order.confirmed",
"location_id": "STREAM_LOCATION_1",
"reference_id": "PROVIDER_LOCATION_1",
"order": {
"id": "STREAM_ORDER_1234",
"source": "uber",
"dsp_order_id": "123456789",
"dsp_order_number": "1234",
"pos_order_id": "POS_ORDER_5678",
"customers": [
{
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
}
],
"customer": {
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
},
"currency": "usd",
"delivery_fee": 350,
"driver_tip": 100,
"estimated_fulfillment_time": 1706124214619,
"fulfillment_type": "delivery",
"items": [
{
"stream_id": "STREAM_ITEM_1",
"name": "Cheeseburger",
"quantity": 1,
"price": 1000,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_1",
"name": "Extra Patty",
"quantity": 1,
"price": 500
}
],
"special_instructions": "No bun please"
}
],
"special_instructions": "Extra napkins please",
"subtotal": 1500,
"tax": 150,
"total": 2100
}
}
Delivery Address Consent
On self-delivery orders (fulfillment_type: merchant_managed_delivery), the
customer's delivery_address and fulfillment_instructions are personal data
that ordering platforms only release with the customer's explicit consent.
Without that consent, the order omits delivery_address and
fulfillment_instructions entirely — the fields are simply absent, not empty
or null. Every other field is unaffected, and the order is still a valid
merchant_managed_delivery order.
Because of this, your integration must not assume a self-delivery order carries
an address. Treat both fields as optional and handle the missing case
explicitly — for example, by falling back to contacting the customer via
phone (with the masked_phone_code when phone masking is in use) to arrange
delivery.
The two examples below show the same self-delivery order without and with the customer's consent for the address.
Self-Delivery Order (without additional consent for delivery_address)
{
"type": "order.confirmed",
"location_id": "STREAM_LOCATION_1",
"reference_id": "PROVIDER_LOCATION_1",
"order": {
"id": "STREAM_ORDER_1234",
"source": "uber",
"dsp_order_id": "123456789",
"dsp_order_number": "1234",
"pos_order_id": "POS_ORDER_5678",
"customers": [
{
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
}
],
"customer": {
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
},
"currency": "usd",
"delivery_fee": 350,
"driver_tip": 100,
"estimated_fulfillment_time": 1706124214619,
"fulfillment_type": "merchant_managed_delivery",
"items": [
{
"stream_id": "STREAM_ITEM_1",
"name": "Cheeseburger",
"quantity": 1,
"price": 1000,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_1",
"name": "Extra Patty",
"quantity": 1,
"price": 500
}
],
"special_instructions": "No bun please"
}
],
"special_instructions": "Extra napkins please",
"subtotal": 1500,
"tax": 150,
"total": 2100
}
}
Self-Delivery Order (with additional consent for delivery_address)
{
"type": "order.confirmed",
"location_id": "STREAM_LOCATION_1",
"reference_id": "PROVIDER_LOCATION_1",
"order": {
"id": "STREAM_ORDER_1234",
"source": "uber",
"dsp_order_id": "123456789",
"dsp_order_number": "1234",
"pos_order_id": "POS_ORDER_5678",
"customers": [
{
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
}
],
"customer": {
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
},
"currency": "usd",
"delivery_address": {
"address_line_1": "123 test lane",
"address_line_2": "apt 11",
"city": "Los Angeles",
"state": "CA",
"postal_code": "90010",
"country": "USA",
"lat": 1000,
"long": 1000
},
"delivery_fee": 350,
"driver_tip": 100,
"estimated_fulfillment_time": 1706124214619,
"fulfillment_type": "merchant_managed_delivery",
"fulfillment_instructions": "Gate code 1234",
"items": [
{
"stream_id": "STREAM_ITEM_1",
"name": "Cheeseburger",
"quantity": 1,
"price": 1000,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_1",
"name": "Extra Patty",
"quantity": 1,
"price": 500
}
],
"special_instructions": "No bun please"
}
],
"special_instructions": "Extra napkins please",
"subtotal": 1500,
"tax": 150,
"total": 2100
}
}
Drive thru Order
{
"type": "order.confirmed",
"location_id": "STREAM_LOCATION_1",
"reference_id": "PROVIDER_LOCATION_1",
"order": {
"id": "STREAM_ORDER_1234",
"source": "flipdish",
"dsp_order_id": "123456789",
"dsp_order_number": "1234",
"pos_order_id": "POS_ORDER_5678",
"customers": [
{
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
}
],
"customer": {
"name": "test c.",
"phone": "+1111111111",
"masked_phone_code": "111"
},
"currency": "usd",
"estimated_fulfillment_time": 1706124214619,
"fulfillment_type": "drive_thru",
"items": [
{
"stream_id": "STREAM_ITEM_1",
"name": "Cheeseburger",
"quantity": 1,
"price": 1000,
"modifiers": [
{
"stream_id": "STREAM_MODIFIER_1",
"name": "Extra Patty",
"quantity": 1,
"price": 500
}
],
"special_instructions": "No bun please"
}
],
"special_instructions": "Extra napkins please",
"subtotal": 1500,
"tax": 150,
"total": 1650,
"unpaid": true,
"cash_amount_due": 1650
}
}