{
  "openapi": "3.0.3",
  "info": {
    "title": "Crave Server-to-Server Integration API",
    "version": "1.0.0",
    "description": "Organization-scoped integration operations. Never expose an organization API key to a browser or diner."
  },
  "servers": [
    {
      "url": "https://sandbox-api.craveup.com/api/v1",
      "description": "Sandbox"
    },
    {
      "url": "https://api.craveup.com/api/v1",
      "description": "Production"
    }
  ],
  "paths": {
    "/locations": {
      "get": {
        "operationId": "integrationListLocations",
        "summary": "List the locations this key can read",
        "description": "Requires an organization API key with merchant:read. Lists the live locations the key was granted, in the merchant's own order, with the id to address them by, their name and their formatted address, so an integration can choose one. Archived locations are left out. Carries no order figures: an integration that only needs to pick a location should not pay for the totals the partner dashboard computes.",
        "security": [
          {
            "OrganizationApiKey": []
          }
        ],
        "x-crave-required-api-key-scope": "merchant:read",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The key's live locations.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationLocationList"
                }
              }
            }
          },
          "401": {
            "description": "Organization API key missing, invalid, or for a different environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "403": {
            "description": "Key scope denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "429": {
            "description": "Baseline API rate limit exceeded before route authentication.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "description": "Locations could not be read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "503": {
            "description": "Rate-limit storage unavailable; request denied before the route handler.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitStoreError"
                }
              }
            }
          }
        }
      }
    },
    "/locations/{locationId}/profile": {
      "get": {
        "operationId": "integrationGetLocationProfile",
        "summary": "Read a location's identity, hours and current open state",
        "description": "Requires an organization API key with merchant:read and access to the requested location. Answers what an integration needs to speak for a restaurant in one read: its name, address, timezone, opening hours, whether it is open at the moment of the request, and which fulfillment methods it accepts. isOpenNow is evaluated server-side against the location's own timezone so that callers do not each reimplement the rule and disagree at opening and closing minutes. scheduleSource distinguishes hours nobody has set (unconfirmed) from a restaurant that is closed right now. Carries no fee rates, provider metadata or internal settings.",
        "security": [
          {
            "OrganizationApiKey": []
          }
        ],
        "x-crave-required-api-key-scope": "merchant:read",
        "parameters": [
          {
            "name": "locationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current profile. Cache-Control: no-store, because isOpenNow is only true of the moment it was asked.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationLocationProfile"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, or the location has no confirmed timezone to read its hours against.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "401": {
            "description": "Organization API key missing, invalid, or for a different environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "403": {
            "description": "Key scope or location assignment denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "404": {
            "description": "No such location is in scope, or it has been archived.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "429": {
            "description": "Baseline API rate limit exceeded before route authentication.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "description": "Profile could not be read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "503": {
            "description": "Rate-limit storage unavailable; request denied before the route handler.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitStoreError"
                }
              }
            }
          }
        }
      }
    },
    "/locations/{locationId}/carts/{cartId}/commerce-projection": {
      "get": {
        "operationId": "integrationGetOrderCommerceProjection",
        "summary": "Read the canonical order and payment projection for a cart",
        "description": "Requires an organization API key with orders:read and access to the requested location. Guest cart capabilities and merchant sessions do not authorize this operation. Returns the minimal current aggregate, not customer details or provider payment payloads. A successful read does not authorize a checkout retry.",
        "security": [
          {
            "OrganizationApiKey": []
          }
        ],
        "x-crave-required-api-key-scope": "orders:read",
        "parameters": [
          {
            "name": "locationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "cartId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Current canonical projection. Cache-Control: private, no-store.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "private, no-store"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrderCommerceProjection"
                }
              }
            }
          },
          "400": {
            "description": "Invalid request.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "401": {
            "description": "Organization API key missing, invalid, or for a different environment.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "403": {
            "description": "Key scope or location assignment denied.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "404": {
            "description": "No matching scoped cart/order projection is available.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "429": {
            "description": "Baseline API rate limit exceeded before route authentication.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitError"
                }
              }
            }
          },
          "500": {
            "description": "Projection could not be read. No payment or order mutation is performed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PrivateApiError"
                }
              }
            }
          },
          "503": {
            "description": "Rate-limit storage unavailable; request denied before the route handler.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "string",
                  "enum": [
                    "1"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RateLimitStoreError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "OrganizationApiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    },
    "schemas": {
      "IntegrationLocationList": {
        "type": "object",
        "properties": {
          "locations": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "name": {
                  "type": "string"
                },
                "address": {
                  "nullable": true,
                  "type": "string"
                }
              },
              "required": [
                "id",
                "name",
                "address"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "locations"
        ],
        "additionalProperties": false
      },
      "IntegrationLocationProfile": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "name": {
            "type": "string"
          },
          "timezone": {
            "type": "string",
            "minLength": 1
          },
          "address": {
            "type": "object",
            "properties": {
              "formatted": {
                "type": "string"
              },
              "components": {
                "type": "object",
                "properties": {
                  "street": {
                    "type": "string"
                  },
                  "streetOptional": {
                    "type": "string"
                  },
                  "city": {
                    "type": "string"
                  },
                  "state": {
                    "type": "string"
                  },
                  "zipCode": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string"
                  },
                  "lat": {
                    "nullable": true,
                    "type": "number",
                    "minimum": -90,
                    "maximum": 90
                  },
                  "lng": {
                    "nullable": true,
                    "type": "number",
                    "minimum": -180,
                    "maximum": 180
                  }
                },
                "additionalProperties": {}
              }
            },
            "required": [
              "formatted",
              "components"
            ],
            "additionalProperties": false
          },
          "schedule": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "day": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 6
                },
                "active": {
                  "type": "boolean"
                },
                "intervals": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "from": {
                        "type": "string",
                        "minLength": 1
                      },
                      "to": {
                        "type": "string",
                        "minLength": 1
                      }
                    },
                    "required": [
                      "from",
                      "to"
                    ],
                    "additionalProperties": false
                  }
                }
              },
              "required": [
                "day",
                "active",
                "intervals"
              ],
              "additionalProperties": false
            }
          },
          "scheduleSource": {
            "type": "string",
            "enum": [
              "location",
              "organization",
              "unconfirmed"
            ]
          },
          "isOpenNow": {
            "type": "boolean"
          },
          "fulfillmentMethods": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "takeout",
                "table_side",
                "room_service",
                "delivery"
              ]
            }
          }
        },
        "required": [
          "id",
          "name",
          "timezone",
          "address",
          "schedule",
          "scheduleSource",
          "isOpenNow",
          "fulfillmentMethods"
        ],
        "additionalProperties": false
      },
      "OrderCommerceProjection": {
        "type": "object",
        "properties": {
          "cartId": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "checkout": {
            "type": "object",
            "properties": {
              "attemptId": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "collectionMode": {
                "type": "string",
                "enum": [
                  "ONLINE",
                  "PAY_AT_STORE"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "PREPARING",
                  "ORDER_CREATED",
                  "AWAITING_CUSTOMER_ACTION",
                  "SUBMITTING",
                  "SUCCEEDED",
                  "FAILED",
                  "EXPIRED",
                  "ACTION_REQUIRED"
                ]
              }
            },
            "required": [
              "attemptId",
              "collectionMode",
              "status"
            ],
            "additionalProperties": false
          },
          "order": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid",
                "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
              },
              "status": {
                "type": "string",
                "minLength": 1,
                "maxLength": 64
              }
            },
            "required": [
              "id",
              "status"
            ],
            "additionalProperties": false
          },
          "payment": {
            "type": "object",
            "properties": {
              "reconciliationStatus": {
                "type": "string",
                "enum": [
                  "CURRENT",
                  "RECONCILING",
                  "ACTION_REQUIRED"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "NO_PAYMENT_REQUIRED",
                  "DUE",
                  "PARTIALLY_PAID",
                  "PAID",
                  "PARTIALLY_REFUNDED",
                  "REFUNDED",
                  "CANCELED"
                ]
              }
            },
            "required": [
              "reconciliationStatus",
              "status"
            ],
            "additionalProperties": false
          }
        },
        "required": [
          "cartId",
          "checkout",
          "order",
          "payment"
        ],
        "additionalProperties": false
      },
      "RateLimitError": {
        "type": "object",
        "required": [
          "status",
          "message"
        ],
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              429
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "RateLimitStoreError": {
        "type": "object",
        "required": [
          "status",
          "code",
          "message"
        ],
        "properties": {
          "status": {
            "type": "integer",
            "enum": [
              503
            ]
          },
          "code": {
            "type": "string",
            "enum": [
              "RATE_LIMIT_STORE_UNAVAILABLE"
            ]
          },
          "message": {
            "type": "string"
          }
        }
      },
      "PrivateApiError": {
        "type": "object",
        "required": [
          "success",
          "message"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "message": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "data": {
            "type": "object",
            "additionalProperties": true
          }
        }
      }
    }
  }
}
