SmartQ.tv SmartQ.tv
Developer API Examples

Representative Response Examples

These are representative response-shape examples. They intentionally use placeholder values such as GROUP_ID, LOCATION_ID, ISO_TIMESTAMP, STATUS_STRING, and venue-neutral IDs or timestamps instead of venue-specific data.

Authentication

Obtain and reuse a 24-hour access token

Run the exchange only from a trusted server. Cache the access token and renew it several minutes before its fixed 86,400-second expiry. No refresh token is issued, and an application revocation invalidates outstanding tokens immediately.

cURL

curl --request POST https://api.smartq.tv/oauth/token \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=YOUR_CLIENT_ID" \
  --data-urlencode "client_secret=YOUR_CLIENT_SECRET"

Node.js / JavaScript

const tokenResponse = await fetch("https://api.smartq.tv/oauth/token", {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "client_credentials",
    client_id: process.env.SMARTQ_CLIENT_ID,
    client_secret: process.env.SMARTQ_CLIENT_SECRET,
  }),
});
if (!tokenResponse.ok) throw new Error(`Token request failed: ${tokenResponse.status}`);
const { access_token, expires_in } = await tokenResponse.json();
const response = await fetch("https://api.smartq.tv/v1/GROUP_ID/locations", {
  headers: { Authorization: `Bearer ${access_token}` },
});

Python

import os
import requests

token_response = requests.post(
    "https://api.smartq.tv/oauth/token",
    data={
        "grant_type": "client_credentials",
        "client_id": os.environ["SMARTQ_CLIENT_ID"],
        "client_secret": os.environ["SMARTQ_CLIENT_SECRET"],
    },
    timeout=10,
)
token_response.raise_for_status()
access_token = token_response.json()["access_token"]
response = requests.get(
    "https://api.smartq.tv/v1/GROUP_ID/locations",
    headers={"Authorization": f"Bearer {access_token}"},
    timeout=10,
)

PHP

$tokenRequest = curl_init("https://api.smartq.tv/oauth/token");
curl_setopt_array($tokenRequest, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Content-Type: application/x-www-form-urlencoded"],
    CURLOPT_POSTFIELDS => http_build_query([
        "grant_type" => "client_credentials",
        "client_id" => getenv("SMARTQ_CLIENT_ID"),
        "client_secret" => getenv("SMARTQ_CLIENT_SECRET"),
    ]),
]);
$token = json_decode(curl_exec($tokenRequest), true)["access_token"];

Successful token JSON contains access_token, token_type: Bearer, expires_in: 86400, and the current optional scope string. Token endpoint errors are invalid_request, unsupported_grant_type, generic invalid_client, or rate-limited temporarily_unavailable.

locations:read

Discovery Example

{
  "ok": true,
  "version": "v1",
  "resource": "locations",
  "groupId": "GROUP_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "token": {
    "scopeType": "group_or_location",
    "scopes": ["locations:read", "shiftq:read", "matriq:read"],
    "rateLimitPerMinute": "NUMBER",
    "expiresAt": "APPLICATION_EXPIRY_OR_NULL",
    "accessTokenExpiresAt": "ACCESS_TOKEN_EXPIRY_OR_NULL_FOR_LEGACY",
    "credentialType": "client_credentials_or_legacy_bearer",
    "status": "active_or_revoked_or_expired",
    "allowedLocationIds": ["LOCATION_ID_IF_LOCATION_SCOPED"],
    "tokenPrefix": "LEGACY_TOKEN_PREFIX_OR_EMPTY"
  },
  "data": {
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "REFRESH_DESCRIPTION"
    },
    "locations": [
      {
        "id": "LOCATION_ID",
        "name": "LOCATION_NAME",
        "locationShortName": "LOCATION_SHORT_NAME",
        "timezone": "IANA_TIMEZONE",
        "slug": "LOCATION_SLUG",
        "status": "STATUS_STRING",
        "modulesEnabled": {
          "shiftq": "BOOLEAN",
          "sportsq": "BOOLEAN",
          "matriq": "BOOLEAN",
          "campaigns": "BOOLEAN",
          "orderboard": "BOOLEAN"
        },
        "resources": ["RESOURCE_NAME"],
        "endpoints": {
          "root": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID",
          "modules": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/modules",
          "shiftq": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/shiftq",
          "sportsq": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/sportsq",
          "matriq": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/matriq"
        }
      }
    ]
  }
}
shiftq:read

ShiftQ Overview Example

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "sales": { "cadence": "CADENCE_STRING", "description": "SALES_REFRESH_DESCRIPTION" },
      "labor": { "cadence": "CADENCE_STRING", "description": "LABOR_REFRESH_DESCRIPTION" },
      "kitchen": { "cadence": "CADENCE_STRING", "description": "KITCHEN_REFRESH_DESCRIPTION" },
      "posMenu": { "cadence": "CADENCE_STRING", "description": "POS_MENU_REFRESH_DESCRIPTION" },
      "clockIns": { "cadence": "CADENCE_STRING", "description": "CLOCK_IN_REFRESH_DESCRIPTION" },
      "employees": { "cadence": "CADENCE_STRING", "description": "EMPLOYEE_REFRESH_DESCRIPTION" },
      "reservations": { "cadence": "CADENCE_STRING", "description": "RESERVATION_REFRESH_DESCRIPTION" }
    },
    "summary": {
      "businessDate": "YYYYMMDD",
      "salesUpdatedAt": "ISO_TIMESTAMP",
      "laborUpdatedAt": "ISO_TIMESTAMP",
      "top5Leaderboard": [
        { "employeeId": "EMPLOYEE_ID", "employeeName": "EMPLOYEE_NAME", "metric": "METRIC_NAME", "value": "NUMBER", "rank": "NUMBER" }
      ],
      "clockedInCount": "NUMBER",
      "soldOutItemCount": "NUMBER",
      "exceptionCounts": {
        "wrongJobCodeClockIns": "NUMBER",
        "scheduledButMissing": "NUMBER",
        "clockedInButNotScheduled": "NUMBER",
        "earlyClockIns": "NUMBER",
        "lateClockIns": "NUMBER"
      },
      "managerOnDutyCount": "NUMBER",
      "roleGroupCount": "NUMBER",
      "newTeamMemberCount": "NUMBER",
      "reservationDayCount": "NUMBER",
      "reservationPartyCount": "NUMBER",
      "kitchenAvgTicketTimeMinutes": "NUMBER",
      "kitchenExpediterAvgTicketTimeMinutes": "NUMBER",
      "kitchenReadyQueueCount": "NUMBER",
      "kitchenInProgressCount": "NUMBER",
      "kitchenStationCount": "NUMBER"
    }
  }
}
shiftq:read

ShiftQ Kitchen Example

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq/kitchen",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "KITCHEN_REFRESH_DESCRIPTION"
    },
    "currentBusinessDate": "YYYYMMDD",
    "requestedBusinessDate": "YYYYMMDD",
    "bucketAvailable": true,
    "retentionDays": 366,
    "latestMetadata": {
      "updatedAt": "ISO_TIMESTAMP",
      "businessDate": "YYYYMMDD",
      "dateKey": "YYYY-MM-DD",
      "liveBusinessDate": "YYYYMMDD",
      "bucketIntervalMinutes": 15,
      "lastStatus": "ok",
      "lastHttpStatus": 200,
      "lastError": "ERROR_STRING_IF_PRESENT",
      "averageBasis": "ticket_count",
      "avgKitchenTicketTimeMinutes": 11.42,
      "avgKitchenTicketTimeDivisorField": "prepTicketCount",
      "avgKitchenTicketTimeDivisorValue": 37,
      "avgExpediterTicketTimeMinutes": 13.58,
      "avgExpediterTicketTimeDivisorField": "expediterTicketCount",
      "avgExpediterTicketTimeDivisorValue": 31,
      "avgExpoClearMinutes": 3.1,
      "prepTicketCount": 37,
      "prepRowCount": 88,
      "expediterTicketCount": 31,
      "expediterRowCount": 75,
      "expediterLevelCount": 1,
      "stationCount": 3,
      "salesCategoryCount": 2,
      "timeBucketCount": 6,
      "thirtyMinuteBucketCount": 6
    },
    "currentSnapshot": {
      "businessDate": "YYYYMMDD",
      "dateKey": "YYYY-MM-DD",
      "metricsBusinessDate": "YYYYMMDD",
      "metricsDateKey": "YYYY-MM-DD",
      "timezone": "IANA_TIMEZONE",
      "updatedAt": "ISO_TIMESTAMP",
      "dayStartMs": 1753632060000,
      "selectionMode": "prepStations",
      "trackedPrepStations": ["PREP_STATION_GUID"],
      "trackAllPrepStations": false,
      "availablePrepStations": [
        {
          "id": "PREP_STATION_GUID",
          "name": "Grill",
          "includeWithExpediter": false,
          "expoRouting": "EXPO"
        }
      ],
      "trackedSalesCategories": ["FOOD"],
      "trackAllSalesCategories": false,
      "availableSalesCategories": ["FOOD", "NA BEVERAGES"],
      "queue": {
        "inProgressCount": 4,
        "readyQueueCount": 2,
        "oldestReadyMinutes": 6
      },
      "averages": {
        "averageBasis": "ticket_count",
        "avgKitchenTicketTimeMinutes": 11.42,
        "avgKitchenTicketTimeSource": "prep_station_ticket_average",
        "avgKitchenTicketTimeDivisorField": "counts.prepTicketCount",
        "avgKitchenTicketTimeDivisorValue": 37,
        "avgExpediterTicketTimeMinutes": 13.58,
        "avgExpediterTicketTimeSource": "first_level_expediter_ticket_average",
        "avgExpediterTicketTimeDivisorField": "counts.expediterTicketCount",
        "avgExpediterTicketTimeDivisorValue": 31,
        "avgExpoClearMinutes": 3.1
      },
      "counts": {
        "clearedTicketCount": 33,
        "prepTicketCount": 37,
        "prepRowCount": 88,
        "sourceTicketCount": 24,
        "expediterTicketCount": 31,
        "expediterRowCount": 75,
        "expediterSourceTicketCount": 31,
        "expediterLevelCount": 1,
        "stationCount": 3,
        "salesCategoryCount": 2,
        "timeBucketCount": 6,
        "thirtyMinuteBucketCount": 6
      },
      "expediterTicketAverages": [
        {
          "fulfillmentLevel": 1,
          "stationName": "Expediter Level 1",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 31,
          "averageTicketTimeMinutes": 13.58,
          "ticketCount": 31,
          "rowCount": 75,
          "minTicketTimeMinutes": 5.2,
          "maxTicketTimeMinutes": 21.6
        }
      ],
      "stationTicketAverages": [
        {
          "stationGuid": "PREP_STATION_GUID",
          "stationName": "Grill",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 12,
          "averageTicketTimeMinutes": 10.67,
          "ticketCount": 12,
          "rowCount": 24,
          "minTicketTimeMinutes": 4.1,
          "maxTicketTimeMinutes": 17.8
        }
      ],
      "salesCategoryTicketAverages": [
        {
          "salesCategory": "FOOD",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 37,
          "averageTicketTimeMinutes": 11.42,
          "ticketCount": 37,
          "rowCount": 88,
          "minTicketTimeMinutes": 3.9,
          "maxTicketTimeMinutes": 19.2
        }
      ],
      "timeBuckets": [
        {
          "bucketStartMs": 1753632000000,
          "bucketEndMs": 1753632900000,
          "bucketStartIso": "ISO_TIMESTAMP",
          "bucketEndIso": "ISO_TIMESTAMP",
          "bucketLabel": "LOCAL_15_MINUTE_RANGE",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 9,
          "averageTicketTimeMinutes": 10.8,
          "ticketCount": 9,
          "rowCount": 21,
          "minTicketTimeMinutes": 4.6,
          "maxTicketTimeMinutes": 17.4,
          "stationTicketAverages": [
            {
              "stationGuid": "PREP_STATION_GUID",
              "stationName": "Grill",
              "averageBasis": "ticket_count",
              "averageDivisorField": "ticketCount",
              "averageDivisorValue": 4,
              "averageTicketTimeMinutes": 10.12,
              "ticketCount": 4,
              "rowCount": 8,
              "minTicketTimeMinutes": 4.8,
              "maxTicketTimeMinutes": 14.5
            }
          ],
          "expediterTicketAverages": [
            {
              "fulfillmentLevel": 1,
              "stationName": "Expediter Level 1",
              "averageBasis": "ticket_count",
              "averageDivisorField": "ticketCount",
              "averageDivisorValue": 8,
              "averageTicketTimeMinutes": 12.4,
              "ticketCount": 8,
              "rowCount": 19,
              "minTicketTimeMinutes": 5.1,
              "maxTicketTimeMinutes": 17.2
            }
          ],
          "salesCategoryTicketAverages": [
            {
              "salesCategory": "FOOD",
              "averageBasis": "ticket_count",
              "averageDivisorField": "ticketCount",
              "averageDivisorValue": 9,
              "averageTicketTimeMinutes": 10.8,
              "ticketCount": 9,
              "rowCount": 21,
              "minTicketTimeMinutes": 4.6,
              "maxTicketTimeMinutes": 17.4
            }
          ]
        }
      ],
      "bucketIntervalMinutes": 15,
      "status": {
        "state": "ok",
        "httpStatus": 200,
        "error": "ERROR_STRING_IF_PRESENT"
      },
      "retentionDays": 366
    },
    "requestedBucket": {
      "businessDate": "YYYYMMDD",
      "dateKey": "YYYY-MM-DD",
      "metricsBusinessDate": "YYYYMMDD",
      "metricsDateKey": "YYYY-MM-DD",
      "timezone": "IANA_TIMEZONE",
      "updatedAt": "ISO_TIMESTAMP",
      "dayStartMs": 1753545660000,
      "selectionMode": "prepStations",
      "trackedPrepStations": ["PREP_STATION_GUID"],
      "trackAllPrepStations": false,
      "availablePrepStations": [
        {
          "id": "PREP_STATION_GUID",
          "name": "Grill",
          "includeWithExpediter": false,
          "expoRouting": "EXPO"
        }
      ],
      "trackedSalesCategories": ["FOOD"],
      "trackAllSalesCategories": false,
      "availableSalesCategories": ["FOOD", "NA BEVERAGES"],
      "queue": {
        "inProgressCount": 3,
        "readyQueueCount": 1,
        "oldestReadyMinutes": 4
      },
      "averages": {
        "averageBasis": "ticket_count",
        "avgKitchenTicketTimeMinutes": 10.94,
        "avgKitchenTicketTimeSource": "prep_station_ticket_average",
        "avgKitchenTicketTimeDivisorField": "counts.prepTicketCount",
        "avgKitchenTicketTimeDivisorValue": 32,
        "avgExpediterTicketTimeMinutes": 12.71,
        "avgExpediterTicketTimeSource": "first_level_expediter_ticket_average",
        "avgExpediterTicketTimeDivisorField": "counts.expediterTicketCount",
        "avgExpediterTicketTimeDivisorValue": 27,
        "avgExpoClearMinutes": 2.8
      },
      "counts": {
        "clearedTicketCount": 29,
        "prepTicketCount": 32,
        "prepRowCount": 74,
        "sourceTicketCount": 21,
        "expediterTicketCount": 27,
        "expediterRowCount": 63,
        "expediterSourceTicketCount": 27,
        "expediterLevelCount": 1,
        "stationCount": 3,
        "salesCategoryCount": 2,
        "timeBucketCount": 5,
        "thirtyMinuteBucketCount": 5
      },
      "expediterTicketAverages": [
        {
          "fulfillmentLevel": 1,
          "stationName": "Expediter Level 1",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 27,
          "averageTicketTimeMinutes": 12.71,
          "ticketCount": 27,
          "rowCount": 63,
          "minTicketTimeMinutes": 5,
          "maxTicketTimeMinutes": 19.3
        }
      ],
      "stationTicketAverages": [
        {
          "stationGuid": "PREP_STATION_GUID",
          "stationName": "Grill",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 10,
          "averageTicketTimeMinutes": 10.11,
          "ticketCount": 10,
          "rowCount": 20,
          "minTicketTimeMinutes": 4,
          "maxTicketTimeMinutes": 16.6
        }
      ],
      "salesCategoryTicketAverages": [
        {
          "salesCategory": "FOOD",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 32,
          "averageTicketTimeMinutes": 10.94,
          "ticketCount": 32,
          "rowCount": 74,
          "minTicketTimeMinutes": 3.8,
          "maxTicketTimeMinutes": 18.7
        }
      ],
      "timeBuckets": [
        {
          "bucketStartMs": 1753545600000,
          "bucketEndMs": 1753546500000,
          "bucketStartIso": "ISO_TIMESTAMP",
          "bucketEndIso": "ISO_TIMESTAMP",
          "bucketLabel": "11:00 AM - 11:15 AM",
          "averageBasis": "ticket_count",
          "averageDivisorField": "ticketCount",
          "averageDivisorValue": 7,
          "averageTicketTimeMinutes": 10.3,
          "ticketCount": 7,
          "rowCount": 16,
          "minTicketTimeMinutes": 4.2,
          "maxTicketTimeMinutes": 15.9,
          "stationTicketAverages": [
            {
              "stationGuid": "PREP_STATION_GUID",
              "stationName": "Grill",
              "averageBasis": "ticket_count",
              "averageDivisorField": "ticketCount",
              "averageDivisorValue": 3,
              "averageTicketTimeMinutes": 9.92,
              "ticketCount": 3,
              "rowCount": 6,
              "minTicketTimeMinutes": 4.4,
              "maxTicketTimeMinutes": 13.8
            }
          ],
          "expediterTicketAverages": [
            {
              "fulfillmentLevel": 1,
              "stationName": "Expediter Level 1",
              "averageBasis": "ticket_count",
              "averageDivisorField": "ticketCount",
              "averageDivisorValue": 6,
              "averageTicketTimeMinutes": 11.9,
              "ticketCount": 6,
              "rowCount": 14,
              "minTicketTimeMinutes": 4.8,
              "maxTicketTimeMinutes": 16.1
            }
          ],
          "salesCategoryTicketAverages": [
            {
              "salesCategory": "FOOD",
              "averageBasis": "ticket_count",
              "averageDivisorField": "ticketCount",
              "averageDivisorValue": 7,
              "averageTicketTimeMinutes": 10.3,
              "ticketCount": 7,
              "rowCount": 16,
              "minTicketTimeMinutes": 4.2,
              "maxTicketTimeMinutes": 15.9
            }
          ]
        }
      ],
      "bucketIntervalMinutes": 15,
      "status": {
        "state": "ok",
        "httpStatus": 200,
        "error": "ERROR_STRING_IF_PRESENT"
      },
      "retentionDays": 366
    },
    "endpoints": {
      "current": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/shiftq/kitchen",
      "byBusinessDate": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/shiftq/kitchen?businessDate=YYYYMMDD"
    }
  }
}

Compatibility note: live ShiftQ kitchen responses also return thirtyMinuteBuckets as a legacy alias of timeBuckets while existing consumers migrate to the 15-minute bucket contract. Historical reads accept both businessDate=YYYYMMDD and businessDay=YYYYMMDD, using the local 4:01 AM through 4:00 AM business day. avgExpoClearMinutes is always included in kitchen metadata and bucket records; it returns null when no cleared expo tickets were recorded for that business day.

Kitchen averages are ticket-based. Use averages.avgKitchenTicketTimeDivisorValue with counts.prepTicketCount for the prep average, averages.avgExpediterTicketTimeDivisorValue with counts.expediterTicketCount for the Expediter Level 1 average, and each nested station, sales-category, bucket, and expediter average uses that row's own ticketCount. rowCount is raw Toast item-fulfillment volume, not the averaging divisor.

shiftq:read

ShiftQ Reservations Example

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq/reservations",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "RESERVATION_REFRESH_DESCRIPTION"
    },
    "source": "pos_reservations_digest",
    "reservations": {
      "venueLabel": "VENUE_LABEL",
      "asOf": "ISO_TIMESTAMP",
      "asOfLabel": "DISPLAY_LABEL",
      "dayCount": "NUMBER",
      "totalParties": "NUMBER",
      "totalCovers": "NUMBER",
      "days": [
        {
          "dayKey": "YYYY-MM-DD",
          "label": "DAY_LABEL",
          "parties": "NUMBER",
          "covers": "NUMBER",
          "items": [
            {
              "id": "RESERVATION_ID",
              "displayTime": "TIME_LABEL",
              "guestName": "GUEST_NAME",
              "partySize": "NUMBER",
              "notes": "NOTES_STRING"
            }
          ]
        }
      ]
    }
  }
}
shiftq:read

POS Reservations Alias Example

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq/toastReservations",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "RESERVATION_REFRESH_DESCRIPTION"
    },
    "source": "pos_reservations_digest",
    "reservations": {
      "venueLabel": "VENUE_LABEL",
      "asOf": "ISO_TIMESTAMP",
      "dayCount": "NUMBER",
      "totalParties": "NUMBER",
      "totalCovers": "NUMBER",
      "days": ["DAY_ROW_OBJECT"]
    }
  }
}
shiftq:read

ShiftQ Reviews Example

Provider-neutral canonical reviews with bounded filters and opaque pagination. Reviewer display names are available only to authenticated, location-authorized customers when provider policy permits.

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq/reviews",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "data": {
    "location": { "groupId": "GROUP_ID", "locationId": "LOCATION_ID", "name": "LOCATION_NAME" },
    "capability": { "available": true, "configured": true, "activeProviders": ["google"] },
    "reviews": [{
      "id": "CANONICAL_REVIEW_ID",
      "provider": "google",
      "rating": 5,
      "reviewerDisplayName": "Jane D.",
      "text": "Great visit.",
      "createdAt": "ISO_TIMESTAMP",
      "updatedAt": "ISO_TIMESTAMP",
      "reviewUrl": "HTTPS_PROVIDER_URL",
      "workflow": { "status": "NEW" },
      "response": { "state": "NONE" },
      "attribution": { "provider": "google", "label": "Google", "url": "HTTPS_PROVIDER_URL" }
    }],
    "page": { "limit": 25, "count": 1, "nextPageToken": null, "hasMore": false }
  }
}
shiftq:read

ShiftQ Group Leaderboard Example

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq/group-leaderboard",
  "groupId": "GROUP_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "groupId": "GROUP_ID",
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "GROUP_LEADERBOARD_REFRESH_DESCRIPTION"
    },
    "updatedAt": "ISO_TIMESTAMP",
    "count": "NUMBER",
    "leaderboard": {
      "byNetSales": [
        {
          "locationId": "LOCATION_ID",
          "locationName": "LOCATION_NAME",
          "locationShortName": "LOCATION_SHORT_NAME",
          "netSales": "NUMBER",
          "discountsPercent": "NUMBER",
          "laborPercent": "NUMBER",
          "voidsPercent": "NUMBER"
        }
      ],
      "byLowestLaborPercent": ["SAME_ROW_STRUCTURE"],
      "byLowestDiscountsPercent": ["SAME_ROW_STRUCTURE"],
      "byLowestVoidsPercent": ["SAME_ROW_STRUCTURE"]
    },
    "locationEndpoints": [
      {
        "id": "LOCATION_ID",
        "name": "LOCATION_NAME",
        "shiftq": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/shiftq",
        "sales": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/shiftq/sales"
      }
    ]
  }
}
shiftq:read

ShiftQ POS Menu Example

{
  "ok": true,
  "version": "v1",
  "resource": "shiftq/pos-menu",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "POS_MENU_REFRESH_DESCRIPTION"
    },
    "syncStatus": {
      "lastStatus": "STATUS_STRING",
      "lastCheckedAt": "ISO_TIMESTAMP",
      "lastSyncedAt": "ISO_TIMESTAMP",
      "fullMenuRewrite": "POLICY_STRING",
      "incrementalBaseUpdates": "POLICY_STRING",
      "stockOverlayRefresh": "CADENCE_STRING"
    },
    "menuMetadata": {
      "restaurantGuid": "POS_RESTAURANT_GUID",
      "lastUpdated": "ISO_TIMESTAMP"
    },
    "menuSnapshot": {
      "lastUpdated": "ISO_TIMESTAMP",
      "lastSynced": "ISO_TIMESTAMP"
    },
    "soldOutItems": {
      "count": "NUMBER",
      "items": [
        { "key": "LOCATION_ID:INDEX:ITEM_NAME", "name": "ITEM_NAME" }
      ]
    }
  }
}
sportsq:read

SportsQ Examples

Provider

GET /v1/GROUP_ID/LOCATION_ID/sportsq/provider

{
  "ok": true,
  "version": "v1",
  "resource": "sportsq/provider",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "data": {
    "location": {
      "groupId": "GROUP_ID",
      "locationId": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "availability": {
      "status": "AVAILABLE",
      "requestedDate": "2026-08-31",
      "effectiveDate": "2026-08-31",
      "freshness": "EXACT_DATE",
      "updatedAt": "ISO_TIMESTAMP",
      "providerCount": 1,
      "channelCount": 1,
      "packageCount": 0,
      "viewingOptionCount": 1
    },
    "providers": [
      {
        "id": "linear-provider",
        "name": "Linear Provider",
        "type": "linear",
        "available": true,
        "availabilityState": "AVAILABLE",
        "lineupId": "LINEUP_ID",
        "market": "MARKET_NAME",
        "channels": [
          { "number": "123", "name": "Sports Network", "type": "network" }
        ]
      }
    ],
    "regionalSportsNetworks": [
      {
        "id": "regional-network",
        "name": "Regional Sports Network",
        "channel": "123"
      }
    ]
  }
}

Packages

GET /v1/GROUP_ID/LOCATION_ID/sportsq/packages retains the legacy catalog and adds this canonical projection:

{
  "ok": true,
  "resource": "sportsq/packages",
  "data": {
    "canonical": {
      "location": {
        "groupId": "GROUP_ID",
        "locationId": "LOCATION_ID"
      },
      "availability": {
        "status": "AVAILABLE",
        "requestedDate": "2026-08-31",
        "effectiveDate": "2026-08-31",
        "freshness": "EXACT_DATE"
      },
      "programmingPackage": {
        "id": "base-package",
        "name": "Base Package",
        "imageUrl": "https://api.smartq.tv/v1/assets/sports-logo/sm_ASSET_ID"
      },
      "packages": [
        {
          "id": "sports-addon",
          "name": "Sports Add-on",
          "type": "addon",
          "imageUrl": "https://api.smartq.tv/v1/assets/sports-logo/sm_ASSET_ID",
          "enabled": true
        }
      ],
      "regionalSportsNetworks": []
    }
  }
}

Programming by Date

GET /v1/GROUP_ID/LOCATION_ID/sportsq/YYYY-MM-DD requires a real, zero-padded date and never falls back to another day or location.

Example: GET https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/sportsq/2026-08-31

{
  "ok": true,
  "version": "v1",
  "resource": "sportsq/2026-08-31",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "data": {
    "location": {
      "groupId": "GROUP_ID",
      "locationId": "LOCATION_ID",
      "timezone": "IANA_TIMEZONE"
    },
    "date": "2026-08-31",
    "updatedAt": "ISO_TIMESTAMP",
    "health": {
      "schedule": {
        "status": "AVAILABLE",
        "eventCount": 1
      },
      "providerAvailability": {
        "status": "AVAILABLE",
        "requestedDate": "2026-08-31",
        "effectiveDate": "2026-08-31",
        "freshness": "EXACT_DATE",
        "updatedAt": "ISO_TIMESTAMP",
        "providerCount": 1,
        "channelCount": 1,
        "packageCount": 0,
        "viewingOptionCount": 1
      }
    },
    "events": [
      {
        "id": "SPORTSQ_EVENT_ID",
        "name": "Away Team at Home Team",
        "startsAt": "ISO_TIMESTAMP",
        "status": {
          "state": "live",
          "label": "Live",
          "detail": "Second Period",
          "period": 2,
          "clock": "04:10"
        },
        "sport": { "id": "sport-id", "name": "Sport" },
        "league": { "id": "league-id", "name": "League", "abbreviation": "LG", "imageUrl": "https://api.smartq.tv/v1/assets/sports-logo/sm_ASSET_ID" },
        "participants": [
          {
            "type": "team",
            "id": "team-id",
            "name": "Home Team",
            "role": "home",
            "imageUrl": "https://api.smartq.tv/v1/assets/sports-logo/sm_ASSET_ID",
            "score": "10",
            "winner": true
          }
        ],
        "eventImageUrl": "https://api.smartq.tv/v1/assets/sports-logo/sm_ASSET_ID",
        "venue": { "id": "venue-id", "name": "Venue", "city": "City", "state": "ST", "country": "USA", "indoor": false },
        "viewingOptions": [
          {
            "provider": {
              "id": "linear-provider",
              "name": "Linear Provider",
              "type": "linear"
            },
            "network": {
              "id": "sports-network",
              "name": "Sports Network",
              "type": "network"
            },
            "channel": "123",
            "available": true
          }
        ]
      }
    ]
  }
}

Event IDs are stable SportsQ identifiers. They do not currently imply a separate event-detail Customer API route.

Schedule health and venue provider-availability health are reported separately. A valid event schedule with missing location viewing data returns PROVIDER_DATA_UNAVAILABLE, not a misleading fully healthy state.

viewingOptions describes location-specific ways to view an event. It does not describe what is currently assigned to a television.

Schedule status values: AVAILABLE, SCHEDULE_DATA_UNAVAILABLE. Provider status values: AVAILABLE, PROVIDER_DATA_UNAVAILABLE, PROVIDER_DATA_STALE, PROVIDER_DATA_PARTIAL. Freshness values: EXACT_DATE, STALE_DATE, MISSING.

Existing integrations can continue using the legacy SportsQ routes. New integrations should use Provider, Packages, and Programming by Date.

Legacy SportsQ guide compatibility

GET /v1/GROUP_ID/LOCATION_ID/sportsq supports an optional ?date=YYYY-MM-DD query for existing integrations. Its broad legacy response includes location, date, refresh, settings, selections, network catalog, and guide data. New integrations should use the customer-safe canonical resources above instead of depending on legacy implementation fields.

matriq:read

MatriQ Overview Example

{
  "ok": true,
  "version": "v1",
  "resource": "matriq",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "gateway": { "cadence": "CADENCE_STRING" },
      "devices": { "cadence": "CADENCE_STRING" },
      "providerAvailable": { "cadence": "CADENCE_STRING", "description": "PROVIDER_REFRESH_DESCRIPTION" },
      "aiScheduler": { "cadence": "CADENCE_STRING", "description": "AI_SCHEDULER_REFRESH_DESCRIPTION" }
    },
    "summary": {
      "deviceIds": ["DEVICE_ID"],
      "providerCount": "NUMBER",
      "aiSchedulerPublished": "BOOLEAN",
      "aiSchedulerRunning": "BOOLEAN"
    }
  }
}
Legacy compatibility

Provider Availability

This legacy compatibility shape is retained for existing integrations. New integrations should use the provider-neutral SportsQ Provider, Packages, and Programming by Date resources. Internal storage and device fields are intentionally omitted.

{
  "ok": true,
  "version": "v1",
  "resource": "providerAvailable",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "businessDate": {
      "active": "YYYYMMDD",
      "calendarDate": "YYYY-MM-DD",
      "timezone": "IANA_TIMEZONE"
    },
    "providers": [
      {
        "providerKey": "PROVIDER_KEY",
        "providerLabel": "PROVIDER_LABEL",
        "providerLogo": "PROVIDER_LOGO_URL"
      }
    ],
    "providersAvailable": [
      {
        "eventId": "EVENT_ID",
        "title": "EVENT_TITLE",
        "league": "LEAGUE_NAME",
        "leagueLogo": "LEAGUE_LOGO_URL",
        "homeTeam": "HOME_TEAM_NAME",
        "awayTeam": "AWAY_TEAM_NAME",
        "homeLogo": "HOME_TEAM_LOGO_URL",
        "awayLogo": "AWAY_TEAM_LOGO_URL",
        "providerLabel": "PROVIDER_LABEL",
        "providerLogo": "PROVIDER_LOGO_URL",
        "channelNumber": "CHANNEL_NUMBER"
      }
    ]
  }
}
matriq:read

Auto Scheduler Example

{
  "ok": true,
  "version": "v1",
  "resource": "matriq/aiScheduler",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "AI_SCHEDULER_REFRESH_DESCRIPTION"
    },
    "deviceIds": ["DEVICE_ID"],
    "businessDay": "YYYYMMDD",
    "statusUpdatedAt": "ISO_TIMESTAMP",
    "publishedDraftUpdatedAt": "ISO_TIMESTAMP",
    "status": {
      "running": "BOOLEAN",
      "published": "BOOLEAN",
      "draftExists": "BOOLEAN",
      "runtime": {
        "emergencyStopActive": "BOOLEAN",
        "lastStartAt": "ISO_TIMESTAMP_OR_NULL",
        "lastStopAt": "ISO_TIMESTAMP_OR_NULL"
      }
    },
    "currentPublishedDraft": {
      "businessDay": "YYYYMMDD",
      "published": "BOOLEAN",
      "status": "STATUS_STRING",
      "draft": {
        "title": "DRAFT_TITLE",
        "items": [
          { "id": "SCHEDULE_ITEM_ID", "start": "HH:MM", "end": "HH:MM", "eventTitle": "EVENT_TITLE" }
        ]
      }
    },
    "actions": [
      {
        "label": "Emergency Stop Auto Scheduler",
        "method": "POST",
        "endpoint": "https://api.smartq.tv/v1/GROUP_ID/LOCATION_ID/matriq/aiScheduler/emergency-stop",
        "scope": "matriq:ai-scheduler:stop",
        "description": "ACTION_DESCRIPTION"
      }
    ]
  }
}
matriq:ai-scheduler:stop

Emergency Stop Example

{
  "ok": true,
  "version": "v1",
  "resource": "matriq/aiScheduler/emergency-stop",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "status": "queued_or_applied_or_failed",
    "deviceId": "DEVICE_ID",
    "commandId": "COMMAND_ID",
    "requestedAt": "ISO_TIMESTAMP",
    "reason": "REASON_STRING",
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "AI_SCHEDULER_REFRESH_DESCRIPTION"
    }
  }
}
campaigns:read

Campaigns Example

{
  "ok": true,
  "version": "v1",
  "resource": "campaigns",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "CAMPAIGN_REFRESH_DESCRIPTION"
    },
    "campaigns": [
      {
        "id": "CAMPAIGN_ID",
        "name": "CAMPAIGN_NAME",
        "status": "STATUS_STRING",
        "startsAt": "ISO_TIMESTAMP",
        "endsAt": "ISO_TIMESTAMP"
      }
    ],
    "deviceStatus": {
      "healthy": "NUMBER",
      "warning": "NUMBER",
      "offline": "NUMBER"
    }
  }
}
orderboard:read

OrderBoard Example

{
  "ok": true,
  "version": "v1",
  "resource": "orderboard",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "refresh": {
      "cadence": "CADENCE_STRING",
      "description": "ORDERBOARD_REFRESH_DESCRIPTION"
    },
    "status": "coming_soon",
    "message": "ORDERBOARD_STATUS_MESSAGE"
  }
}
bevq:read

BevQ Location Menu

GET /v1/GROUP_ID/bevq returns group availability metadata. GET /v1/GROUP_ID/LOCATION_ID/bevq returns location status and the menu endpoint. Fetch the complete /bevq/menu resource from a trusted customer backend and cache by the returned ETag. No widget ID or separate POS join is required.

{
  "ok": true,
  "version": "v1",
  "resource": "bevq/menu",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "generatedAt": "ISO_TIMESTAMP",
  "data": {
    "location": {
      "id": "LOCATION_ID",
      "name": "LOCATION_NAME",
      "timezone": "IANA_TIMEZONE"
    },
    "menu": {
      "updatedAt": "ISO_TIMESTAMP",
      "revision": "MENU_REVISION",
      "sourceHealth": { "pos": "current", "lastPosSyncAt": "ISO_TIMESTAMP" },
      "sectionCount": 1,
      "itemCount": 1,
      "sections": [{
        "id": "on-tap",
        "name": "On Tap",
        "description": "Draft beer",
        "sortOrder": 0,
        "enabled": true,
        "itemCount": 1,
        "updatedAt": "ISO_TIMESTAMP",
        "items": [{
          "id": "CANONICAL_BEVQ_ITEM_ID",
          "type": "beer",
          "name": "Example IPA",
          "displayName": "House IPA",
          "brand": { "id": "CANONICAL_BRAND_ID", "name": "Example Brewery", "type": "brewery", "imageUrl": "PUBLIC_BRAND_IMAGE_URL" },
          "style": "American IPA",
          "category": "beer",
          "abv": 7,
          "ibu": 55,
          "country": "USA",
          "region": "Michigan",
          "description": "Aromatic India pale ale.",
          "imageUrl": "PUBLIC_ITEM_IMAGE_URL",
          "posItemId": "POS_ITEM_ID",
          "posGroupId": "POS_GROUP_ID",
          "mapped": true,
          "available": true,
          "sizes": [{ "id": "pint", "name": "16 oz", "ounces": 16, "price": 6.5, "currency": "USD", "available": true }],
          "sortOrder": 0,
          "sectionId": "on-tap",
          "updatedAt": "ISO_TIMESTAMP"
        }]
      }]
    }
  }
}

The API preserves configured section and item order. Priced size rows use the location's authoritative ISO 4217 POS currency; currency is not guessed from locale or symbols and is not limited to USD. brand is optional, and its id, name, type, and imageUrl may be absent independently.

Store the response ETag and echo it exactly in If-None-Match. SmartQ also accepts the equivalent weak validator, a matching validator list, and * for an existing menu. Treat 304 Not Modified with its empty body as a successful unchanged-menu result.

sourceHealth.pos is current after a successful persisted POS reconciliation with no later failure, stale after a later failed attempt, and unknown before a successful timestamp exists. It is not based on an arbitrary age threshold, and lastPosSyncAt is not a universal per-price freshness clock.

Server-to-server menu update event

Configure an HTTPS destination for an active customer-managed client_credentials application with bevq:read in SmartQ Admin's Developer API Access area. Location applications cover their authorized location. Group applications may cover all authorized locations or an explicit selected set. Whole-group coverage automatically includes future locations authorized to that application; selected-location coverage never broadens silently. SmartQ Support-managed legacy bearers cannot own new destinations.

bevq.menu.updated is a small, location-specific signed prompt for the customer's backend to re-fetch this menu with its bearer and ETag; it never contains a combined group menu, the complete menu, or credentials. Keep a scheduled conditional reconciliation job as a fallback.

{
  "eventId": "STABLE_EVENT_ID",
  "eventType": "bevq.menu.updated",
  "occurredAt": "ISO_TIMESTAMP",
  "groupId": "GROUP_ID",
  "locationId": "LOCATION_ID",
  "revision": "MENU_REVISION",
  "resource": "/v1/GROUP_ID/LOCATION_ID/bevq/menu"
}

Verify X-SmartQ-Signature against the exact raw request body before parsing, validate X-SmartQ-Timestamp, and deduplicate by X-SmartQ-Event-Id. SmartQ retries eligible failures at most five attempts. See the complete BevQ webhook reference for the exact wire contract and Node.js, Python, and PHP examples.