{
  "openapi": "3.1.0",
  "info": {
    "title": "Buzzer API",
    "version": "1.0.0-draft",
    "description": "Complete public /v1 contract for the unreleased connector implementation. Backend release and live-provider verification are pending. Configure the actual service origin; buzzerapi.com serves the website, not /v1. This reference excludes Lowkey app-internal /api routes and the website session bridge.",
    "x-backend-commit": "a85cadaf34b953a575ec58fe37acb5e3d770be77"
  },
  "servers": [
    {
      "url": "https://{serviceOrigin}",
      "description": "Replace with your configured backend host. The default is a placeholder, not a working service.",
      "variables": {
        "serviceOrigin": {
          "default": "YOUR_CONFIGURED_SERVICE_ORIGIN"
        }
      }
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "paths": {
    "/v1/health": {
      "get": {
        "operationId": "get_v1_health",
        "summary": "Check service health",
        "description": "Health is public; success does not establish account readiness.",
        "security": [],
        "x-required-scopes": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/plans": {
      "get": {
        "operationId": "get_v1_plans",
        "summary": "Read live public prices",
        "description": "Only Standard/Premium monthly/yearly are listed. Multi Building is managed in the Lowkey app. Public cache max-age=60, shared cache max-age=300.",
        "security": [],
        "x-required-scopes": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plans"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/otp": {
      "post": {
        "operationId": "post_v1_auth_otp",
        "summary": "Request email sign-in code",
        "description": "Expires in 15 minutes, five verification attempts, single use. At most three unexpired challenges per email. Delivery is not proof that the account already exists.",
        "security": [],
        "x-required-scopes": [],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OtpRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OtpChallenge"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/verify": {
      "post": {
        "operationId": "post_v1_auth_verify",
        "summary": "Verify email and obtain owner credential",
        "description": "Creates the account if needed. At most five active non-web owner credentials. Never disclose apiKey. purpose=web is for server-side website sessions; it is not a browser-storage recommendation.",
        "security": [],
        "x-required-scopes": [],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OwnerCredential"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/key": {
      "delete": {
        "operationId": "delete_v1_auth_key",
        "summary": "Revoke the current credential",
        "description": "No extra scope required. Subsequent use fails authentication. CLI child credentials are invalidated when their parent expires or is revoked. Website account-agent keys survive website logout.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Revoked"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/keys": {
      "post": {
        "operationId": "post_v1_auth_keys",
        "summary": "Create an agent credential",
        "description": "One delegation level; a parentKeyId credential cannot delegate again. Requested scopes must be held by the issuer. Website sessions require an active subscription and may issue only default access scopes plus optional buildings:write, with a limit of 20 active account-agent keys. Website-issued keys are independent of session expiry; CLI-delegated keys depend on their parent.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "keys:write"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/KeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AgentCredential"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "get_v1_auth_keys",
        "summary": "List manageable agent credentials",
        "description": "Website sessions list account-agent keys. Other credentials list their own children plus account-agent keys. Excludes revoked keys, but can include expired ones. No secrets or pagination.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "keys:write"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyList"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/auth/keys/{id}": {
      "delete": {
        "operationId": "delete_v1_auth_keys_id",
        "summary": "Revoke a manageable agent key",
        "description": "Same ownership rules as list. Repeated revocation of a known managed key succeeds.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "keys:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/KeyRevoked"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/account": {
      "get": {
        "operationId": "get_v1_account",
        "summary": "Inspect account readiness",
        "description": "Account-level response; building header does not select another account. Subscription active and a recorded buzz do not prove physical entry.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Account"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/plans": {
      "get": {
        "operationId": "get_v1_billing_plans",
        "summary": "Read authenticated price catalog",
        "description": "Same catalog as public plans; no active subscription required.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "billing:write"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plans"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/checkout": {
      "get": {
        "operationId": "get_v1_billing_checkout",
        "summary": "Recover latest checkout",
        "description": "Read after a timeout. complete means hosted checkout completed; recheck account.subscription.status and setup. preparing means retry original plan and request ID.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "billing:write"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkout"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "post_v1_billing_checkout",
        "summary": "Start or recover hosted checkout",
        "description": "Parent account only. Existing subscriptions use the portal. One pending checkout: same plan reuses it, another plan conflicts until the unpaid session is expired. Same request ID with another plan conflicts. After an unresolved provider outcome older than 23 hours, reconciliation is required instead of another purchase. No card details accepted by this API.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "billing:write"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "",
              "pattern": "^[a-zA-Z0-9_-]{16,100}$"
            },
            "description": "Stable identifier for this action. Reuse the identical body and key after a timeout."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkout"
                }
              }
            }
          },
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Checkout"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/checkout/{id}/expire": {
      "post": {
        "operationId": "post_v1_billing_checkout_id_expire",
        "summary": "Expire unpaid checkout",
        "description": "Open sessions can be expired; already-expired sessions succeed. Completed checkout returns 409 SUBSCRIPTION_EXISTS. This does not cancel a subscription.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "billing:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Stripe checkout session ID."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckoutExpired"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/billing/portal": {
      "post": {
        "operationId": "post_v1_billing_portal",
        "summary": "Open hosted billing management",
        "description": "Parent account with an existing billing customer. No request body.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "billing:write"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Portal"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/setup": {
      "get": {
        "operationId": "get_v1_setup",
        "summary": "Inspect selected building setup",
        "description": "No subscription required for inspection. A previous successful buzz is not proof of physical door release.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read"
        ],
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Setup"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "patch_v1_setup",
        "summary": "Configure selected building release tone",
        "description": "Must own a provisioned number. Linked-building writes require active Multi Building. The API does not program the building call box; its manager must change the destination.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "setup:write"
        ],
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetupRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SetupUpdated"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/buildings": {
      "get": {
        "operationId": "get_v1_buildings",
        "summary": "List accessible buildings",
        "description": "No pagination. Ordered by creation time then ID. Parent sees primary and children; child sees only itself.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read"
        ],
        "parameters": [],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Buildings"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "post_v1_buildings",
        "summary": "Execute quoted building addition",
        "description": "Parent account only; do not send building header. Supply the matching unexpired quote and stable request ID. Stale inventory/billing requires a fresh preview before execution. Replay returns stored operation. HTTP 202 is not completion. running: poll the operation; reconciliation_required: contact support with its ID and do not create another request. Account mutations are serialized; uncertain outcomes retain the lock.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read",
          "buildings:write"
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "",
              "pattern": "^[a-zA-Z0-9_-]{16,100}$"
            },
            "description": "Stable identifier for this action. Reuse the identical body and key after a timeout."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "202": {
            "description": "Pending or uncertain: inspect status and next_action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/buildings/preview": {
      "post": {
        "operationId": "post_v1_buildings_preview",
        "summary": "Preview addition or removal",
        "description": "Parent account only; do not send building header. Requires exactly one active/trialing Multi Building subscription, matching billed quantity, maximum 20 buildings including primary. Preview purchases nothing, creates a 15-minute quote and invoice snapshot. Only US addresses supported for additions. Removal cannot target primary; review release/deletion effects.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read",
          "buildings:write"
        ],
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BuildingPreview"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/buildings/{id}": {
      "delete": {
        "operationId": "delete_v1_buildings_id",
        "summary": "Execute quoted building removal",
        "description": "Parent account only; do not send building header. Supply the matching unexpired quote and stable request ID. Stale inventory/billing requires a fresh preview before execution. Replay returns stored operation. HTTP 202 is not completion. running: poll the operation; reconciliation_required: contact support with its ID and do not create another request. Account mutations are serialized; uncertain outcomes retain the lock. The JSON DELETE body is required. Permanently releases the number, deletes child rules/activity, revokes child keys, disables child webhooks, and reduces subscription quantity.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read",
          "buildings:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "",
              "pattern": "^[a-zA-Z0-9_-]{16,100}$"
            },
            "description": "Stable identifier for this action. Reuse the identical body and key after a timeout."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "202": {
            "description": "Pending or uncertain: inspect status and next_action.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/buildings/operations/{id}": {
      "get": {
        "operationId": "get_v1_buildings_operations_id",
        "summary": "Read building operation receipt",
        "description": "Parent-owned operation. Recovery remains available without an active subscription. A running operation older than five minutes is reported as reconciliation_required. Quotes/receipts are not erased when the quote expires.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "account:read",
          "buildings:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildingOperation"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/unlock": {
      "post": {
        "operationId": "post_v1_unlock",
        "summary": "Create an access grant",
        "description": "Requires active subscription and provisioned selected number. Lite: timer; Standard: timer/passcode; Premium, Business, Multi Building: all three. Linked buildings require Multi Building. New grants start enabled. A repeated idempotency key returns the current grant (including revoked state), never reopens it; a changed body conflicts.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "access:write"
        ],
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "",
              "pattern": "^[a-zA-Z0-9_-]{16,100}$"
            },
            "description": "Stable identifier for this action. Reuse the identical body and key after a timeout."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnlockCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unlock"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "description": ""
                },
                "description": "On new 201: relative /v1/unlock/{id}."
              },
              "Idempotency-Replayed": {
                "schema": {
                  "const": "true"
                },
                "description": "Present on replay."
              }
            }
          },
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unlock"
                }
              }
            },
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "description": ""
                },
                "description": "On new 201: relative /v1/unlock/{id}."
              },
              "Idempotency-Replayed": {
                "schema": {
                  "const": "true"
                },
                "description": "Present on replay."
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "get_v1_unlock",
        "summary": "List access grants",
        "description": "No active subscription required. Excludes API-revoked grants. Ordered by descending ID. active=true means timer within window, nonexpired/nonexhausted enabled passcode, or enabled routine (not necessarily inside its schedule).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "access:read"
        ],
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          },
          {
            "name": "active",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            },
            "description": ""
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "timer",
                "passcode",
                "routine"
              ]
            },
            "description": ""
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "description": "",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Use pagination.cursor; this endpoint uses a grant ID cursor."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnlockPage"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/unlock/{id}": {
      "get": {
        "operationId": "get_v1_unlock_id",
        "summary": "Read one access grant",
        "description": "Includes revoked grants; ownership is scoped to selected building.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "access:read"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unlock"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string",
                  "description": "Quoted decimal version, e.g. \"3\"."
                },
                "description": "Supply this exact quoted value as If-Match when updating."
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "patch": {
        "operationId": "patch_v1_unlock_id",
        "summary": "Update an access grant",
        "description": "Requires If-Match from GET. Missing/malformed version: 428 VERSION_REQUIRED; changed version: 409 VERSION_CONFLICT. Reread before retrying. Revoked grants cannot be reopened. Changing max_uses resets remaining uses. Creation plan/setup checks apply except body exactly {\"active\":false}; linked-building PATCH still requires active Multi Building even for deactivation. DELETE remains the downgrade cleanup path.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "access:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          },
          {
            "name": "If-Match",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "",
              "pattern": "^\"[0-9]+\"$"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UnlockUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Unlock"
                }
              }
            },
            "headers": {
              "ETag": {
                "schema": {
                  "type": "string",
                  "description": "Quoted decimal version, e.g. \"3\"."
                },
                "description": "Supply this exact quoted value as If-Match when updating."
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_v1_unlock_id",
        "summary": "Revoke access grant",
        "description": "No active subscription required. Soft revocation is repeatable; never erases create idempotency receipt. Missing grant is 404. Other grants may still allow access. No manual-revocation callback is currently dispatched.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "access:write"
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UnlockRevoked"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/logs": {
      "get": {
        "operationId": "get_v1_logs",
        "summary": "Read persisted access activity",
        "description": "No active subscription required. Descending created_at then ID. since/until inclusive. Follow every cursor with unchanged filters; overlap timestamp checkpoints and deduplicate by ID to recover use events. Failed entries and sensitive passcodes may be included. Older records may lack unlock_id. No server streaming endpoint.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "logs:read"
        ],
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "description": "",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "ISO 8601 timestamp.",
              "format": "date-time"
            },
            "description": ""
          },
          {
            "name": "until",
            "in": "query",
            "schema": {
              "type": "string",
              "description": "ISO 8601 timestamp.",
              "format": "date-time"
            },
            "description": ""
          },
          {
            "name": "type",
            "in": "query",
            "schema": {
              "const": "unlock"
            },
            "description": ""
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string",
              "description": ""
            },
            "description": "Opaque base64url cursor; do not construct or decode as part of client logic."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogPage"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks": {
      "post": {
        "operationId": "post_v1_webhooks",
        "summary": "Register callback",
        "description": "Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Maximum five registrations per selected building, including inactive registrations. events is required in REST. Save the secret once. No idempotency support: after uncertain creation, inspect list before repeating; if the secret was lost, delete/recreate deliberately.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "webhooks:write"
        ],
        "x-active-subscription-required": true,
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookCreated"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "operationId": "get_v1_webhooks",
        "summary": "List callback registrations",
        "description": "Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Newest first, no pagination. Secret is never returned; optional status timestamps are omitted until set.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "webhooks:write"
        ],
        "x-active-subscription-required": true,
        "parameters": [
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhooks"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}": {
      "patch": {
        "operationId": "patch_v1_webhooks_id",
        "summary": "Update callback registration",
        "description": "Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Returns updated fields, failure counters, last success/failure and created/updated timestamps. disabledAt/disabledReason are list-only. No signing-secret rotation endpoint; create a replacement registration.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "webhooks:write"
        ],
        "x-active-subscription-required": true,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Webhook"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "delete": {
        "operationId": "delete_v1_webhooks_id",
        "summary": "Delete callback registration",
        "description": "Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Hard delete; repeating returns 404.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "webhooks:write"
        ],
        "x-active-subscription-required": true,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deleted"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhooks/{id}/test": {
      "post": {
        "operationId": "post_v1_webhooks_id_test",
        "summary": "Send a test callback",
        "description": "Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. One attempt, ten-second timeout; no automatic retries for test. Sends even to inactive registrations. HTTP 200 describes the attempt: inspect success and statusCode. Test does not create access or reset automatic failure state.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "x-required-scopes": [
          "webhooks:write"
        ],
        "x-active-subscription-required": true,
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            }
          },
          {
            "name": "X-Buzzer-Building-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Opaque resource ID, normally 24 hexadecimal characters.",
              "pattern": "^[a-fA-F0-9]{24}$"
            },
            "description": "Required when a parent account has linked buildings, even for its primary building. Omit only for a single-building account. Child keys cannot select siblings."
          }
        ],
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookTest"
                }
              }
            }
          },
          "default": {
            "description": "Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API key from email authentication or owner delegation. Required scopes are listed on each operation as x-required-scopes."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "permission_error",
                  "authentication_error",
                  "invalid_request_error",
                  "not_found_error",
                  "rate_limit_error",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string",
                "description": ""
              },
              "code": {
                "type": "string",
                "description": ""
              },
              "retryable": {
                "type": "boolean",
                "description": "True for 429 and 5xx; does not authorize blindly repeating mutations."
              },
              "next_action": {
                "type": "string",
                "description": ""
              }
            },
            "required": [
              "type",
              "message",
              "code",
              "retryable",
              "next_action"
            ]
          },
          "requestId": {
            "type": "string",
            "description": "Also returned as X-Request-ID."
          }
        },
        "required": [
          "error",
          "requestId"
        ]
      },
      "Health": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "version": {
            "type": "string",
            "description": "Backend package version."
          },
          "uptime": {
            "type": "number",
            "description": "Process uptime in seconds."
          }
        },
        "required": [
          "status",
          "version",
          "uptime"
        ]
      },
      "Plans": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "enum": [
                    "standard_monthly",
                    "standard_yearly",
                    "premium_monthly",
                    "premium_yearly"
                  ]
                },
                "available": {
                  "type": "boolean",
                  "description": ""
                },
                "amount": {
                  "anyOf": [
                    {
                      "type": "integer",
                      "description": "Minor currency units; do not hardcode prices."
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "currency": {
                  "type": "string",
                  "description": ""
                },
                "interval": {
                  "type": "string",
                  "description": ""
                },
                "interval_count": {
                  "type": "integer",
                  "description": ""
                },
                "access": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "timer",
                      "passcode",
                      "routine"
                    ]
                  }
                }
              },
              "required": [
                "id",
                "available",
                "amount",
                "currency",
                "access"
              ]
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "OtpRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "Trimmed and lowercased.",
            "format": "email",
            "maxLength": 254
          }
        },
        "required": [
          "email"
        ]
      },
      "OtpChallenge": {
        "type": "object",
        "properties": {
          "challenge": {
            "type": "string",
            "description": "One-use challenge; expires after 15 minutes."
          },
          "message": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "challenge",
          "message"
        ]
      },
      "VerifyRequest": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "description": "",
            "format": "email"
          },
          "challenge": {
            "type": "string",
            "description": ""
          },
          "otp": {
            "type": "string",
            "description": "Six-digit email sign-in code, not a visitor code.",
            "pattern": "^[0-9]{6}$"
          },
          "purpose": {
            "type": "string",
            "enum": [
              "web"
            ]
          }
        },
        "required": [
          "email",
          "challenge",
          "otp"
        ]
      },
      "OwnerCredential": {
        "type": "object",
        "properties": {
          "apiKey": {
            "type": "string",
            "description": "Secret returned once. Omit purpose for CLI owner key; purpose=web produces a 12-hour website session key."
          },
          "keyPrefix": {
            "type": "string",
            "description": ""
          },
          "name": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "apiKey",
          "keyPrefix",
          "name"
        ]
      },
      "KeyRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Nonblank; trimmed before storage.",
            "default": "Delivery agent",
            "maxLength": 120
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "account:read",
                "access:read",
                "access:write",
                "logs:read",
                "setup:write",
                "billing:write",
                "webhooks:write",
                "keys:write",
                "buildings:write"
              ]
            },
            "minItems": 1,
            "default": [
              "account:read",
              "access:read",
              "access:write",
              "logs:read",
              "webhooks:write"
            ]
          },
          "expires_in_days": {
            "type": "integer",
            "description": "CLI delegation is also bounded by parent expiry.",
            "minimum": 1,
            "maximum": 90,
            "default": 30
          }
        },
        "additionalProperties": false
      },
      "AgentCredential": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "api_key": {
            "type": "string",
            "description": "Returned once; store in secret settings."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "account:read",
                "access:read",
                "access:write",
                "logs:read",
                "setup:write",
                "billing:write",
                "webhooks:write",
                "keys:write",
                "buildings:write"
              ]
            }
          },
          "expires_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "message": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "id",
          "api_key",
          "scopes",
          "expires_at",
          "message"
        ]
      },
      "KeyList": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                  "pattern": "^[a-fA-F0-9]{24}$"
                },
                "name": {
                  "type": "string",
                  "description": ""
                },
                "scopes": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "enum": [
                      "account:read",
                      "access:read",
                      "access:write",
                      "logs:read",
                      "setup:write",
                      "billing:write",
                      "webhooks:write",
                      "keys:write",
                      "buildings:write"
                    ]
                  }
                },
                "expires_at": {
                  "anyOf": [
                    {
                      "type": "string",
                      "description": "ISO 8601 timestamp.",
                      "format": "date-time"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "last_used_at": {
                  "anyOf": [
                    {
                      "type": "string",
                      "description": "ISO 8601 timestamp.",
                      "format": "date-time"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "required": [
                "id",
                "name"
              ]
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "Revoked": {
        "type": "object",
        "properties": {
          "revoked": {
            "const": true
          }
        },
        "required": [
          "revoked"
        ]
      },
      "KeyRevoked": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "revoked": {
            "const": true
          }
        },
        "required": [
          "id",
          "revoked"
        ]
      },
      "Account": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "email": {
            "type": "string",
            "description": ""
          },
          "virtual_number": {
            "anyOf": [
              {
                "type": "string",
                "description": ""
              },
              {
                "type": "null"
              }
            ]
          },
          "subscription": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "NONE",
                  "LITE",
                  "STANDARD",
                  "PREMIUM",
                  "BUSINESS",
                  "MULTI_BUILDING"
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "none",
                  "active",
                  "past_due"
                ]
              }
            },
            "required": [
              "type",
              "status"
            ]
          },
          "usage": {
            "type": "object",
            "properties": {
              "total_unlocks": {
                "type": "integer",
                "description": "Recorded activity count, including failed attempts; not a physical-entry count."
              },
              "active_routines": {
                "type": "integer",
                "description": "Stored activated rules; not necessarily currently effective."
              },
              "shared_users": {
                "type": "integer",
                "description": "Number of users associated with the number."
              }
            },
            "required": [
              "total_unlocks",
              "active_routines",
              "shared_users"
            ]
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "buildings": {
            "type": "object",
            "properties": {
              "count": {
                "type": "integer",
                "description": ""
              },
              "selection_required": {
                "type": "boolean",
                "description": ""
              },
              "list_command": {
                "type": "string",
                "description": ""
              }
            },
            "required": [
              "count",
              "selection_required",
              "list_command"
            ]
          },
          "capabilities": {
            "type": "object",
            "properties": {
              "multi_building": {
                "type": "boolean",
                "description": ""
              },
              "access_types": {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "timer",
                    "passcode",
                    "routine"
                  ]
                }
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": "May contain owner for legacy unscoped keys."
                }
              }
            },
            "required": [
              "multi_building",
              "access_types",
              "scopes"
            ]
          },
          "readiness": {
            "type": "object",
            "properties": {
              "can_create_access": {
                "type": "boolean",
                "description": "Readiness hint; creation still validates plan, scope, setup, and selected building."
              },
              "building_connection": {
                "type": "string",
                "enum": [
                  "not_provisioned",
                  "previous_buzz_recorded",
                  "unverified"
                ]
              },
              "physical_entry_verified": {
                "const": false
              }
            },
            "required": [
              "can_create_access",
              "building_connection",
              "physical_entry_verified"
            ]
          },
          "next_actions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "command": {
                  "type": "string",
                  "description": ""
                },
                "reason": {
                  "type": "string",
                  "description": ""
                }
              },
              "required": [
                "command",
                "reason"
              ]
            }
          }
        },
        "required": [
          "id",
          "email",
          "virtual_number",
          "subscription",
          "usage",
          "created_at",
          "buildings",
          "capabilities",
          "readiness",
          "next_actions"
        ]
      },
      "CheckoutRequest": {
        "type": "object",
        "properties": {
          "plan": {
            "type": "string",
            "enum": [
              "standard_monthly",
              "standard_yearly",
              "premium_monthly",
              "premium_yearly"
            ]
          }
        },
        "required": [
          "plan"
        ],
        "additionalProperties": false
      },
      "Checkout": {
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "status": {
                "const": "none"
              },
              "payment_required": {
                "const": false
              },
              "next_action": {
                "type": "string",
                "description": ""
              }
            },
            "required": [
              "status",
              "payment_required",
              "next_action"
            ]
          },
          {
            "type": "object",
            "properties": {
              "id": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "Stripe checkout session ID."
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "request_id": {
                "type": "string",
                "description": ""
              },
              "plan": {
                "type": "string",
                "enum": [
                  "standard_monthly",
                  "standard_yearly",
                  "premium_monthly",
                  "premium_yearly"
                ]
              },
              "url": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": "",
                    "format": "uri"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "status": {
                "type": "string",
                "enum": [
                  "preparing",
                  "open",
                  "complete",
                  "expired",
                  "failed"
                ]
              },
              "payment_required": {
                "type": "boolean",
                "description": ""
              },
              "next_action": {
                "type": "string",
                "description": ""
              },
              "message": {
                "type": "string",
                "description": ""
              }
            },
            "required": [
              "id",
              "request_id",
              "plan",
              "url",
              "status",
              "payment_required",
              "next_action",
              "message"
            ]
          }
        ]
      },
      "CheckoutExpired": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": ""
          },
          "status": {
            "const": "expired"
          },
          "payment_required": {
            "const": false
          }
        },
        "required": [
          "id",
          "status",
          "payment_required"
        ]
      },
      "Portal": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Hosted Stripe billing-management URL.",
            "format": "uri"
          }
        },
        "required": [
          "url"
        ]
      },
      "Setup": {
        "type": "object",
        "properties": {
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "virtual_number": {
            "anyOf": [
              {
                "type": "string",
                "description": ""
              },
              {
                "type": "null"
              }
            ]
          },
          "unlock_tone": {
            "anyOf": [
              {
                "type": "string",
                "description": ""
              },
              {
                "type": "null"
              }
            ]
          },
          "last_successful_buzz_at": {
            "anyOf": [
              {
                "type": "string",
                "description": "ISO 8601 timestamp.",
                "format": "date-time"
              },
              {
                "type": "null"
              }
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "awaiting_subscription_or_provisioning",
              "previous_buzz_recorded",
              "building_setup_required"
            ]
          },
          "instructions": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "building_id",
          "virtual_number",
          "unlock_tone",
          "last_successful_buzz_at",
          "status",
          "instructions"
        ]
      },
      "SetupRequest": {
        "type": "object",
        "properties": {
          "unlock_tone": {
            "type": "string",
            "description": "Exact building release keypress; test at the entrance.",
            "pattern": "^[0-9#*]{1,3}$"
          }
        },
        "required": [
          "unlock_tone"
        ],
        "additionalProperties": false
      },
      "SetupUpdated": {
        "type": "object",
        "properties": {
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "virtual_number": {
            "type": "string",
            "description": ""
          },
          "unlock_tone": {
            "type": "string",
            "description": ""
          },
          "physical_test_required": {
            "const": true
          }
        },
        "required": [
          "building_id",
          "virtual_number",
          "unlock_tone",
          "physical_test_required"
        ]
      },
      "Address": {
        "type": "object",
        "properties": {
          "street": {
            "type": "string",
            "description": ""
          },
          "city": {
            "type": "string",
            "description": ""
          },
          "state": {
            "type": "string",
            "description": ""
          },
          "zip": {
            "type": "string",
            "description": ""
          },
          "country": {
            "type": "string",
            "description": ""
          }
        }
      },
      "Buildings": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                  "pattern": "^[a-fA-F0-9]{24}$"
                },
                "label": {
                  "type": "string",
                  "description": ""
                },
                "address": {
                  "anyOf": [
                    {
                      "$ref": "#/components/schemas/Address"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "virtual_number": {
                  "anyOf": [
                    {
                      "type": "string",
                      "description": ""
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "is_primary": {
                  "type": "boolean",
                  "description": ""
                },
                "provisioned": {
                  "type": "boolean",
                  "description": ""
                }
              },
              "required": [
                "id",
                "label",
                "address",
                "virtual_number",
                "is_primary",
                "provisioned"
              ]
            }
          },
          "selection_required": {
            "type": "boolean",
            "description": ""
          },
          "next_action": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "data",
          "selection_required",
          "next_action"
        ]
      },
      "BuildingAddPreview": {
        "type": "object",
        "properties": {
          "action": {
            "const": "add"
          },
          "label": {
            "type": "string",
            "description": "Nonblank; trimmed.",
            "maxLength": 120
          },
          "address": {
            "type": "object",
            "properties": {
              "street": {
                "type": "string",
                "description": "",
                "maxLength": 200
              },
              "city": {
                "type": "string",
                "description": "Nonblank; trimmed.",
                "maxLength": 120
              },
              "state": {
                "type": "string",
                "description": "Normalized uppercase.",
                "pattern": "^[A-Za-z]{2}$"
              },
              "zip": {
                "type": "string",
                "description": "",
                "pattern": "^[0-9]{5}(-[0-9]{4})?$"
              },
              "country": {
                "const": "US",
                "default": "US"
              }
            },
            "required": [
              "city",
              "state",
              "zip"
            ],
            "additionalProperties": false
          },
          "unlock_tone": {
            "type": "string",
            "description": "Exact building release keypress; test at the entrance.",
            "pattern": "^[0-9#*]{1,3}$"
          },
          "phone_number": {
            "type": "string",
            "description": "Resident forwarding number. Defaults to parent phone if omitted; a valid number is still required.",
            "pattern": "^\\+[1-9][0-9]{6,14}$"
          }
        },
        "required": [
          "action",
          "label",
          "address",
          "unlock_tone"
        ],
        "additionalProperties": false
      },
      "BuildingRemovePreview": {
        "type": "object",
        "properties": {
          "action": {
            "const": "remove"
          },
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          }
        },
        "required": [
          "action",
          "building_id"
        ],
        "additionalProperties": false
      },
      "BuildingPreview": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/BuildingAddPreview"
          },
          {
            "$ref": "#/components/schemas/BuildingRemovePreview"
          }
        ]
      },
      "QuoteRequest": {
        "type": "object",
        "properties": {
          "quote_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          }
        },
        "required": [
          "quote_id"
        ],
        "additionalProperties": false
      },
      "BuildingOperation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "action": {
            "type": "string",
            "enum": [
              "add",
              "remove"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "quoted",
              "expired",
              "running",
              "succeeded",
              "reconciliation_required"
            ]
          },
          "input": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string",
                "description": ""
              },
              "address": {
                "$ref": "#/components/schemas/Address"
              },
              "unlock_tone": {
                "type": "string",
                "description": ""
              },
              "phone_number": {
                "type": "string",
                "description": ""
              },
              "building_id": {
                "type": "string",
                "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                "pattern": "^[a-fA-F0-9]{24}$"
              },
              "virtual_number": {
                "anyOf": [
                  {
                    "type": "string",
                    "description": ""
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "effects": {
                "type": "array",
                "items": {
                  "type": "string",
                  "description": ""
                }
              }
            },
            "description": "Add: normalized label/address/unlock_tone/phone_number. Remove: building_id/label/virtual_number/effects. Inspect the exact snapshot before execution."
          },
          "billing": {
            "type": "object",
            "properties": {
              "subscription_id": {
                "type": "string",
                "description": ""
              },
              "price_id": {
                "type": "string",
                "description": ""
              },
              "current_quantity": {
                "type": "integer",
                "description": ""
              },
              "new_quantity": {
                "type": "integer",
                "description": ""
              },
              "currency": {
                "type": "string",
                "description": ""
              },
              "next_invoice_amount_due": {
                "type": "integer",
                "description": "Entire upcoming invoice estimate in minor currency units, not an immediate charge or refund."
              },
              "per_building_amount": {
                "anyOf": [
                  {
                    "type": "integer",
                    "description": "Can be null for tiered pricing. Do not multiply this to reconstruct the invoice."
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "interval": {
                "type": "string",
                "description": ""
              },
              "proration_date": {
                "type": "integer",
                "description": "Unix seconds used for the preview and execution."
              },
              "note": {
                "type": "string",
                "description": ""
              }
            },
            "required": [
              "subscription_id",
              "price_id",
              "current_quantity",
              "new_quantity",
              "currency",
              "next_invoice_amount_due",
              "per_building_amount",
              "proration_date",
              "note"
            ]
          },
          "expires_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "building_id": {
            "anyOf": [
              {
                "type": "string",
                "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                "pattern": "^[a-fA-F0-9]{24}$"
              },
              {
                "type": "null"
              }
            ]
          },
          "request_id": {
            "anyOf": [
              {
                "type": "string",
                "description": ""
              },
              {
                "type": "null"
              }
            ]
          },
          "result": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "building_id": {
                    "type": "string",
                    "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                    "pattern": "^[a-fA-F0-9]{24}$"
                  },
                  "label": {
                    "type": "string",
                    "description": ""
                  },
                  "virtual_number": {
                    "type": "string",
                    "description": ""
                  },
                  "physical_test_required": {
                    "const": true
                  },
                  "deleted": {
                    "const": true
                  },
                  "released_number": {
                    "anyOf": [
                      {
                        "type": "string",
                        "description": ""
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "building_id"
                ],
                "description": "Add result: building_id, label, virtual_number, physical_test_required. Remove result: building_id, deleted, released_number. Null until succeeded."
              },
              {
                "type": "null"
              }
            ]
          },
          "next_action": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "id",
          "action",
          "status",
          "input",
          "billing",
          "expires_at",
          "building_id",
          "request_id",
          "result",
          "next_action"
        ]
      },
      "TimerCreate": {
        "type": "object",
        "properties": {
          "type": {
            "const": "timer"
          },
          "label": {
            "type": "string",
            "description": "Defaults to the grant type when omitted or empty on creation.",
            "maxLength": 120
          },
          "duration_minutes": {
            "type": "integer",
            "description": "Starts/restarts the timer immediately.",
            "minimum": 1,
            "maximum": 1440
          }
        },
        "required": [
          "type",
          "duration_minutes"
        ],
        "additionalProperties": false
      },
      "PasscodeCreate": {
        "type": "object",
        "properties": {
          "type": {
            "const": "passcode"
          },
          "label": {
            "type": "string",
            "description": "Defaults to the grant type when omitted or empty on creation.",
            "maxLength": 120
          },
          "code": {
            "type": "string",
            "description": "Omit to generate exactly four digits. Custom codes retain app compatibility: 1–4 digits except 1 alone. Preserve leading zeroes. Six-digit visitor codes are rejected.",
            "pattern": "^(?!1$)[0-9]{1,4}$"
          },
          "max_uses": {
            "type": "integer",
            "description": "On update, resets both maximum and remaining uses.",
            "minimum": 1,
            "maximum": 100,
            "default": 1
          },
          "voice_enabled": {
            "type": "boolean",
            "description": "Allow spoken-code recognition.",
            "default": false
          },
          "expires_at": {
            "type": "string",
            "description": "Future ISO timestamp with Z or numeric timezone offset, no more than seven days ahead. Cannot combine with expires_in_minutes.",
            "format": "date-time"
          },
          "expires_in_minutes": {
            "type": "integer",
            "description": "Default expiry is 60 minutes. Do not combine with expires_at.",
            "minimum": 1,
            "maximum": 10080,
            "default": 60
          }
        },
        "required": [
          "type"
        ],
        "additionalProperties": false,
        "not": {
          "required": [
            "expires_at",
            "expires_in_minutes"
          ]
        }
      },
      "RoutineCreate": {
        "type": "object",
        "properties": {
          "type": {
            "const": "routine"
          },
          "label": {
            "type": "string",
            "description": "Defaults to the grant type when omitted or empty on creation.",
            "maxLength": 120
          },
          "days": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Day name, case-insensitive: sunday, monday, tuesday, wednesday, thursday, friday, saturday."
            },
            "description": "Case-insensitive input. Only one day, all five weekdays, both weekend days, or all seven days are supported. Arbitrary subsets and duplicates are not supported.",
            "minItems": 1,
            "maxItems": 7
          },
          "start": {
            "type": "string",
            "description": "24-hour HH:MM. End must be later than start on the same day; no overnight window.",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$"
          },
          "end": {
            "type": "string",
            "description": "24-hour HH:MM. End must be later than start on the same day; no overnight window.",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$"
          },
          "timezone": {
            "type": "string",
            "description": "Valid IANA timezone, such as America/Los_Angeles."
          }
        },
        "required": [
          "type",
          "days",
          "start",
          "end",
          "timezone"
        ],
        "additionalProperties": false
      },
      "UnlockCreate": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/TimerCreate"
          },
          {
            "$ref": "#/components/schemas/PasscodeCreate"
          },
          {
            "$ref": "#/components/schemas/RoutineCreate"
          }
        ]
      },
      "TimerUpdate": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Defaults to the grant type when omitted or empty on creation.",
            "maxLength": 120
          },
          "active": {
            "type": "boolean",
            "description": "False ends timer now; true restarts with stored duration unless a new duration is supplied."
          },
          "duration_minutes": {
            "type": "integer",
            "description": "Starts/restarts the timer immediately.",
            "minimum": 1,
            "maximum": 1440
          }
        },
        "additionalProperties": false,
        "minProperties": 1
      },
      "PasscodeUpdate": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Defaults to the grant type when omitted or empty on creation.",
            "maxLength": 120
          },
          "active": {
            "type": "boolean",
            "description": ""
          },
          "code": {
            "type": "string",
            "description": "Optional replacement code; omission leaves the current code unchanged. Same 1–4 digit app-compatible validation as creation.",
            "pattern": "^(?!1$)[0-9]{1,4}$"
          },
          "max_uses": {
            "type": "integer",
            "description": "Optional; supplying it resets both maximum and remaining uses.",
            "minimum": 1,
            "maximum": 100
          },
          "voice_enabled": {
            "type": "boolean",
            "description": ""
          },
          "expires_at": {
            "type": "string",
            "description": "Future ISO timestamp with Z or numeric timezone offset, no more than seven days ahead. Cannot combine with expires_in_minutes.",
            "format": "date-time"
          }
        },
        "additionalProperties": false,
        "minProperties": 1
      },
      "RoutineUpdate": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Defaults to the grant type when omitted or empty on creation.",
            "maxLength": 120
          },
          "active": {
            "type": "boolean",
            "description": ""
          },
          "days": {
            "type": "array",
            "items": {
              "type": "string",
              "description": "Day name, case-insensitive: sunday, monday, tuesday, wednesday, thursday, friday, saturday."
            },
            "description": "Case-insensitive input. Only one day, all five weekdays, both weekend days, or all seven days are supported. Arbitrary subsets and duplicates are not supported.",
            "minItems": 1,
            "maxItems": 7
          },
          "start": {
            "type": "string",
            "description": "24-hour HH:MM. End must be later than start on the same day; no overnight window.",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$"
          },
          "end": {
            "type": "string",
            "description": "24-hour HH:MM. End must be later than start on the same day; no overnight window.",
            "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$"
          },
          "timezone": {
            "type": "string",
            "description": "IANA timezone."
          }
        },
        "additionalProperties": false,
        "dependentRequired": {
          "start": [
            "end"
          ],
          "end": [
            "start"
          ]
        },
        "minProperties": 1
      },
      "UnlockUpdate": {
        "anyOf": [
          {
            "$ref": "#/components/schemas/TimerUpdate"
          },
          {
            "$ref": "#/components/schemas/PasscodeUpdate"
          },
          {
            "$ref": "#/components/schemas/RoutineUpdate"
          }
        ],
        "description": "Nonempty body; fields must match existing grant type. Type cannot change. Include start and end together if changing either. expires_in_minutes is creation-only."
      },
      "Unlock": {
        "type": "object",
        "properties": {
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "type": {
            "type": "string",
            "enum": [
              "timer",
              "passcode",
              "routine"
            ]
          },
          "label": {
            "type": "string",
            "description": ""
          },
          "active": {
            "type": "boolean",
            "description": "For routines, enabled does not mean currently inside the schedule."
          },
          "version": {
            "type": "integer",
            "description": ""
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "inactive",
              "expired",
              "exhausted",
              "revoked"
            ]
          },
          "request_id": {
            "type": "string",
            "description": "Original create idempotency key, absent on older app-created rules."
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "duration_minutes": {
            "type": "integer",
            "description": ""
          },
          "expires_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "code": {
            "type": "string",
            "description": "Sensitive visitor code, present for passcodes."
          },
          "max_uses": {
            "anyOf": [
              {
                "type": "integer",
                "description": ""
              },
              {
                "type": "null"
              }
            ]
          },
          "remaining_uses": {
            "anyOf": [
              {
                "type": "integer",
                "description": ""
              },
              {
                "type": "null"
              }
            ]
          },
          "voice_enabled": {
            "type": "boolean",
            "description": ""
          },
          "days": {
            "type": "array",
            "items": {
              "type": "string",
              "description": ""
            }
          },
          "start": {
            "type": "string",
            "description": ""
          },
          "end": {
            "type": "string",
            "description": ""
          },
          "timezone": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "building_id",
          "id",
          "type",
          "label",
          "active",
          "version",
          "status",
          "created_at"
        ]
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "cursor": {
            "anyOf": [
              {
                "type": "string",
                "description": "Pass unchanged into the next request with the same filters and building."
              },
              {
                "type": "null"
              }
            ]
          },
          "has_more": {
            "type": "boolean",
            "description": ""
          }
        },
        "required": [
          "cursor",
          "has_more"
        ]
      },
      "UnlockPage": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Unlock"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "required": [
          "data",
          "pagination"
        ]
      },
      "UnlockRevoked": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "deleted": {
            "const": true
          },
          "status": {
            "const": "revoked"
          }
        },
        "required": [
          "id",
          "deleted",
          "status"
        ]
      },
      "Log": {
        "type": "object",
        "properties": {
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "unlock_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "type": {
            "const": "unlock"
          },
          "unlock_type": {
            "type": "string",
            "description": "timer, passcode, routine, or unknown(N) for legacy values."
          },
          "name": {
            "type": "string",
            "description": ""
          },
          "succeeded": {
            "type": "boolean",
            "description": ""
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "passcode": {
            "type": "string",
            "description": "May contain an attempted or accepted code. Treat activity data as sensitive."
          }
        },
        "required": [
          "building_id",
          "id",
          "type",
          "name",
          "succeeded",
          "created_at"
        ]
      },
      "LogPage": {
        "type": "object",
        "properties": {
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Log"
            }
          },
          "pagination": {
            "$ref": "#/components/schemas/Pagination"
          }
        },
        "required": [
          "building_id",
          "data",
          "pagination"
        ]
      },
      "WebhookCreate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "Production: HTTPS, no URL credentials, DNS must resolve to supported public IPv4. Private/reserved destinations and IPv6-only endpoints are unsupported. Redirects are not followed.",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "unlock.created",
                "unlock.completed",
                "unlock.expired",
                "unlock.revoked",
                "access.denied"
              ]
            },
            "minItems": 1,
            "description": "Required in REST. The CLI alone defaults to unlock.completed."
          },
          "description": {
            "type": "string",
            "description": ""
          }
        },
        "required": [
          "url",
          "events"
        ]
      },
      "WebhookUpdate": {
        "type": "object",
        "properties": {
          "url": {
            "type": "string",
            "description": "",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "unlock.created",
                "unlock.completed",
                "unlock.expired",
                "unlock.revoked",
                "access.denied"
              ]
            },
            "minItems": 1
          },
          "description": {
            "type": "string",
            "description": ""
          },
          "active": {
            "type": "boolean",
            "description": "Setting true resets consecutive failures and clears disabled metadata."
          }
        }
      },
      "Webhook": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "url": {
            "type": "string",
            "description": ""
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "unlock.created",
                "unlock.completed",
                "unlock.expired",
                "unlock.revoked",
                "access.denied"
              ]
            }
          },
          "description": {
            "type": "string",
            "description": ""
          },
          "active": {
            "type": "boolean",
            "description": ""
          },
          "consecutiveFailures": {
            "type": "integer",
            "description": ""
          },
          "lastSuccessAt": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "lastFailureAt": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "disabledAt": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "disabledReason": {
            "type": "string",
            "description": ""
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "active",
          "createdAt"
        ]
      },
      "WebhookCreated": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "url": {
            "type": "string",
            "description": ""
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "unlock.created",
                "unlock.completed",
                "unlock.expired",
                "unlock.revoked",
                "access.denied"
              ]
            }
          },
          "description": {
            "type": "string",
            "description": ""
          },
          "active": {
            "type": "boolean",
            "description": ""
          },
          "secret": {
            "type": "string",
            "description": "64 hex characters, returned only here. Use the string itself as the HMAC key, not hex-decoded bytes."
          },
          "createdAt": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          }
        },
        "required": [
          "id",
          "url",
          "events",
          "active",
          "secret",
          "createdAt"
        ]
      },
      "Webhooks": {
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Webhook"
            }
          }
        },
        "required": [
          "data"
        ]
      },
      "Deleted": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "deleted": {
            "const": true
          }
        },
        "required": [
          "id",
          "deleted"
        ]
      },
      "WebhookTest": {
        "type": "object",
        "properties": {
          "success": {
            "type": "boolean",
            "description": "Inspect this even when the API returns HTTP 200."
          },
          "statusCode": {
            "type": "integer",
            "description": "Present when endpoint responded."
          },
          "latencyMs": {
            "type": "integer",
            "description": ""
          },
          "errorMessage": {
            "type": "string",
            "description": "May be present for a transport failure."
          }
        },
        "required": [
          "success",
          "latencyMs"
        ]
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "event": {
            "type": "string",
            "enum": [
              "unlock.completed",
              "unlock.revoked",
              "access.denied",
              "webhook.test"
            ]
          },
          "eventId": {
            "type": "string",
            "description": "Deduplicate this ID; retries retain it."
          },
          "building_id": {
            "type": "string",
            "description": "Opaque resource ID, normally 24 hexadecimal characters.",
            "pattern": "^[a-fA-F0-9]{24}$"
          },
          "created_at": {
            "type": "string",
            "description": "ISO 8601 timestamp.",
            "format": "date-time"
          },
          "data": {
            "type": "object",
            "properties": {
              "routine_id": {
                "type": "string",
                "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                "pattern": "^[a-fA-F0-9]{24}$"
              },
              "unlock_id": {
                "type": "string",
                "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                "pattern": "^[a-fA-F0-9]{24}$"
              },
              "activity_id": {
                "type": "string",
                "description": "Opaque resource ID, normally 24 hexadecimal characters.",
                "pattern": "^[a-fA-F0-9]{24}$"
              },
              "occurred_at": {
                "type": "string",
                "description": "ISO 8601 timestamp.",
                "format": "date-time"
              },
              "name": {
                "type": "string",
                "description": ""
              },
              "unlock_type": {
                "type": "integer",
                "description": "Numeric callback values: 1 timer, 2 passcode, 3 routine. Log values are strings."
              },
              "reason": {
                "const": "uses_exhausted"
              },
              "attempted_passcode": {
                "type": "string",
                "description": "Denied events can contain the attempted digits or speech. Do not log indiscriminately."
              },
              "message": {
                "type": "string",
                "description": ""
              }
            },
            "description": "unlock.completed: routine_id, unlock_id, activity_id, occurred_at, name, numeric unlock_type. unlock.revoked: routine_id, name, reason. access.denied: attempted_passcode (may be omitted). webhook.test: message only."
          }
        },
        "required": [
          "event",
          "eventId",
          "building_id",
          "data"
        ]
      }
    }
  },
  "externalDocs": {
    "url": "https://buzzerapi.com/api-reference",
    "description": "Human-readable reference, error catalog, retry rules, and signature verification example."
  },
  "webhooks": {
    "buzzerEvent": {
      "post": {
        "operationId": "receiveBuzzerEvent",
        "summary": "Receive a registered building callback",
        "description": "Delivered to the URL registered with POST /v1/webhooks. Best effort, up to three attempts for live events; test deliveries have one attempt. Validate the signature using the raw body and string secret, deduplicate eventId, and promptly acknowledge. created_at is absent for webhook.test. unlock.created and unlock.expired are not currently dispatched.",
        "security": [],
        "parameters": [
          {
            "name": "X-Buzzer-Signature",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "HMAC-SHA256 of timestamp + period + raw body, using the returned secret as a string.",
              "pattern": "^t=[0-9]+,v1=[a-f0-9]{64}$"
            }
          },
          {
            "name": "X-Buzzer-Event",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "unlock.completed",
                "unlock.revoked",
                "access.denied",
                "webhook.test"
              ]
            }
          },
          {
            "name": "X-Buzzer-Delivery-Id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "description": "Same as eventId."
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Acknowledged; no further retry."
          },
          "default": {
            "description": "Network errors, 429 and 5xx may be retried for live events. Other statuses are failures without retry."
          }
        }
      }
    }
  }
}
