# Tote Online Ordering API REST API for 3rd party developers building online ordering integrations with Tote POS convenience stores and fuel stations. ## Overview The Tote Online Ordering API enables partners to: - Authenticate via OAuth 2.0 client credentials - Browse store locations and business hours - Retrieve full menus with nested modifier groups - Create and manage shopping carts - Submit orders with split payment support - Track order fulfillment status - Subscribe to webhook events for real-time updates - Check item availability and inventory ## Base URL The server URLs below are **placeholders**. Your actual API base URL, client ID, and client secret are provided during partner onboarding. Contact developer@totepos.com to get started. Path parameters use snake_case. JSON response fields use snake_case. Schema names use PascalCase. Enum values use SCREAMING_SNAKE_CASE. Version: 1.0.0 ## Servers Production (placeholder -- use your assigned base URL) ``` https://api.tote.ai/v1/online-ordering ``` Sandbox (placeholder -- use your assigned base URL) ``` https://sandbox.api.tote.ai/v1/online-ordering ``` ## Security ### oauth2 OAuth 2.0 client credentials flow for machine-to-machine authentication. Use your client_id and client_secret to obtain an access token via POST /auth/token, then include the token in the Authorization header of all subsequent requests. Type: oauth2 ## Download OpenAPI description [Tote Online Ordering API](https://tote.redocly.app/_bundle/online-ordering/spec/openapi.yaml) ## Authentication OAuth 2.0 client credentials token management. ### Create an access token - [POST /auth/token](https://tote.redocly.app/online-ordering/spec/openapi/authentication/createtoken.md): Exchange your client credentials for an access token using the OAuth 2.0 client credentials flow. How it works: 1. Send your client_id and client_secret in the request body 2. Receive a Bearer token valid for 24 hours 3. Include the token in the Authorization header of all subsequent requests Token caching: Cache your token and reuse it until it expires. Do not request a new token for every API call -- this will trigger rate limiting. Token expiry: Tokens expire after 86400 seconds (24 hours). When your token expires, request a new one. There is no refresh token flow -- simply re-authenticate with your client credentials. ## Locations Store locations, hours, and capabilities. ### List all locations - [GET /locations](https://tote.redocly.app/online-ordering/spec/openapi/locations/listlocations.md): Returns a paginated list of locations accessible to your API credentials. Use the online_ordering_enabled field to filter locations that accept online orders. Pagination: Results are returned in pages. Use the cursor parameter from the pagination.next_cursor field to retrieve the next page. ### Get a location by ID - [GET /locations/{location_id}](https://tote.redocly.app/online-ordering/spec/openapi/locations/getlocation.md): Returns a single location by its unique identifier. The response includes the full location details including address, business hours, and whether online ordering is enabled. ## Menus Menu categories, items, modifier groups, and pricing. ### Get the full menu for a location - [GET /locations/{location_id}/menu](https://tote.redocly.app/online-ordering/spec/openapi/menus/getmenu.md): Returns the complete menu for a location, including all categories, items, and nested modifier groups. Menu structure: - A menu contains categories (e.g., "Deli & Sandwiches", "Beverages") - Each category contains items (e.g., "Build Your Own Sub Sandwich") - Each item may have modifier groups (e.g., "Bread Choice", "Protein") - Each modifier group contains modifiers (e.g., "White", "Wheat") - Modifiers can have nested modifier groups (up to 3 levels deep) Caching: Use the version_hash field to detect menu changes. For lightweight change detection without downloading the full menu, use the GET /locations/{location_id}/menu/metadata endpoint. Response size: Menu responses can be large (100KB+). Cache aggressively and use the metadata endpoint for polling. ### Get menu metadata for change detection - [GET /locations/{location_id}/menu/metadata](https://tote.redocly.app/online-ordering/spec/openapi/menus/getmenumetadata.md): Returns lightweight metadata about a location's menu, including the last modification timestamp and a version hash. Use case: Poll this endpoint to detect menu changes without downloading the full menu payload. Compare the version_hash with your cached version -- if they differ, re-fetch the full menu. Recommended polling interval: Every 5 minutes during business hours, every 30 minutes outside business hours. ## Carts Shopping cart creation, item management, pricing, and checkout. ### Create a new cart - [POST /carts](https://tote.redocly.app/online-ordering/spec/openapi/carts/createcart.md): Creates an empty cart for the specified location. The cart is initialized with ACTIVE status and zero totals. Items are added via POST /carts/{cart_id}/items. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### Get cart details - [GET /carts/{cart_id}](https://tote.redocly.app/online-ordering/spec/openapi/carts/getcart.md): Retrieves the current state of a cart, including all items, computed totals, and status. Returns the cart with server-computed subtotal, total_tax, total_discount, and total. ### Abandon a cart - [DELETE /carts/{cart_id}](https://tote.redocly.app/online-ordering/spec/openapi/carts/deletecart.md): Marks a cart as ABANDONED. This is a soft delete -- the cart data is retained but can no longer be modified or checked out. Only ACTIVE carts can be abandoned. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### Update cart properties - [PATCH /carts/{cart_id}](https://tote.redocly.app/online-ordering/spec/openapi/carts/updatecart.md): Partially updates a cart's properties. Currently supports updating the customer_id field. The cart must be in ACTIVE status. Customer identity: Setting customer_id enables the server to apply member-specific pricing on the next /calculate call. Setting customer_id to null reverts to anonymous pricing. After changing customer_id, re-calculate prices -- previous pricing may be stale. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. ### Add an item to the cart - [POST /carts/{cart_id}/items](https://tote.redocly.app/online-ordering/spec/openapi/carts/addcartitem.md): Adds a menu item with optional modifier selections to the cart. The server validates that the menu item exists, is available, and that modifier selections satisfy the min_selections/max_selections constraints of each modifier group. Age-restricted items can be added freely. The cart's age_verification_required flag will be set to true, and the consumer will be informed that verification is required at pickup or delivery. The response includes the updated cart with recalculated totals. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### Update a cart item - [PUT /carts/{cart_id}/items/{item_id}](https://tote.redocly.app/online-ordering/spec/openapi/carts/updatecartitem.md): Updates the quantity and/or modifier selections of an existing cart item. The full CartItemRequest must be provided (this is a full replacement, not a patch). The server re-validates modifier constraints against the current menu. The response includes the updated cart with recalculated totals. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### Remove an item from the cart - [DELETE /carts/{cart_id}/items/{item_id}](https://tote.redocly.app/online-ordering/spec/openapi/carts/removecartitem.md): Removes a specific item from the cart. The response includes the updated cart with recalculated totals. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### Calculate cart prices - [POST /carts/{cart_id}/calculate](https://tote.redocly.app/online-ordering/spec/openapi/carts/calculatecart.md): Returns an itemized price breakdown for the cart without creating or modifying an order. This is a read-only preview -- it computes prices at request time based on the current menu, tax configuration, and applicable discounts. Important: Prices may change between calculate and checkout if the menu or tax configuration is updated. Always call calculate immediately before checkout to show the customer the most accurate total. This endpoint is analogous to Square's CalculateOrder and Toast's /prices endpoint. ### Check out a cart - [POST /carts/{cart_id}/checkout](https://tote.redocly.app/online-ordering/spec/openapi/carts/checkoutcart.md): Converts an ACTIVE cart into an order. The cart must have at least one item and a handoff mode must be available (either stored on the cart via PUT /carts/{cart_id}/handoff, or provided as an override in the request body). Validation performed at checkout: - Cart is in ACTIVE status - Cart has at least one item - All items are still available in the current menu - Modifier selections satisfy group constraints (min/max) - Handoff mode exists (stored on cart OR provided in request body) Age verification: If the cart contains age-restricted items, checkout succeeds but the response includes age_verification_required: true and an age_verification_notice explaining that the consumer will be verified at pickup or delivery. Price changes: If expected_total is provided and the current total differs, the server returns 409 Conflict. Omit expected_total to accept the current price. After successful checkout, the cart status changes to CHECKED_OUT and can no longer be modified. The created order is returned with status PENDING. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. If the cart has already been checked out with the same idempotency key, the original order is returned. ### Set or update the cart's handoff mode - [PUT /carts/{cart_id}/handoff](https://tote.redocly.app/online-ordering/spec/openapi/carts/setcarthandoff.md): Sets how the customer wants to receive their order. The handoff mode is stored on the cart and used at checkout. Can be called multiple times to change the mode before checkout (e.g., customer switches from PICKUP to DELIVERY). The response returns the updated cart with the stored handoff mode. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### Apply a promo code to a cart - [POST /carts/{cart_id}/promo-codes](https://tote.redocly.app/online-ordering/spec/openapi/carts/applypromocode.md): Applies a promo code to the cart. The server validates the code, checks eligibility (expiry, minimum order amount, applicable items), and applies the discount. Returns the updated Cart with the promo in the promo_codes array and the discount reflected in totals. One promo at a time: Only one promo code can be active on a cart. If a promo code is already applied, the server returns 409 Conflict. Remove the existing code first via DELETE /carts/{cart_id}/promo-codes/{code}. Case-insensitive: Codes are case-insensitive on input. "summer25" and "SUMMER25" are treated identically. The code is always returned as uppercase. Discount visibility: The promo discount appears in the PriceCalculation discounts array as a DiscountLineItem with source: PROMO_CODE. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ### List promo codes on a cart - [GET /carts/{cart_id}/promo-codes](https://tote.redocly.app/online-ordering/spec/openapi/carts/listpromocodes.md): Returns the promo codes currently applied to the cart. This information is also included in the Cart response, but this endpoint is useful for checking promo status without fetching the full cart. ### Validate a promo code without applying - [GET /carts/{cart_id}/promo-codes/validate](https://tote.redocly.app/online-ordering/spec/openapi/carts/validatepromocode.md): Checks if a promo code is valid for this cart without applying it. Returns a discount preview (estimated amount, description, applicable items) for valid codes, or a rejection reason and message for invalid codes. Use this endpoint to show the customer what discount they would receive before they commit to applying the code. Note: The discount preview is an estimate. The actual discount is computed when the code is applied via POST /carts/{cart_id}/promo-codes. ### Remove a promo code from a cart - [DELETE /carts/{cart_id}/promo-codes/{code}](https://tote.redocly.app/online-ordering/spec/openapi/carts/removepromocode.md): Removes the specified promo code from the cart. The associated discount is removed and totals are recalculated. Returns the updated Cart with the promo removed from promo_codes and the discount removed from totals. The code path parameter is case-insensitive -- the server normalizes it before matching. Idempotency: This endpoint supports idempotent requests. Include an Idempotency-Key header (UUID v4) to safely retry requests. Successful responses are cached for 24 hours; error responses are not cached. ## Orders Order retrieval, fulfillment tracking, and cancellation. ### List orders - [GET /orders](https://tote.redocly.app/online-ordering/spec/openapi/orders/listorders.md): Returns a paginated list of orders accessible to your API credentials. Results are ordered by creation date (newest first). Pagination: Results use cursor-based pagination. Use the cursor parameter from the pagination.next_cursor field to retrieve the next page. Filtering: Combine filters to narrow results. All filters are optional and applied with AND logic: - status: Filter by order lifecycle status (e.g., CONFIRMED, COMPLETED). - fulfillment_status: Filter by fulfillment stage (e.g., PREPARING, READY_FOR_PICKUP). - location_id: Filter by store location. - customer_id: Filter by customer identifier (exact match). - date_from / date_to: Filter by creation date range. Response format: Returns OrderSummary objects (lightweight, no items or payments arrays). Use GET /orders/{order_id} for the full order detail. ### Get order details - [GET /orders/{order_id}](https://tote.redocly.app/online-ordering/spec/openapi/orders/getorder.md): Returns the full order resource including line items, payments, all three status fields, financial totals, and handoff details. Status fields: - status: Order lifecycle (PENDING, CONFIRMED, COMPLETED, etc.) - payment_status: Aggregate payment state (UNPAID, PROCESSING, PARTIALLY_PAID, PAID) - fulfillment_status: Physical fulfillment progress (PENDING through FULFILLED/DELIVERED) Polling for updates: After submitting a payment, poll this endpoint to check the updated payment_status and balance_due. Recommended polling interval: 2 seconds, with exponential backoff up to 30 seconds. Stop polling when payment_status reaches PAID or the payment reaches a terminal state. Payments array: The payments array contains all payments attached to this order, including their individual statuses. Use this to display payment history and determine if additional payments are needed for split-pay scenarios. ### Cancel an order - [POST /orders/{order_id}/cancel](https://tote.redocly.app/online-ordering/spec/openapi/orders/cancelorder.md): Cancels an order that has not yet started preparation. This endpoint is only available when fulfillment_status is PENDING or IN_PROGRESS. Fulfillment restriction: Once the store begins preparing the order (fulfillment_status is PREPARING or later), this endpoint returns 409 Conflict. Cancellation at that stage requires store intervention. Automatic void/refund: When an order is cancelled, the server automatically handles all attached payments: - Payments in PENDING or AUTHORIZED status are voided (no charge). - Payments in CAPTURED or COMPLETED status are refunded in full. - Refunds follow the minimize-cash-refund strategy: loyalty points are refunded first, then gift cards, then credit/debit cards. No separate refund request is needed after cancellation. Idempotency: This endpoint requires the Idempotency-Key header. If the order was already cancelled with the same key, the server returns the current order state without re-processing. Result: The order's status is set to CANCELLED, fulfillment_status is set to CANCELLED, and all payments are voided or refunded. ## Payments Payment submission, split payments, and refunds. ### Submit a payment for an order - [POST /orders/{order_id}/payments](https://tote.redocly.app/online-ordering/spec/openapi/payments/submitpayment.md): Attaches a payment to an order. Supports credit cards, debit cards, gift cards, loyalty points, and digital wallets. Idempotency: This endpoint requires the Idempotency-Key header. If a request with the same key is received within 24 hours, the server returns the cached response without re-processing the payment. Error responses are NOT cached. Split payments: To pay with multiple tenders, submit separate payment requests for each tender. Each request must use a different Idempotency-Key. Ordering rules: Submit tenders in this order: loyalty points first, then gift cards, then credit/debit cards. This maximizes non-cash tender redemption. Sequential submission: Wait for each payment to reach a terminal state (COMPLETED or FAILED) before submitting the next. Concurrent submissions on the same order may produce undefined behavior. Failure handling for split payments: If a payment fails mid-sequence, previous successful payments remain. You must either: (a) retry the failed tender with the same Idempotency-Key, (b) submit a different tender for the remaining balance, or (c) cancel the order (which triggers void/refund of all successful payments). ### Refund an order (full or partial) - [POST /orders/{order_id}/refunds](https://tote.redocly.app/online-ordering/spec/openapi/payments/createrefund.md): Processes a full or partial refund for an order. The server determines how to distribute the refund across the order's payment methods using a minimize-cash-refund strategy (non-cash tenders are refunded first). Full refund: Set amount to the order's total paid amount. Partial refund: Set amount to the desired refund amount. Item-level refund: Include line_items to record which items are being refunded (informational -- the amount field determines the refund total). The refund amount must not exceed the order's refundable balance (amount_paid - amount_refunded). Multiple partial refunds can be issued until the full amount has been refunded. ## Webhook Subscriptions Webhook subscription management and event delivery. ### Create a webhook subscription - [POST /webhooks](https://tote.redocly.app/online-ordering/spec/openapi/webhook-subscriptions/createwebhooksubscription.md): Register an HTTPS endpoint to receive webhook events. You can create multiple subscriptions to route different event types to different URLs. Signing secret: The response includes a signing_secret field that is ONLY returned in this response. Store it securely -- it cannot be retrieved again. Use it to verify the X-Tote-Signature header on incoming webhook deliveries. Idempotency: This endpoint requires the Idempotency-Key header. If a request with the same key is received within 24 hours, the server returns the cached response without creating a duplicate subscription. ### List webhook subscriptions - [GET /webhooks](https://tote.redocly.app/online-ordering/spec/openapi/webhook-subscriptions/listwebhooksubscriptions.md): Returns all webhook subscriptions for the authenticated partner. Results are ordered by creation date (newest first). Note: The signing_secret field is NOT included in list responses. It is only returned when a subscription is first created via POST. Pagination: Results use cursor-based pagination. Use the cursor parameter from the pagination.next_cursor field to retrieve the next page. ### Delete a webhook subscription - [DELETE /webhooks/{webhook_id}](https://tote.redocly.app/online-ordering/spec/openapi/webhook-subscriptions/deletewebhooksubscription.md): Permanently removes a webhook subscription. Events will no longer be delivered to the subscription URL. This action cannot be undone. After deletion, any in-flight deliveries for this subscription may still arrive at the endpoint. Your webhook handler should gracefully ignore events for unknown subscriptions. ## Inventory Item availability and stock status. ### Get item availability for a location - [GET /locations/{location_id}/inventory](https://tote.redocly.app/online-ordering/spec/openapi/inventory/getlocationinventory.md): Returns the current availability status for all items at a location. Use this endpoint to check stock before adding items to a cart, or as a polling fallback for the stock.updated webhook event. Items not returned in this response are not available at this location. Pagination: Results use cursor-based pagination. Use the cursor parameter from the pagination.next_cursor field to retrieve the next page. ## Events Webhook event payloads delivered to your subscription URLs. ### Order created event - [POST order-created](https://tote.redocly.app/online-ordering/spec/openapi/events/ordercreatedevent.md): Sent when a new order is placed via checkout. Contains minimal order data -- fetch the full order via GET /orders/{order_id}. ### Order status changed event - [POST order-status-changed](https://tote.redocly.app/online-ordering/spec/openapi/events/orderstatuschangedevent.md): Sent when any of the order's three status fields change (lifecycle status, fulfillment status, or payment status). Includes previous and current values for all three fields. ### Order cancelled event - [POST order-cancelled](https://tote.redocly.app/online-ordering/spec/openapi/events/ordercancelledevent.md): Sent when an order is cancelled. Includes cancellation reason if provided. Any pending payments are voided and completed payments are refunded. ### Stock updated event - [POST stock-updated](https://tote.redocly.app/online-ordering/spec/openapi/events/stockupdatedevent.md): Sent when item availability changes at a location. A single event can contain multiple items. Use GET /locations/{location_id}/inventory as a polling fallback. ### Menu changed event - [POST menu-changed](https://tote.redocly.app/online-ordering/spec/openapi/events/menuchangedevent.md): Sent when a location's menu is modified. Re-fetch the full menu via GET /locations/{location_id}/menu. Use version_hash to avoid unnecessary re-fetches. ### Location hours changed event - [POST location-hours-changed](https://tote.redocly.app/online-ordering/spec/openapi/events/locationhourschangedevent.md): Sent when a location's business hours are modified. Includes both previous and current hours for comparison.