Skip to content
Last updated

Getting Started with the Tote Online Ordering API

The Tote Online Ordering API lets third-party developers build online ordering experiences for convenience stores and fuel stations running Tote POS. With this API you can authenticate, browse store locations and menus, create carts, submit payments, and track order fulfillment.

This guide walks you through your first three API calls: authenticating, listing locations, and retrieving a menu.

Prerequisites

Before you begin, make sure you have:

  • Tote partner account -- Contact us to sign up as a partner.
  • Base URL -- Your API base URL is provided during partner onboarding. Each partner receives a dedicated endpoint.
  • Client ID and Client Secret -- Your API credentials are provided during partner onboarding alongside your base URL.
  • curl (or any HTTP client) -- All examples in this guide use curl.
  • HTTPS only -- All API requests must use HTTPS. HTTP requests are rejected.

Base URL

Important: The URLs shown throughout these docs (e.g., https://sandbox.api.tote.ai/v1/online-ordering) are placeholders. Your actual base URL, client ID, and client secret will be provided when you complete partner onboarding. Replace the example URLs in all code samples with your assigned base URL.

Use the sandbox environment for development and testing. All examples in this guide use a sandbox-style URL.

Step 1: Authenticate

Exchange your client credentials for an access token using the OAuth 2.0 client credentials flow.

curl -X POST https://sandbox.api.tote.ai/v1/online-ordering/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "CLIENT_CREDENTIALS",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET"
  }'

Response (200 OK):

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "BEARER",
  "expires_in": 86400
}

The access_token is valid for 24 hours (86,400 seconds). Save it -- you will include it in every subsequent request.

Tip: Cache your token and reuse it until it expires. Do not request a new token for every API call or you will trigger rate limiting.

Step 2: List Locations

Retrieve the locations accessible to your API credentials. Each location represents a physical store.

curl https://sandbox.api.tote.ai/v1/online-ordering/locations \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response (200 OK):

{
  "data": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "QuickStop #247",
      "address": {
        "street": "4521 Congress Ave",
        "city": "Austin",
        "state": "TX",
        "postal_code": "78745",
        "country": "US"
      },
      "phone": "+15125551234",
      "timezone": "America/Chicago",
      "online_ordering_enabled": true,
      "business_hours": [
        { "day_of_week": "MONDAY", "open_time": "06:00", "close_time": "23:00", "is_closed": false },
        { "day_of_week": "TUESDAY", "open_time": "06:00", "close_time": "23:00", "is_closed": false }
      ]
    }
  ],
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJpZCI6ImIyYzNkNGU1LWY2YTctODkwMS1iY2RlLWYxMjM0NTY3ODkwMSJ9"
  }
}

Use the online_ordering_enabled field to filter locations that accept online orders. To fetch the next page, pass the next_cursor value as a query parameter:

curl "https://sandbox.api.tote.ai/v1/online-ordering/locations?cursor=eyJpZCI6..." \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Step 3: Get a Location's Menu

Retrieve the full menu for a specific location. The menu includes categories, items, and nested modifier groups.

curl https://sandbox.api.tote.ai/v1/online-ordering/locations/a1b2c3d4-e5f6-7890-abcd-ef1234567890/menu \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response (200 OK):

{
  "location_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "last_modified": "2026-01-15T14:30:00Z",
  "version_hash": "sha256:a1b2c3d4e5f6",
  "categories": [
    {
      "id": "c0000001-0000-0000-0000-000000000001",
      "name": "Deli & Sandwiches",
      "description": "Fresh-made sandwiches and deli items.",
      "sort_order": 1,
      "items": [
        {
          "id": "d0000001-0000-0000-0000-000000000001",
          "name": "Build Your Own Sub Sandwich",
          "description": "Choose your bread, protein, and toppings.",
          "base_price": { "amount": 899, "currency": "USD" },
          "available": true,
          "modifier_groups": [ "..." ]
        }
      ]
    }
  ]
}

Key fields in the menu response:

FieldDescription
version_hashOpaque hash for cache invalidation. Compare with your cached value to detect changes.
categories[].items[]Menu items within each category.
base_price.amountPrice in cents. 899 means $8.99.
modifier_groupsCustomization options for each item (e.g., bread choice, toppings).

API Conventions

The Tote Online Ordering API follows consistent conventions across all endpoints.

Naming

ElementConventionExample
JSON fieldssnake_caselocation_id, base_price, access_token
URL pathskebab-case/auth/token, /locations/{location_id}/menu
Enum valuesSCREAMING_SNAKE_CASECLIENT_CREDENTIALS, MONDAY, BEARER
Schema namesPascalCaseTokenResponse, MenuItem, ModifierGroup

Data Formats

FormatConventionExample
Resource IDsUUID v4"a1b2c3d4-e5f6-7890-abcd-ef1234567890"
TimestampsISO 8601 UTC"2026-01-15T14:30:00Z"
Monetary valuesInteger cents (USD)899 = $8.99
Phone numbersE.164"+15125551234"
TimezonesIANA identifiers"America/Chicago"

Pagination

List endpoints use cursor-based pagination:

# First page (default 20 results)
curl ".../locations"

# Next page
curl ".../locations?cursor=eyJpZCI6...&limit=50"

The response includes a pagination object:

{
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJpZCI6..."
  }
}

When has_more is false, you have reached the last page.

Error Handling

All errors follow a consistent envelope format:

{
  "error": {
    "code": "AUTHENTICATION_ERROR",
    "message": "Invalid or expired access token.",
    "detail": "The provided Bearer token has expired. Request a new token via POST /auth/token.",
    "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "field": null
  }
}
FieldDescription
codeMachine-readable error category for programmatic handling.
messageHuman-readable description.
detailAdditional context and resolution suggestions.
request_idInclude this when contacting support.
fieldJSON pointer to the problematic field, or null for non-field errors.

Error categories:

CodeHTTP StatusWhen
AUTHENTICATION_ERROR401, 403Invalid/expired token or insufficient permissions.
INVALID_REQUEST_ERROR400Malformed request or validation failure.
NOT_FOUND_ERROR404Resource does not exist.
RATE_LIMIT_ERROR429Too many requests. Check the Retry-After header.
CONFLICT_ERROR409Resource conflict (e.g., duplicate idempotency key).
INTERNAL_ERROR500Server error. Retry with exponential backoff.

Next Steps