{
  "openapi": "3.1.1",
  "info": {
    "title": "Hala Valet — Privacy-minimal appointment tickets",
    "version": "1.0.0",
    "description": "Site is derived from the Bearer key. No name, mobile, vehicle or medical data in partner requests. Consume only on committed intake. Read status through GET (no automatic webhook). Exact retries are safe; changed windows are rejected. QR/barcode is ticketNumber only. Production connects to the existing Hala Valet database. Obtain your site key from Hala Valet. The sandbox is isolated and expires after 2h."
  },
  "servers": [
    {
      "url": "https://api.halavalet.com",
      "description": "Production — site key required; use /admissions.ashx"
    },
    {
      "url": "https://hala-valet-integration-sandbox.halavalet.workers.dev",
      "description": "Sandbox — use /api/v1/admissions, obtain partnerToken from /api/sandbox/session"
    }
  ],
  "paths": {
    "/admissions.ashx": {
      "servers": [
        {
          "url": "https://api.halavalet.com"
        }
      ],
      "get": {
        "summary": "Retrieve permission / usage status at the credential site",
        "security": [
          {
            "SiteKey": []
          }
        ],
        "parameters": [
          {
            "name": "ticketNumber",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,100}$",
              "description": "Opaque, case-sensitive, unique within the credential site. Encode this exact string in QR / barcode. No URL or personal data."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Exact retry or status query; no visitor or vehicle data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Permission ticket created, not vehicle custody",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON"
          },
          "401": {
            "description": "Invalid/revoked site key"
          },
          "404": {
            "description": "Not found within the credential site"
          },
          "409": {
            "description": "Same ticket number with a different window"
          },
          "413": {
            "description": "Maximum 4 KiB body"
          },
          "422": {
            "description": "Only three fields allowed; invalid number or time window"
          },
          "429": {
            "description": "Sandbox rate or ticket limit"
          }
        }
      },
      "post": {
        "summary": "Create permission ticket; exact repeat is idempotent",
        "security": [
          {
            "SiteKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "ticketNumber",
                  "validFrom",
                  "validUntil"
                ],
                "properties": {
                  "ticketNumber": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{1,100}$",
                    "description": "Opaque, case-sensitive, unique within the credential site. Encode this exact string in QR / barcode. No URL or personal data."
                  },
                  "validFrom": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Inclusive; ISO8601 with timezone, at most millisecond precision."
                  },
                  "validUntil": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Exclusive; later than validFrom."
                  }
                }
              },
              "example": {
                "ticketNumber": "FK-DEMO-1001",
                "validFrom": "2026-10-31T14:50:34.340Z",
                "validUntil": "2026-10-31T17:20:34.342Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Exact retry or status query; no visitor or vehicle data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Permission ticket created, not vehicle custody",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON"
          },
          "401": {
            "description": "Invalid/revoked site key"
          },
          "404": {
            "description": "Not found within the credential site"
          },
          "409": {
            "description": "Same ticket number with a different window"
          },
          "413": {
            "description": "Maximum 4 KiB body"
          },
          "422": {
            "description": "Only three fields allowed; invalid number or time window"
          },
          "429": {
            "description": "Sandbox rate or ticket limit"
          }
        }
      }
    },
    "/api/v1/admissions": {
      "servers": [
        {
          "url": "https://hala-valet-integration-sandbox.halavalet.workers.dev"
        }
      ],
      "get": {
        "summary": "Retrieve permission / usage status at the credential site",
        "security": [
          {
            "SiteKey": []
          }
        ],
        "parameters": [
          {
            "name": "ticketNumber",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{1,100}$",
              "description": "Opaque, case-sensitive, unique within the credential site. Encode this exact string in QR / barcode. No URL or personal data."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Exact retry or status query; no visitor or vehicle data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Permission ticket created, not vehicle custody",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON"
          },
          "401": {
            "description": "Invalid/revoked site key"
          },
          "404": {
            "description": "Not found within the credential site"
          },
          "409": {
            "description": "Same ticket number with a different window"
          },
          "413": {
            "description": "Maximum 4 KiB body"
          },
          "422": {
            "description": "Only three fields allowed; invalid number or time window"
          },
          "429": {
            "description": "Sandbox rate or ticket limit"
          }
        }
      },
      "post": {
        "summary": "Create permission ticket; exact repeat is idempotent",
        "security": [
          {
            "SiteKey": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "ticketNumber",
                  "validFrom",
                  "validUntil"
                ],
                "properties": {
                  "ticketNumber": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{1,100}$",
                    "description": "Opaque, case-sensitive, unique within the credential site. Encode this exact string in QR / barcode. No URL or personal data."
                  },
                  "validFrom": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Inclusive; ISO8601 with timezone, at most millisecond precision."
                  },
                  "validUntil": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Exclusive; later than validFrom."
                  }
                }
              },
              "example": {
                "ticketNumber": "FK-DEMO-1001",
                "validFrom": "2026-10-31T14:50:34.342Z",
                "validUntil": "2026-10-31T17:20:34.342Z"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Exact retry or status query; no visitor or vehicle data",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Permission ticket created, not vehicle custody",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "data"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "data": {
                      "type": "object",
                      "required": [
                        "ticketNumber",
                        "validFrom",
                        "validUntil",
                        "status",
                        "usedAt"
                      ],
                      "properties": {
                        "ticketNumber": {
                          "type": "string"
                        },
                        "validFrom": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "validUntil": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "status": {
                          "enum": [
                            "available",
                            "not_yet_valid",
                            "expired",
                            "used"
                          ]
                        },
                        "usedAt": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid JSON"
          },
          "401": {
            "description": "Invalid/revoked site key"
          },
          "404": {
            "description": "Not found within the credential site"
          },
          "409": {
            "description": "Same ticket number with a different window"
          },
          "413": {
            "description": "Maximum 4 KiB body"
          },
          "422": {
            "description": "Only three fields allowed; invalid number or time window"
          },
          "429": {
            "description": "Sandbox rate or ticket limit"
          }
        }
      }
    },
    "/api/sandbox/session": {
      "servers": [
        {
          "url": "https://hala-valet-integration-sandbox.halavalet.workers.dev"
        }
      ],
      "post": {
        "security": [],
        "summary": "Start isolated 2-hour test session (never production credentials)",
        "responses": {
          "200": {
            "description": "sessionId, expiresAt, partnerToken, staffToken"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "SiteKey": {
        "type": "http",
        "scheme": "bearer",
        "description": "Production: key issued once by a Hala admin for one site. Sandbox: session partnerToken. Never mix credentials."
      }
    }
  }
}