Skip to content

Response Schemas

This page documents the common response formats and data structures used across the KitchenClick Ecommerce API.

Response Envelope

All API responses follow a consistent structure:

Success Response

json
{
  "status": "success",
  "message": "Optional success message",
  "data": {
    // Response payload
  },
  "meta": {
    // Pagination or additional metadata
  }
}

Error Response

json
{
  "status": "error",
  "message": "Human-readable error description",
  "errors": {
    "field_name": ["Error message 1", "Error message 2"]
  },
  "error_code": "specific_error_code"
}

Response Fields

FieldTypeDescription
statusstringsuccess or error
messagestringHuman-readable message
dataobject/arrayResponse payload (success only)
metaobjectPagination or metadata
errorsobjectField-specific errors (error only)
error_codestringMachine-readable error code

Pagination

List endpoints return paginated results:

json
{
  "status": "success",
  "data": [...],
  "meta": {
    "current_page": 1,
    "from": 1,
    "to": 25,
    "per_page": 25,
    "total": 150,
    "last_page": 6,
    "path": "/api/v1/ecommerce/locations/loc_xxxxx/menus",
    "links": {
      "first": "...?page=1",
      "prev": null,
      "next": "...?page=2",
      "last": "...?page=6"
    }
  }
}

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
per_pageinteger25Items per page (max: 100)

ID Formats (Hashkeys)

All entities use opaque, prefixed identifiers (never raw integer IDs). They are exposed in responses as entity-specific _id fields — e.g. location_id, item_id, order_id — carrying these prefixed values:

EntityPrefixExample
Locationloc_loc_00000k1L2m3N4o5
Conceptcon_con_00000a1B2c3D4e5
Menumnu_mnu_00000f5G6h7I8j9
Menu Itemitm_itm_00000p1Q2r3S4t5
Option Groupopg_opg_00000u1V2w3X4y5
Modifiermod_mod_00000z1A2b3C4d5
Orderord_ord_00000e5F6g7H8i9
Kioskksk_ksk_00000j1K2l3M4n5

Data Types

Money

Monetary values are represented as decimal numbers with 2 decimal places:

json
{
  "base_price": 12.99,
  "tax_amount": 1.07,
  "total": 14.06
}

Timestamps

All timestamps are ISO 8601 format in UTC:

json
{
  "created_at": "2024-01-15T14:30:00Z",
  "updated_at": "2024-01-15T15:45:30Z"
}

Phone Numbers

Phone numbers are stored with formatting:

json
{
  "phone": "(555) 123-4567"
}

Entity Schemas

Field names

These are simplified reference shapes. Entity identifiers are exposed as entity-specific _id fields (e.g. location_id, item_id, order_id) carrying the prefixed values from the ID Formats table — never raw integer IDs. For the exact fields of any endpoint, the per-endpoint reference page is authoritative.

Location

typescript
interface Location {
  location_id: string;          // loc_xxxxx
  name: string;
  address: {
    street: string;
    city: string;
    state: string;
    zip: string;
    country: string;
  };
  coordinates?: {
    latitude: number;
    longitude: number;
  };
  phone?: string;
  timezone: string;             // IANA timezone
  status: 'active' | 'inactive';
  order_types: OrderType[];
  prep_time_minutes: number;
  minimum_order?: number;
  delivery_settings?: {
    radius_miles: number;
    fee: number;
    minimum_order: number;
  };
}

Concept

typescript
interface Concept {
  concept_id: string;           // con_xxxxx
  name: string;
  description?: string;
  concept_type: string;
  logo_url?: string;
  hero_url?: string;
  gallery_urls?: string[];
  status: 'active' | 'inactive';
}
typescript
interface Menu {
  menu_id: string;              // mnu_xxxxx
  name: string;
  description?: string;
  menu_type: MenuType;
  hero_url?: string;
  status: 'draft' | 'live' | 'archived';
  display_order: number;
  valid_from?: string;          // ISO date
  valid_to?: string;            // ISO date
  availability_rules?: {
    days: DayOfWeek[];
    time_ranges: TimeRange[];
  };
  channel_restrictions?: Channel[];
}

type MenuType = 'regular' | 'breakfast' | 'lunch' | 'dinner' |
                'late_night' | 'happy_hour' | 'seasonal';

type DayOfWeek = 'monday' | 'tuesday' | 'wednesday' | 'thursday' |
                 'friday' | 'saturday' | 'sunday';

interface TimeRange {
  start: string;  // HH:MM
  end: string;    // HH:MM
}
typescript
interface MenuItem {
  item_id: string;              // itm_xxxxx
  name: string;
  description?: string;
  type: 'category' | 'subcategory' | 'item';
  parent_id?: string;
  base_price?: number;          // For type: 'item'
  hero_url?: string;
  gallery_images?: GalleryImage[];
  nutritional_info?: NutritionalInfo;
  allergens?: Allergen[];
  dietary_tags?: DietaryTag[];
  tags?: string[];
  prep_time_minutes?: number;
  station_routing?: string[];
  is_featured: boolean;
  is_86d: boolean;
  display_order: number;
  status: 'draft' | 'live';
  children?: MenuItem[];        // For categories
  option_groups?: OptionGroup[]; // For items
}

interface GalleryImage {
  url: string;
  alt_text?: string;
  caption?: string;
  sort_order: number;
}

interface NutritionalInfo {
  calories?: number;
  protein?: number;
  carbs?: number;
  fat?: number;
  sodium?: number;
  sugar?: number;
}

type Allergen = 'dairy' | 'eggs' | 'fish' | 'shellfish' |
               'tree_nuts' | 'peanuts' | 'wheat' | 'gluten' |
               'soy' | 'sesame';

type DietaryTag = 'vegetarian' | 'vegan' | 'gluten-free' |
                  'keto' | 'halal' | 'kosher';

OptionGroup

typescript
interface OptionGroup {
  option_group_id: string;      // opg_xxxxx
  name: string;
  display_name?: string;
  description?: string;
  is_required: boolean;
  min_selections: number;
  max_selections: number;
  sort_order: number;
  modifiers: Modifier[];
}

interface Modifier {
  modifier_id: string;          // mod_xxxxx
  name: string;
  price_adjustment: number;
  price_adjustment_type?: 'fixed' | 'percentage';
  is_default: boolean;
  sort_order: number;
}

Order

typescript
interface Order {
  order_id: string;             // ord_xxxxx
  order_number: string;
  location_id: string;
  kiosk_id?: string;
  channel: Channel;
  customer: CustomerInfo;
  order_type: OrderType;
  status: OrderStatus;
  scheduled_at?: string;
  placed_at: string;
  confirmed_at?: string;
  started_at?: string;
  ready_at?: string;
  completed_at?: string;
  estimated_ready_time?: string;
  totals: OrderTotals;
  items: OrderItem[];
  special_instructions?: string;
  delivery_info?: DeliveryInfo;
  payment_info?: PaymentInfo;
}

interface CustomerInfo {
  name: string;
  phone: string;
  email?: string;
}

interface OrderTotals {
  subtotal: number;
  tax: number;
  tax_breakdown?: TaxLine[];
  deposits?: number;
  fees?: Fee[];
  delivery_fee?: number;
  tip?: number;
  total: number;
}

interface TaxLine {
  name: string;
  rate: number;
  amount: number;
}

interface Fee {
  name: string;
  amount: number;
}

interface OrderItem {
  item_id: string;
  name: string;
  quantity: number;
  unit_price: number;
  line_total: number;
  status?: ItemStatus;
  customizations?: string[];
  special_instructions?: string;
}

interface DeliveryInfo {
  address: string;
  address_line2?: string;
  city: string;
  state: string;
  zip: string;
  instructions?: string;
}

// `order_type` is normalized to one of these canonical values.
// Input aliases are also accepted (e.g. `pickup` -> `takeout`,
// `dinein`/`dine-in` -> `dine_in`, `drive-thru` -> `drive_thru`).
type OrderType = 'takeout' | 'delivery' | 'dine_in' | 'drive_thru' |
                 'curbside' | 'catering' | 'special_order';

type OrderStatus = 'pending' | 'confirmed' | 'scheduled' | 'preparing' |
                   'ready' | 'out_for_delivery' | 'completed' | 'cancelled';

type ItemStatus = 'pending' | 'preparing' | 'ready';

type Channel = 'ecommerce' | 'kiosk' | 'pos' | 'mobile' | 'third_party';

Kiosk

Note: the kiosk endpoints use _hashkey-suffixed field names (e.g. kiosk_hashkey, location_hashkey), unlike the rest of the ecommerce API which uses _id. See Kiosks.

typescript
interface Kiosk {
  kiosk_hashkey: string;        // ksk_xxxxx
  name: string;
  location_hashkey: string;
  status: 'active' | 'inactive' | 'maintenance';
  last_heartbeat_at?: string;
  config: KioskConfig;
}

interface KioskConfig {
  idle_timeout_seconds: number;
  order_types: OrderType[];
  payment_methods: PaymentMethod[];
  receipt_options: ReceiptOption[];
  ui_theme: 'light' | 'dark';
  language: string;
  accessibility_mode: boolean;
}

type PaymentMethod = 'credit_card' | 'debit_card' | 'cash' |
                     'gift_card' | 'mobile_pay';

type ReceiptOption = 'print' | 'email' | 'sms' | 'none';

Error Codes

Authentication Errors

CodeHTTPDescription
unauthorized401Missing or invalid token
forbidden403Insufficient permissions
token_expired401Access token has expired
invalid_scope403Token lacks required scope

Validation Errors

CodeHTTPDescription
validation_failed422Request validation failed
invalid_hashkey400Invalid entity identifier
missing_required_field400Required field not provided

Business Logic Errors

CodeHTTPDescription
location_closed400Location not accepting orders
item_unavailable400Item is 86'd or inactive
invalid_modifiers400Modifier selection invalid
minimum_not_met400Order minimum not reached
delivery_unavailable400Delivery not available

Payment Errors

CodeHTTPDescription
card_declined400Payment card declined
insufficient_funds400Insufficient funds
payment_failed400General payment failure
presentment_expired400Payment presentment expired

Rate Limiting

CodeHTTPDescription
rate_limit_exceeded429Too many requests

Rate Limit Headers

All responses include rate limit information:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1705330800
HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRequests remaining
X-RateLimit-ResetUnix timestamp when limit resets

Changelog
DateChange
2026-06-17Aligned entity ID field names to the _id convention used by the API; corrected the OrderType and Channel enums and the DeliveryInfo shape; noted that kiosk endpoints use _hashkey-suffixed names.
2026-01-15Initial publication.

ShopHero CommerceCore Platform