# 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: - **A cart with items** -- See the [Cart Lifecycle guide](/online-ordering/guides/04-cart-lifecycle) for how to create a cart and add items. - **An access token** -- See the [Authentication guide](/online-ordering/guides/02-authentication). - **Familiarity with fulfillment status** -- See the [Order Tracking guide](/online-ordering/guides/08-order-tracking) for the three independent status fields on an order. 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 | Mode | Description | Required Fields | Terminal Fulfillment State | | --- | --- | --- | --- | | `PICKUP` | Customer picks up at the store counter | `mode` only (`pickup_time` optional) | FULFILLED | | `CURBSIDE` | Staff brings order to the customer's vehicle | `vehicle_make`, `vehicle_model`, `vehicle_color` (`pickup_time` optional) | FULFILLED | | `DELIVERY` | Delivered to the customer's address | `delivery_address` (`delivery_instructions` optional) | DELIVERED | | `KIOSK` | Self-service terminal inside the store | `mode` 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. ```python 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 ```python 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 ```python 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 ```python 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 ```python 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:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `mode` | string | Yes | Must be `"PICKUP"` | | `pickup_time` | datetime / null | No | Scheduled pickup time (ISO 8601). Null for ASAP. | ### Fulfillment flow ```mermaid stateDiagram-v2 [*] --> PENDING : Order created PENDING --> IN_PROGRESS : Store accepts IN_PROGRESS --> PREPARING : Staff assembling PREPARING --> READY_FOR_PICKUP : Order ready at counter READY_FOR_PICKUP --> FULFILLED : Customer picks up FULFILLED --> RETURNED : Return processed ``` ### Notification pattern | Fulfillment Status | Customer Notification | Action | | --- | --- | --- | | 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:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `mode` | string | Yes | Must be `"CURBSIDE"` | | `vehicle_make` | string | Yes | Manufacturer (e.g., "Toyota") | | `vehicle_model` | string | Yes | Model (e.g., "Camry") | | `vehicle_color` | string | Yes | Color (e.g., "Silver") | | `pickup_time` | datetime / null | No | Scheduled time. Null for ASAP. | ### Fulfillment flow ```mermaid stateDiagram-v2 [*] --> PENDING : Order created PENDING --> IN_PROGRESS : Store accepts IN_PROGRESS --> PREPARING : Staff assembling PREPARING --> READY_FOR_PICKUP : Order ready READY_FOR_PICKUP --> FULFILLED : Staff delivers to vehicle FULFILLED --> RETURNED : Return processed ``` 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 Status | Customer Notification | Action | | --- | --- | --- | | 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:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `mode` | string | Yes | Must be `"DELIVERY"` | | `delivery_address` | object | Yes | Destination address (see fields below) | | `delivery_instructions` | string / null | No | Instructions for the driver | **Delivery address fields:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `street` | string | Yes | Street address including apt/suite | | `city` | string | Yes | City name | | `state` | string | Yes | Two-letter US state code | | `postal_code` | string | Yes | ZIP or postal code | | `country` | string | No | Two-letter ISO 3166-1 code (defaults to "US") | ### Fulfillment flow ```mermaid stateDiagram-v2 [*] --> PENDING : Order created PENDING --> IN_PROGRESS : Store accepts IN_PROGRESS --> PREPARING : Staff assembling PREPARING --> READY_FOR_PICKUP : Ready for dispatch READY_FOR_PICKUP --> DELIVERED : Driver confirms delivery DELIVERED --> RETURNED : Return processed ``` **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 Status | Customer Notification | Action | | --- | --- | --- | | 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:** | Field | Type | Required | Description | | --- | --- | --- | --- | | `mode` | string | Yes | Must be `"KIOSK"` | | `kiosk_id` | string / null | No | Identifier for the kiosk terminal | ### Fulfillment flow ```mermaid stateDiagram-v2 [*] --> PENDING : Order created PENDING --> IN_PROGRESS : Store accepts IN_PROGRESS --> PREPARING : Staff assembling PREPARING --> READY_FOR_PICKUP : Order ready at counter READY_FOR_PICKUP --> FULFILLED : Customer picks up FULFILLED --> RETURNED : Return processed ``` 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 Status | Customer Notification | Action | | --- | --- | --- | | 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 Status | PICKUP | CURBSIDE | DELIVERY | KIOSK | | --- | --- | --- | --- | --- | | PENDING | Yes | Yes | Yes | Yes | | IN_PROGRESS | Yes | Yes | Yes | Yes | | PREPARING | Yes | Yes | Yes | Yes | | READY_FOR_PICKUP | Yes (at counter) | Yes (awaiting vehicle) | Yes (ready for dispatch) | Yes (at counter) | | FULFILLED | Yes (terminal) | Yes (terminal) | -- | Yes (terminal) | | DELIVERED | -- | -- | Yes (terminal) | -- | | RETURNED | Yes | Yes | Yes | Yes | | CANCELLED | Yes | Yes | Yes | Yes | 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: ```python 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](/online-ordering/guides/09-webhooks) for setup instructions. If webhooks are not yet configured, fall back to polling with exponential backoff as described in the [Order Tracking guide](/online-ordering/guides/08-order-tracking#polling-for-status-updates). **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: | Mode | READY_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 - [Cart Lifecycle guide](/online-ordering/guides/04-cart-lifecycle) -- Setting handoff mode as part of the cart flow - [Order Tracking guide](/online-ordering/guides/08-order-tracking) -- Monitoring fulfillment status after checkout - [Checkout & Payments guide](/online-ordering/guides/06-checkout-payments) -- Submitting payment for the order - [Webhooks guide](/online-ordering/guides/09-webhooks) -- Real-time push notifications for fulfillment status changes