# 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](mailto:developer@totepos.com) 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. ```bash 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):** ```json { "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. ```bash curl https://sandbox.api.tote.ai/v1/online-ordering/locations \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" ``` **Response (200 OK):** ```json { "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: ```bash 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. ```bash 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):** ```json { "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: | Field | Description | | --- | --- | | `version_hash` | Opaque hash for cache invalidation. Compare with your cached value to detect changes. | | `categories[].items[]` | Menu items within each category. | | `base_price.amount` | Price in cents. `899` means $8.99. | | `modifier_groups` | Customization options for each item (e.g., bread choice, toppings). | ## API Conventions The Tote Online Ordering API follows consistent conventions across all endpoints. ### Naming | Element | Convention | Example | | --- | --- | --- | | JSON fields | `snake_case` | `location_id`, `base_price`, `access_token` | | URL paths | `kebab-case` | `/auth/token`, `/locations/{location_id}/menu` | | Enum values | `SCREAMING_SNAKE_CASE` | `CLIENT_CREDENTIALS`, `MONDAY`, `BEARER` | | Schema names | `PascalCase` | `TokenResponse`, `MenuItem`, `ModifierGroup` | ### Data Formats | Format | Convention | Example | | --- | --- | --- | | Resource IDs | UUID v4 | `"a1b2c3d4-e5f6-7890-abcd-ef1234567890"` | | Timestamps | ISO 8601 UTC | `"2026-01-15T14:30:00Z"` | | Monetary values | Integer cents (USD) | `899` = $8.99 | | Phone numbers | E.164 | `"+15125551234"` | | Timezones | IANA identifiers | `"America/Chicago"` | ### Pagination List endpoints use **cursor-based pagination**: ```bash # First page (default 20 results) curl ".../locations" # Next page curl ".../locations?cursor=eyJpZCI6...&limit=50" ``` The response includes a `pagination` object: ```json { "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: ```json { "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 } } ``` | Field | Description | | --- | --- | | `code` | Machine-readable error category for programmatic handling. | | `message` | Human-readable description. | | `detail` | Additional context and resolution suggestions. | | `request_id` | Include this when contacting support. | | `field` | JSON pointer to the problematic field, or `null` for non-field errors. | **Error categories:** | Code | HTTP Status | When | | --- | --- | --- | | `AUTHENTICATION_ERROR` | 401, 403 | Invalid/expired token or insufficient permissions. | | `INVALID_REQUEST_ERROR` | 400 | Malformed request or validation failure. | | `NOT_FOUND_ERROR` | 404 | Resource does not exist. | | `RATE_LIMIT_ERROR` | 429 | Too many requests. Check the `Retry-After` header. | | `CONFLICT_ERROR` | 409 | Resource conflict (e.g., duplicate idempotency key). | | `INTERNAL_ERROR` | 500 | Server error. Retry with exponential backoff. | ## Next Steps - [Authentication Guide](/online-ordering/guides/02-authentication) -- Token lifecycle, caching strategy, and security best practices. - [Menu Synchronization Guide](/online-ordering/guides/03-menu-sync) -- Strategies for keeping your menu data current. - [Modifiers Deep-Dive](/online-ordering/guides/05-modifiers) -- Understanding nested modifier groups with worked examples.