Skip to content
Last updated

Handoff Modes

Tote supports four handoff modes that determine how a customer receives their order. Each mode has different required fields, a different fulfillment flow, and different notification patterns for your UI.

The handoff mode is set on the cart before checkout using PUT /carts/{cartId}/handoff and persisted to the order when the cart is checked out. It can also be overridden at checkout time. The API uses a discriminated union: the mode field determines which additional fields are required.

This guide covers each mode in detail, walks through the fulfillment flow per mode, and shows how to set and override handoff modes with code examples.

Prerequisites

Before following this guide, make sure you have:

All examples in this guide use the sandbox base URL and consistent IDs:

Base URL:    https://sandbox.api.tote.ai/v1/online-ordering
Cart ID:     c1d2e3f4-a5b6-7890-cdef-1234567890ab
Order ID:    f9a8b7c6-d5e4-3210-fedc-ba9876543210
Location ID: b5a7c8d9-e0f1-4a2b-8c3d-4e5f6a7b8c9d

Mode Overview

ModeDescriptionRequired FieldsTerminal Fulfillment State
PICKUPCustomer picks up at the store countermode only (pickup_time optional)FULFILLED
CURBSIDEStaff brings order to the customer's vehiclevehicle_make, vehicle_model, vehicle_color (pickup_time optional)FULFILLED
DELIVERYDelivered to the customer's addressdelivery_address (delivery_instructions optional)DELIVERED
KIOSKSelf-service terminal inside the storemode only (kiosk_id optional)FULFILLED

The mode field acts as a discriminator. The API validates that the request body contains the required fields for the specified mode and rejects requests with missing or extraneous fields (422 Unprocessable Entity).

Setting Handoff Mode

Set the handoff mode on an ACTIVE cart using PUT /carts/{cartId}/handoff. You can call this endpoint multiple times -- the latest call wins. The same endpoint accepts all four mode shapes; only the request body changes.

import requests
from uuid import uuid4

BASE_URL = "https://sandbox.api.tote.ai/v1/online-ordering"
headers = {"Authorization": "Bearer YOUR_ACCESS_TOKEN"}
cart_id = "c1d2e3f4-a5b6-7890-cdef-1234567890ab"

PICKUP

response = requests.put(
    f"{BASE_URL}/carts/{cart_id}/handoff",
    headers={**headers, "Idempotency-Key": str(uuid4())},
    json={
        "mode": "PICKUP",
        "pickup_time": None,  # null = ASAP
    },
)
cart = response.json()
# cart["handoff_mode"] == {"mode": "PICKUP", "pickup_time": null}

Pass pickup_time as an ISO 8601 datetime to schedule a future pickup, or null for ASAP preparation.

CURBSIDE

response = requests.put(
    f"{BASE_URL}/carts/{cart_id}/handoff",
    headers={**headers, "Idempotency-Key": str(uuid4())},
    json={
        "mode": "CURBSIDE",
        "vehicle_make": "Toyota",
        "vehicle_model": "Camry",
        "vehicle_color": "Silver",
        "pickup_time": None,
    },
)
cart = response.json()

All three vehicle fields are required. Store staff uses this information to identify the customer's vehicle in the parking lot.

DELIVERY

response = requests.put(
    f"{BASE_URL}/carts/{cart_id}/handoff",
    headers={**headers, "Idempotency-Key": str(uuid4())},
    json={
        "mode": "DELIVERY",
        "delivery_address": {
            "street": "123 Main St, Apt 4B",
            "city": "Austin",
            "state": "TX",
            "postal_code": "78701",
        },
        "delivery_instructions": "Leave at the front door",
    },
)
cart = response.json()

The delivery_address object requires street, city, state (two-letter code), and postal_code. The country field defaults to "US" if omitted. delivery_instructions is optional.

KIOSK

response = requests.put(
    f"{BASE_URL}/carts/{cart_id}/handoff",
    headers={**headers, "Idempotency-Key": str(uuid4())},
    json={
        "mode": "KIOSK",
        "kiosk_id": "KIOSK-03",
    },
)
cart = response.json()

The kiosk_id is optional but useful for tracking which terminal placed the order. Omit it if your integration does not track individual kiosks.

PICKUP Mode

When to use: The customer places an order online, drives to the store, and picks it up at the counter.

Required fields:

FieldTypeRequiredDescription
modestringYesMust be "PICKUP"
pickup_timedatetime / nullNoScheduled pickup time (ISO 8601). Null for ASAP.

Fulfillment flow

Order created

Store accepts

Staff assembling

Order ready at counter

Customer picks up

Return processed

PENDING

IN_PROGRESS

PREPARING

READY_FOR_PICKUP

FULFILLED

RETURNED

Notification pattern

Fulfillment StatusCustomer NotificationAction
PENDING"Order received"Display confirmation screen
IN_PROGRESS"Store accepted your order"Show estimated prep time
PREPARING"Your order is being prepared"Show progress indicator
READY_FOR_PICKUP"Your order is ready for pickup"Prominent alert -- customer should head to store
FULFILLED"Order complete"Show receipt / feedback prompt

The transition from READY_FOR_PICKUP to FULFILLED happens when the store staff confirms the customer has picked up the order.

Cancellation

Orders can be cancelled via the API when fulfillment_status is PENDING or IN_PROGRESS. Once the status reaches PREPARING, the API returns 409 Conflict and cancellation requires store intervention.

CURBSIDE Mode

When to use: The customer parks in a designated curbside spot and staff brings the order to their vehicle. Common for convenience stores with drive-through lanes or designated pickup parking.

Required fields:

FieldTypeRequiredDescription
modestringYesMust be "CURBSIDE"
vehicle_makestringYesManufacturer (e.g., "Toyota")
vehicle_modelstringYesModel (e.g., "Camry")
vehicle_colorstringYesColor (e.g., "Silver")
pickup_timedatetime / nullNoScheduled time. Null for ASAP.

Fulfillment flow

Order created

Store accepts

Staff assembling

Order ready

Staff delivers to vehicle

Return processed

PENDING

IN_PROGRESS

PREPARING

READY_FOR_PICKUP

FULFILLED

RETURNED

The curbside flow uses the same states as PICKUP. The difference is operational: when the order reaches READY_FOR_PICKUP, staff looks for the customer's vehicle using the stored make, model, and color instead of waiting for the customer at the counter.

Notification pattern

Fulfillment StatusCustomer NotificationAction
PENDING"Order received"Display confirmation
IN_PROGRESS"Store accepted your order"Show estimated prep time
PREPARING"Your order is being prepared"Show progress indicator
READY_FOR_PICKUP"Your order is ready -- pull into a curbside spot"Prompt customer to park and signal arrival
FULFILLED"Order delivered to your vehicle"Show receipt / feedback prompt

Vehicle arrival

How the customer signals their arrival at the store is implementation-specific. Some approaches:

  • Check-in button in your app that calls a custom endpoint or updates order metadata
  • Push notification asking the customer to confirm they have arrived
  • Manual communication (customer calls the store)

The Tote API tracks fulfillment status, not vehicle location. The store staff advances the order from READY_FOR_PICKUP to FULFILLED after delivering the order to the vehicle.

Cancellation

Same rules as PICKUP: cancellable when PENDING or IN_PROGRESS. Once PREPARING, requires store intervention.

DELIVERY Mode

When to use: The order is delivered to the customer's address by store staff or a third-party delivery provider.

Required fields:

FieldTypeRequiredDescription
modestringYesMust be "DELIVERY"
delivery_addressobjectYesDestination address (see fields below)
delivery_instructionsstring / nullNoInstructions for the driver

Delivery address fields:

FieldTypeRequiredDescription
streetstringYesStreet address including apt/suite
citystringYesCity name
statestringYesTwo-letter US state code
postal_codestringYesZIP or postal code
countrystringNoTwo-letter ISO 3166-1 code (defaults to "US")

Fulfillment flow

Order created

Store accepts

Staff assembling

Ready for dispatch

Driver confirms delivery

Return processed

PENDING

IN_PROGRESS

PREPARING

READY_FOR_PICKUP

DELIVERED

RETURNED

Note on READY_FOR_PICKUP: In delivery context, this state means "order is packed and ready for the driver." The state name reflects the shared backend enum across all modes. In your customer-facing UI, display this as "Out for delivery" or "Awaiting driver pickup" rather than "Ready for pickup."

Notification pattern

Fulfillment StatusCustomer NotificationAction
PENDING"Order received"Display confirmation
IN_PROGRESS"Store accepted your order"Show estimated delivery time
PREPARING"Your order is being prepared"Show progress indicator
READY_FOR_PICKUP"Your order is packed and awaiting pickup by driver"Show driver assignment (if available)
DELIVERED"Your order has been delivered"Confirm delivery / feedback prompt

Delivery dispatch

The Tote API tracks fulfillment status but does not manage delivery driver assignment or routing. Partners integrate with their preferred delivery provider: store fleet, DoorDash Drive, Uber Direct, or another service.

The recommended integration pattern:

  1. When the order reaches PREPARING, request a delivery quote from your provider.
  2. When the order reaches READY_FOR_PICKUP, accept the quote to dispatch a driver.
  3. When the driver confirms delivery, update the order's fulfillment status to DELIVERED.

Delivery dispatch integration details (provider-specific APIs, driver tracking, estimated arrival times) are outside the scope of the Tote Online Ordering API. Your delivery provider handles driver assignment, routing, and real-time driver location.

Address validation

The API does not validate delivery addresses. It stores the address as provided. Partners should validate addresses before checkout using their preferred geocoding service to ensure:

  • The address is real and deliverable
  • The address is within the store's delivery radius (if applicable)
  • The address components are properly formatted

Cancellation

Same rules as other modes. Additionally, if a third-party delivery driver has already been dispatched, you may need to cancel the delivery through the provider's API separately.

KIOSK Mode

When to use: The customer places an order at a self-service kiosk terminal inside the store. The customer is already on-site, so no delivery address or vehicle details are needed.

Required fields:

FieldTypeRequiredDescription
modestringYesMust be "KIOSK"
kiosk_idstring / nullNoIdentifier for the kiosk terminal

Fulfillment flow

Order created

Store accepts

Staff assembling

Order ready at counter

Customer picks up

Return processed

PENDING

IN_PROGRESS

PREPARING

READY_FOR_PICKUP

FULFILLED

RETURNED

This is the simplest flow. The customer is already in the store, so preparation and handoff happen quickly. Many kiosk orders skip the PENDING and IN_PROGRESS states entirely if the store auto-accepts kiosk orders.

Notification pattern

Fulfillment StatusCustomer NotificationAction
PENDING"Order placed"Show order number on kiosk screen
PREPARING"Preparing your order"Display on kiosk or overhead screen
READY_FOR_PICKUP"Order ready -- pick up at counter"Prominent display: "Order #XYZ is ready"
FULFILLED"Order complete"Clear kiosk screen / show receipt

For kiosk deployments, consider displaying the order number prominently on an overhead display or the kiosk screen itself so the customer knows when to approach the counter.

Cancellation

Same rules as other modes: cancellable when PENDING or IN_PROGRESS.

Fulfillment Status by Mode

This table shows which fulfillment states apply to each handoff mode and their meaning in context.

Fulfillment StatusPICKUPCURBSIDEDELIVERYKIOSK
PENDINGYesYesYesYes
IN_PROGRESSYesYesYesYes
PREPARINGYesYesYesYes
READY_FOR_PICKUPYes (at counter)Yes (awaiting vehicle)Yes (ready for dispatch)Yes (at counter)
FULFILLEDYes (terminal)Yes (terminal)--Yes (terminal)
DELIVERED----Yes (terminal)--
RETURNEDYesYesYesYes
CANCELLEDYesYesYesYes

Key observations:

  • FULFILLED is the terminal success state for PICKUP, CURBSIDE, and KIOSK. It means the customer physically received the order at the store.
  • DELIVERED is the terminal success state for DELIVERY only. It means the order was delivered to the customer's address.
  • READY_FOR_PICKUP is shared across all modes but has different operational meaning: "at the counter" for PICKUP/KIOSK, "staff heading to vehicle" for CURBSIDE, and "ready for driver dispatch" for DELIVERY.
  • RETURNED and CANCELLED apply to all modes.

Overriding at Checkout

The checkout endpoint (POST /carts/{cartId}/checkout) accepts an optional handoff_mode field that overrides whatever was previously set via PUT /carts/{cartId}/handoff.

This is useful when the customer changes their mind at the last moment -- for example, switching from PICKUP to CURBSIDE after they realize they would rather stay in the car.

Example: Override from PICKUP to CURBSIDE

Assume the cart already has PICKUP set from an earlier call. At checkout, the customer decides they want curbside instead:

response = requests.post(
    f"{BASE_URL}/carts/{cart_id}/checkout",
    headers={**headers, "Idempotency-Key": str(uuid4())},
    json={
        "handoff_mode": {
            "mode": "CURBSIDE",
            "vehicle_make": "Honda",
            "vehicle_model": "Civic",
            "vehicle_color": "Blue",
        },
        "expected_total": 1945,
    },
)
order = response.json()
# order["handoff"]["mode"] == "CURBSIDE"

The order's handoff field will reflect the override (CURBSIDE), not the cart's original stored mode (PICKUP).

Rules

  • If handoff_mode is provided in the checkout request, it completely replaces the cart's stored handoff mode.
  • If handoff_mode is omitted from the checkout request, the cart's stored handoff mode is used.
  • If neither the cart nor the checkout request has a handoff mode, the server returns 422 Unprocessable Entity.

Best Practices

Validate mode-specific fields on the client. Check that vehicle details are non-empty for CURBSIDE and that the delivery address has all required fields before calling the API. This saves a round trip and provides instant feedback to the customer.

Default to PICKUP when the mode is unknown. If your UI does not yet support all four modes, default to PICKUP as the simplest option. You can add CURBSIDE, DELIVERY, and KIOSK as your integration matures.

Validate delivery addresses before checkout. The API stores the address as provided without validation. Use a geocoding service to verify the address is real and within the store's delivery radius before the customer completes their order.

Track fulfillment_status for customer-facing updates. The fulfillment_status field is the most relevant for real-time order tracking. Use it to power progress indicators in your app. The status field (PENDING, CONFIRMED, COMPLETED) is better suited for order history views.

Use webhooks for real-time updates. Subscribe to the order.status_changed webhook event to receive push notifications when the fulfillment status changes. This eliminates the need for polling. See the Webhooks guide for setup instructions. If webhooks are not yet configured, fall back to polling with exponential backoff as described in the Order Tracking guide.

Display READY_FOR_PICKUP appropriately per mode. This state has different customer-facing meanings depending on the handoff mode. Map it to the right label in your UI:

ModeREADY_FOR_PICKUP Display Text
PICKUP"Your order is ready for pickup"
CURBSIDE"Your order is ready -- head to a curbside spot"
DELIVERY"Your order is packed and awaiting driver"
KIOSK"Your order is ready at the counter"

Next Steps