BUZZER API / REST REFERENCE

REST endpoints.
Requests and responses.

API reference

The public API endpoint is https://api.buzzerapi.com. Check service health before integrating. Checkout and the endpoint are being prepared.

Coverage: 37 public /v1 operations. The reference covers the public API only. Download the OpenAPI 3.1 contract for AI agents and tools, or start with the API guide. Schemas preserve the API's actual casing, including apiKey, requestId, and timestamp fields.

Spec discovery

The machine-readable contract is https://www.buzzerapi.com/openapi.json (OpenAPI 3.1). The API host also serves it: GET https://api.buzzerapi.com/v1/openapi.json is public, not rate limited, allows any origin, and returns 302 to the website copy, so follow redirects. GET /v1/health includes openapi_url (this contract) and docs_url (the AI agent guide).

curl -sSL https://api.buzzerapi.com/v1/openapi.json -o openapi.json
curl -sS https://api.buzzerapi.com/v1/health
# {"status":"ok",...,"openapi_url":"https://www.buzzerapi.com/openapi.json","docs_url":"https://www.buzzerapi.com/agents.md"}

HTTP, authentication, and limits

Use HTTPS; a plain-HTTP request returns 426 HTTPS_REQUIRED with an Upgrade: TLS/1.2 header, and a key sent that way should be treated as exposed. Send Authorization: Bearer YOUR_API_KEY and Content-Type: application/json for bodies. Never send a key as a query parameter. Use a server-side client; the backend is not configured for arbitrary browser origins. The website uses its own server-side session bridge. JSON bodies are limited to 100 KB; a larger body returns 413 PAYLOAD_TOO_LARGE, which is not retryable. Do not send files or card details.

Responses use X-Request-ID and normally Cache-Control: no-store; public plans explicitly allow caching. Selected-building endpoints return X-Buzzer-Building-Id. Treat identifiers as opaque, timestamps as ISO 8601 unless explicitly Unix seconds, and money as minor currency units.

Account's planAuthenticated API requests per 15-minute window, per API key
BuzzerAPI2,000
No active subscription100

Public, sign-in, and verification endpoints have their own limits. Sign-in and verification codes are single-use, expire, and are rate- and attempt-limited. On 429, wait the Retry-After seconds.

Every rate-limited response, successful or not, carries RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset (seconds until the window resets). A 429 sends Retry-After: the number of seconds to wait. PHONE_CODE_ATTEMPTS has no Retry-After: request a new code instead. /v1/health and /v1/openapi.json are not rate limited. Back off with jitter after a 429, and inspect mutation state before repeating anything with side effects.

Access rules can be created, edited, activated, deactivated, and revoked before subscribing or connecting a building; no subscription or virtual number is needed. They start opening the door once a virtual number is connected, because nothing can open a door until that number receives a call. Account, setup, access, and log reads and access revocation stay available after subscription expiry. If the account's current plan doesn't include a rule type, that type returns PLAN_UPGRADE_REQUIRED, except for the exact body {"enabled":false}. Release-tone changes on the primary building need only an owned number. Linked-building writes, including access PATCH, require an active BuzzerAPI subscription, so use DELETE for downgrade cleanup.

Multiple buildings

BuzzerAPI supports managing multiple buildings. Each building uses one unit of the same subscription price. List available buildings with GET /v1/buildings, then send X-Buzzer-Building-Id with setup, access, and logs requests for the intended entrance. Accounts with linked buildings must select a building explicitly, including the primary one.

Adding or removing a linked building is a separate, quoted operation. Use POST /v1/buildings/preview to review the building and billing effects before executing the quote. Buildings can be added in the United States or Canada; the virtual number is local to the building's country. See the multiple-building guide for setup, permissions, and recovery steps.

REST examples

These examples use https://api.buzzerapi.com and assume authentication and physical building setup are complete. Read onboarding for email authentication and hosted payment.

# Create a one-use, four-digit visitor code. Save the returned grant ID.
curl -sS "https://api.buzzerapi.com/v1/unlock" \
  -H "Authorization: Bearer $BUZZER_API_KEY" \
  -H "X-Buzzer-Building-Id: $BUILDING_ID" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: delivery-example-001' \
  --data '{"type":"passcode","label":"Delivery","enabled":true,"remaining_uses":1,"expires_in_minutes":60}'

# Read the current grant and ETag before an update.
curl -i "https://api.buzzerapi.com/v1/unlock/$UNLOCK_ID" \
  -H "Authorization: Bearer $BUZZER_API_KEY" \
  -H "X-Buzzer-Building-Id: $BUILDING_ID"

# Replace "3" with the exact quoted ETag from that GET.
curl -sS -X PATCH "https://api.buzzerapi.com/v1/unlock/$UNLOCK_ID" \
  -H "Authorization: Bearer $BUZZER_API_KEY" \
  -H "X-Buzzer-Building-Id: $BUILDING_ID" \
  -H 'Content-Type: application/json' -H 'If-Match: "3"' \
  --data '{"enabled":false}'

# Fetch an activity page; repeat with the returned pagination.cursor.
# Optional filters: type=unlock|call, unlock_id=ID, succeeded=true|false, q=TEXT (searches entry names).
curl -sS -G "https://api.buzzerapi.com/v1/logs" \
  -H "Authorization: Bearer $BUZZER_API_KEY" \
  -H "X-Buzzer-Building-Id: $BUILDING_ID" \
  --data-urlencode 'since=2026-09-19T00:00:00Z' --data-urlencode 'limit=100'
# Add --data-urlencode "cursor=$CURSOR" for subsequent pages.

Creation returns a grant like this (illustrative values, optional fields omitted):

{
  "building_id": "507f1f77bcf86cd799439011",
  "id": "507f1f77bcf86cd799439012",
  "type": "passcode", "label": "Delivery", "enabled": true,
  "version": 0, "status": "active", "request_id": "delivery-example-001",
  "created_at": "2026-09-19T12:00:00Z", "ends_at": "2026-09-19T13:00:00Z",
  "access_code": "0427", "remaining_uses": 1, "enter_code_with_voice": false
}

To page safely, retain the same building and filters while pagination.has_more is true. Unlock lists use an ID cursor; logs use an opaque encoded cursor. For ongoing activity recovery, persist a timestamp checkpoint, overlap the next polling window, and deduplicate by log ID. There is no streaming endpoint; poll GET /v1/logs, or register a webhook to be told when access is used.

Idempotency

Mutations that create something take an Idempotency-Key header so a retry after a timeout cannot create it twice.

EndpointIdempotency-Key
POST /v1/unlockRequired. Scoped to the selected building.
POST /v1/billing/checkoutRequired. Used as the checkout request_id.
POST /v1/buildings, DELETE /v1/buildings/{id}Required. Tied to one quote.
POST /v1/webhooksOptional.
  • Format: 16 to 100 letters, digits, hyphens, or underscores (^[a-zA-Z0-9_-]{16,100}$). A missing or malformed required key returns 400 IDEMPOTENCY_KEY_REQUIRED. Generate one per intended action, such as a UUID, and store it before sending.
  • Same key, same body: returns the original resource instead of creating another. Access creation replays with HTTP 200 and Idempotency-Replayed: true, as does webhook creation; checkout and building execution replay the stored checkout or operation.
  • Same key, different body: 409 IDEMPOTENCY_CONFLICT. Reuse the original body, or choose a new key for a genuinely new action.
  • Keys never expire. A key keeps pointing at its original result.
  • Replays never reopen access. Replaying the create of a rule that was later revoked returns it with status: "revoked"; it stays revoked. A replay returns current state, such as the current remaining_uses. Create a new rule with a new key to restore access.

PATCH /v1/unlock/{id} uses If-Match with the ETag from GET instead of an idempotency key. DELETE requests are safe to repeat.

Webhooks

Register an HTTPS endpoint to be told when access is used instead of polling logs. Endpoints belong to the account, not a building (do not send X-Buzzer-Building-Id); every event carries building_id and building_label (the same id and label as GET /v1/buildings). An account can have at most 5 endpoints. Endpoints can also be added, tested, and deleted on the account page, which shows the secret once. See the webhooks guide for signature verification in Node and Python.

RequestScopeResult
POST /v1/webhooks {"url","events"}access:write201 endpoint with secret. A replay with the same Idempotency-Key and body returns 200 with the same endpoint and secret plus Idempotency-Replayed: true; a different body returns 409 IDEMPOTENCY_CONFLICT. The secret is never listed.
GET /v1/webhooksaccess:read200 {data:[endpoint]} without secrets
DELETE /v1/webhooks/{id}access:write200 {id, deleted: true}; cancels pending deliveries; safe to repeat
POST /v1/webhooks/{id}/testaccess:write202 {delivery_id}; sends a webhook.test event

The URL must be a publicly reachable https:// URL (max 2048 characters); otherwise 400 INVALID_WEBHOOK_URL. events is optional and defaults to every event type; an unknown type returns 400 INVALID_WEBHOOK_EVENTS. A sixth endpoint returns 409 WEBHOOK_LIMIT.

Event typeWhen it firesdata fields
access.grantedA rule granted accessbuilding_id, building_label, unlock_id, unlock_type, label, remaining_uses (number or null), occurred_at
access.call_forwardedA buzz matched no rule and was forwarded to the resident's phonebuilding_id, building_label, occurred_at
access.deniedA wrong passcode was enteredbuilding_id, building_label, occurred_at
unlock.no_uses_remainingA passcode or timer used its last usebuilding_id, building_label, unlock_id, unlock_type, label, occurred_at
webhook.testSent by POST /v1/webhooks/{id}/testempty object
POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: BuzzerAPI-Webhooks/1
BuzzerAPI-Signature: t=1790000000,v1=5f2b...e9

{"id":"evt_8Jk2...","type":"access.granted","created_at":"2026-09-28T17:04:05.000Z","api_version":"v1",
 "data":{"building_id":"507f1f77bcf86cd799439011","building_label":"Mission St","unlock_id":"507f1f77bcf86cd799439012","unlock_type":"timer",
         "label":"Package delivery","remaining_uses":0,"occurred_at":"2026-09-28T17:04:05.000Z"}}

v1 is the hex HMAC-SHA256 of t, a period, and the raw request body, keyed with the whole endpoint secret, including the whsec_ prefix. Verify it with a constant-time compare and reject timestamps older than 5 minutes. Respond with any 2xx within 5 seconds; redirects are not followed. Anything else is retried at +1 minute, +5 minutes, +30 minutes, and +2 hours (5 attempts in total), then marked failed. A retry repeats the same event id, so deduplicate on it. Delivery order is not guaranteed; order events by data.occurred_at. Delivery never delays the call at the door.

Errors and recovery

{
  "error": {
    "type": "invalid_request_error",
    "message": "Access changed since it was read...",
    "code": "VERSION_CONFLICT", "retryable": false,
    "next_action": "GET /v1/unlock/<id>"
  },
  "requestId": "req_example"
}

Preserve requestId for support. retryable is true for 429 and 5xx; it is not permission to duplicate a mutation. Retry access creation, checkout, and building execution with the original idempotency key and identical body. Access updates use If-Match; after a timeout reread the grant. Building reconciliation requires support instead of another mutation; a failed building addition changed nothing and can be retried with a new preview. A checkout request ID whose checkout ended as failed keeps returning that result, so use a new Idempotency-Key to try again.

HTTP / codesRecovery
400: INVALID_JSON, INVALID_EMAIL, LINKED_ACCOUNT, INVALID_KEY_OPTIONS, INVALID_PLAN, INVALID_TONE, INVALID_ID, INVALID_BUILDING, INVALID_BUILDING_CONTEXT, INVALID_BUILDING_INPUT, INVALID_PHONE_NUMBER, INVALID_PHONE_CODE, PHONE_CODE_EXPIRED, PHONE_VERIFICATION_REQUIRED, FORWARDING_PHONE_NOT_VERIFIED, INVALID_QUOTE, INVALID_UNLOCK, INVALID_TYPE, INVALID_FILTER, INVALID_LIMIT, INVALID_CURSOR, INVALID_DATE, INVALID_SUCCEEDED, INVALID_WEBHOOK_URL, INVALID_WEBHOOK_EVENTS, IDEMPOTENCY_KEY_REQUIRED, WEBSITE_SIGN_IN_REQUIREDCorrect the named input. AI agent keys cannot hold keys:write (INVALID_KEY_OPTIONS). Email sign-in is only for the website (WEBSITE_SIGN_IN_REQUIRED); create an AI agent key on the account page instead. Linked building accounts sign in through the primary account (LINKED_ACCOUNT). A webhook url must be public HTTPS (INVALID_WEBHOOK_URL). Follow returned messages and documented bounds. Use a returned cursor.
401: AUTH_REQUIRED, AUTHENTICATION_REQUIRED, INVALID_KEY, INVALID_OTPSign in on buzzerapi.com and create a new API key on the account page. OTP challenges are single-use, expiring, and attempt-limited.
403: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED, PARENT_BILLING_REQUIRED, PARENT_BUILDING_KEY_REQUIRED, SUBSCRIPTION_REQUIRED, NUMBER_NOT_PROVISIONED, PLAN_UPGRADE_REQUIRED, MULTI_BUILDING_REQUIRED, NUMBER_OWNER_REQUIREDUse a parent-account key with the needed scopes, or manage the subscription. Creating and editing access rules does not return SUBSCRIPTION_REQUIRED; PLAN_UPGRADE_REQUIRED means the account's current plan doesn't include this capability. Manage keys and the forwarding phone from a website sign-in on the account page (WEB_SESSION_REQUIRED). Do not rotate credentials to bypass a plan requirement. NUMBER_OWNER_REQUIRED means an invited member tried to change building settings; only the number’s owner can, so do not retry.
404: NOT_FOUND, ACCOUNT_NOT_FOUND, NUMBER_NOT_FOUND, BUILDING_NOT_FOUND, CUSTOMER_NOT_FOUNDVerify resource ownership and building selection. A number may still need provisioning. An unknown API path or unreleased service can return a non-JSON 404 instead.
409: KEY_LIMIT, PLAN_UNAVAILABLE, SUBSCRIPTION_EXISTS, CHECKOUT_EXISTS, CHECKOUT_PENDING, CHECKOUT_RECONCILIATION_REQUIRED, BUILDING_REQUIRED, BUILDING_BILLING_MISMATCH, BUILDING_LIMIT, INVALID_QUOTE, IDEMPOTENCY_CONFLICT, QUOTE_EXPIRED, QUOTE_STALE, BUILDING_BUSY, BUILDING_ADD_FAILED, GRANT_REVOKED, VERSION_CONFLICT, ALREADY_VERIFIED, PHONE_IN_USE, WEBHOOK_LIMIT, SETUP_REQUIREDInspect existing state. Expired/stale unexecuted quotes need a new preview. Pending checkout needs recovery, not another purchase. Reconciliation/billing mismatch needs support. A failed building addition rolled back fully; request a new preview when ready. For KEY_LIMIT (20 active AI agent keys), revoke an unused key on the account page. A revoked grant requires a deliberately new grant. For WEBHOOK_LIMIT (5 endpoints), delete one first. Access creation and updates no longer return SETUP_REQUIRED.
402: PAYMENT_FAILEDA building addition could not be charged. Update the payment method in the billing portal, then request a fresh preview.
413: PAYLOAD_TOO_LARGESend a smaller body. Only documented JSON fields are accepted.
426: HTTPS_REQUIREDResend over https://. Revoke any key that was sent over plain HTTP.
428: VERSION_REQUIREDGET the grant and send its quoted ETag as If-Match.
429: AUTH_RATE_LIMIT, OTP_RATE_LIMIT, RATE_LIMIT_EXCEEDED, PHONE_CODE_RATE_LIMIT, PHONE_CODE_ATTEMPTS, PHONE_CHANGE_LIMITWait at least Retry-After seconds and reduce polling or request frequency. PHONE_CODE_ATTEMPTS has no Retry-After and needs a new code.
500: INTERNAL_ERROR, FETCH_FAILED; 502: PHONE_CODE_SEND_FAILED; 503: CHECKOUT_UNAVAILABLERetry reads later with backoff. Check a mutation's receipt or resource before repeating it. Persist requestId and contact support if it persists.

error.code is documented as an enum in the Error schema, and each operation lists the codes it can return. New codes may be added; handle an unknown code by its HTTP status and retryable. Hosting/proxy failures may return text or HTML rather than this envelope. Check HTTP status and content type before parsing. Unknown fields are rejected on access, setup, key creation, checkout, and building mutation bodies; other routes may ignore them. Send only documented fields. A 202 building receipt uses the normal operation schema, not the error envelope; inspect status before treating it as success.

Every REST operation

GET /v1/health

Check service health

Health is public and not rate limited; success does not establish account readiness. openapi_url and docs_url point at this contract and the AI agent guide.

Authorization: Public; no key required.

No request body.

Responses

HTTP statusContract
200Success. Health
5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/openapi.json

Fetch the OpenAPI contract

Public, not rate limited, and sent with Access-Control-Allow-Origin: *. Redirects to the website copy of this contract (https://www.buzzerapi.com/openapi.json in production). Follow the redirect.

Authorization: Public; no key required.

No request body.

Responses

HTTP statusContract
302Found. Follow Location to the OpenAPI 3.1 JSON document.

Location: Absolute URL of openapi.json on the website. string

format: uri

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/plans

Read live public prices

Only BuzzerAPI weekly and yearly are listed. Each building uses one subscription unit at the selected price. Public cache max-age=60, shared cache max-age=300.

Authorization: Public; no key required.

No request body.

Responses

HTTP statusContract
200Success. Plans
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/auth/otp

Request email sign-in code

Sign-in codes are single-use, expire, and are rate- and attempt-limited. On 429, wait the Retry-After seconds. Delivery is not proof that the account already exists.

Authorization: Public; no key required.

JSON request body

OtpRequest

Responses

HTTP statusContract
200Success. OtpChallenge
400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_EMAIL, LINKED_ACCOUNT. Error
429Rate limited; wait for Retry-After. Codes: OTP_RATE_LIMIT, AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/auth/verify

Verify email and start a website session

Website sign-in only: purpose must be web, otherwise 400 WEBSITE_SIGN_IN_REQUIRED is returned before the code is consumed. Creates the account if needed. Returns a 12-hour website session key that the website keeps server-side; it is not a browser-storage recommendation. AI agents use keys created on the account page instead. Never disclose apiKey.

Authorization: Public; no key required.

JSON request body

VerifyRequest

Responses

HTTP statusContract
200Success. WebSessionCredential
400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_EMAIL, LINKED_ACCOUNT, WEBSITE_SIGN_IN_REQUIRED. Error
401Missing, invalid, expired, or revoked key. Codes: INVALID_OTP. Error
429Rate limited; wait for Retry-After. Codes: AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

DELETE /v1/auth/key

Revoke the current credential

No extra scope required. Any key can revoke itself; subsequent use fails authentication. AI agent keys survive website sign-out.

Authorization: Bearer key; no additional scope.

No request body.

Responses

HTTP statusContract
200Success. Revoked
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
429Rate limited; wait for Retry-After. Codes: AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/auth/keys

Create an AI agent key

Website session only; AI agent keys get 403 WEB_SESSION_REQUIRED. Keys can be issued before payment with access, setup, billing, and optional building scopes. keys:write or any other unlisted scope returns 400 INVALID_KEY_OPTIONS. At most 20 active AI agent keys (409 KEY_LIMIT). Live access and building mutations still require the corresponding subscription. AI agent keys are independent of website session expiry.

Authorization: Bearer key; keys:write.

JSON request body

KeyRequest

Responses

HTTP statusContract
201Created. AgentCredential
400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_KEY_OPTIONS. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED. Error
409Conflicts with current state; inspect it before retrying. Codes: KEY_LIMIT. Error
429Rate limited; wait for Retry-After. Codes: AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/auth/keys

List every key on the account

Website session only; AI agent keys get 403 WEB_SESSION_REQUIRED. Returns every active key: website sign-ins (type web_session, current true for the calling session) and AI agent keys (type agent). Newest first. Excludes revoked keys, but can include expired ones. No secrets or pagination.

Authorization: Bearer key; keys:write.

No request body.

Responses

HTTP statusContract
200Success. KeyList
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

DELETE /v1/auth/keys/{id}

Revoke any key on the account

Website session only; AI agent keys get 403 WEB_SESSION_REQUIRED. Revokes any active key on the account, including other website sign-ins and the calling session itself, which is then signed out. A malformed ID returns 400 INVALID_ID; an unknown ID or another account's key returns 404 NOT_FOUND.

Authorization: Bearer key; keys:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. KeyRevoked
400Invalid request; correct the input. Codes: INVALID_ID. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED. Error
404Not found for this key or building. Codes: NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/auth/keys/revoke-others

Revoke every other key

Website session only; AI agent keys get 403 WEB_SESSION_REQUIRED. Revokes every active key on the account except the calling website session, including AI agent keys and other website sign-ins. No request body. Returns the number of keys revoked.

Authorization: Bearer key; keys:write.

No request body.

Responses

HTTP statusContract
200Success. KeysRevoked
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: AUTH_RATE_LIMIT. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/account

Inspect account readiness

Account-level response; building header does not select another account. Subscription active and a recorded buzz do not prove physical entry.

Authorization: Bearer key; account:read.

No request body.

Responses

HTTP statusContract
200Success. Account
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: ACCOUNT_NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: FETCH_FAILED, INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/account/phone

Read forwarding phone status

Website session only; AI agent keys and linked-building keys get 403 WEB_SESSION_REQUIRED. A phone verified earlier appears here without another SMS.

Authorization: Bearer key; account:read + keys:write.

No request body.

Responses

HTTP statusContract
200Success. PhoneStatus
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED. Error
404Not found for this key or building. Codes: ACCOUNT_NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/account/phone/verification

Send forwarding phone code

Website session only (403 WEB_SESSION_REQUIRED otherwise). Requires an active subscription and an owned provisioned virtual number. Explicit request only. Verification codes are single-use, expire, and are rate- and attempt-limited, and changing an existing phone is also limited. On 429, wait the Retry-After seconds.

Authorization: Bearer key; account:read + keys:write.

JSON request body

PhoneVerificationRequest

Responses

HTTP statusContract
200Success. PhoneCodeSent
400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_PHONE_NUMBER. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED, SUBSCRIPTION_REQUIRED, NUMBER_NOT_PROVISIONED. Error
404Not found for this key or building. Codes: ACCOUNT_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: ALREADY_VERIFIED, PHONE_IN_USE. Error
429Rate limited; wait for Retry-After. Codes: PHONE_CODE_RATE_LIMIT, PHONE_CHANGE_LIMIT, RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: PHONE_CODE_SEND_FAILED, INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/account/phone/confirmation

Confirm forwarding phone code

Website session only (403 WEB_SESSION_REQUIRED otherwise). Requires an active subscription and owned provisioned virtual number. Codes expire and are attempt-limited; after PHONE_CODE_EXPIRED or PHONE_CODE_ATTEMPTS, request a new code.

Authorization: Bearer key; account:read + keys:write.

JSON request body

PhoneConfirmationRequest

Responses

HTTP statusContract
200Success. PhoneStatus
400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_PHONE_CODE, PHONE_CODE_EXPIRED. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, WEB_SESSION_REQUIRED, SUBSCRIPTION_REQUIRED, NUMBER_NOT_PROVISIONED. Error
404Not found for this key or building. Codes: ACCOUNT_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: PHONE_IN_USE. Error
429Rate limited; wait for Retry-After. Codes: PHONE_CODE_ATTEMPTS, PHONE_CHANGE_LIMIT, RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/billing/plans

Read authenticated price catalog

Same catalog as public plans; no active subscription required.

Authorization: Bearer key; billing:write.

No request body.

Responses

HTTP statusContract
200Success. Plans
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/billing/checkout

Recover latest checkout

Read after a timeout. complete means hosted checkout completed; recheck account.subscription.status and setup. preparing means retry original plan and request ID. Once the subscription a completed checkout created is canceled, this reports none and a new checkout can start.

Authorization: Bearer key; billing:write.

No request body.

Responses

HTTP statusContract
200Success. Checkout
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: CHECKOUT_UNAVAILABLE, INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/billing/checkout

Start or recover hosted checkout

Parent account only. Any existing subscription on the account, including a past_due one, returns SUBSCRIPTION_EXISTS; manage it in the portal. A request ID whose checkout ended failed replays that result; use a new Idempotency-Key to try again. A new request ID for the same plan returns the existing open checkout with its original request_id. One pending checkout: same plan reuses it. Requesting another plan while the existing session is still open (unpaid) expires that session and starts the new plan; a checkout that is being prepared or was already paid returns CHECKOUT_EXISTS. A completed checkout keeps blocking new ones until its subscription is canceled; then start again with a new Idempotency-Key. Same request ID with another plan conflicts. Hosted checkout returns to the account page with checkout=success after payment or checkout=cancel when the customer backs out. CHECKOUT_RECONCILIATION_REQUIRED means an earlier checkout needs support to resolve; contact support instead of purchasing again. No card details accepted by this API.

Authorization: Bearer key; billing:write.

Parameters

ParameterContract
Idempotency-Key
header · required
Stable identifier for this action: 16 to 100 letters, digits, hyphens, or underscores. Reuse the identical body and key after a timeout. Keys never expire.string

pattern: ^[a-zA-Z0-9_-]{16,100}$

JSON request body

CheckoutRequest

Responses

HTTP statusContract
200Success. Checkout
201Created. Checkout
400Invalid request; correct the input. Codes: INVALID_JSON, IDEMPOTENCY_KEY_REQUIRED, INVALID_PLAN. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PARENT_BILLING_REQUIRED. Error
404Not found for this key or building. Codes: ACCOUNT_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: IDEMPOTENCY_CONFLICT, SUBSCRIPTION_EXISTS, CHECKOUT_EXISTS, CHECKOUT_PENDING, CHECKOUT_RECONCILIATION_REQUIRED, PLAN_UNAVAILABLE. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: CHECKOUT_UNAVAILABLE, INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/billing/checkout/{id}/expire

Expire unpaid checkout

Open sessions can be expired; already-expired sessions succeed. Completed checkout returns 409 SUBSCRIPTION_EXISTS. This does not cancel a subscription.

Authorization: Bearer key; billing:write.

Parameters

ParameterContract
id
path · required
string

Stripe checkout session ID.

No request body.

Responses

HTTP statusContract
200Success. CheckoutExpired
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: SUBSCRIPTION_EXISTS. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: CHECKOUT_UNAVAILABLE, INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/billing/portal

Open hosted billing management

Parent account with an existing billing customer. No request body.

Authorization: Bearer key; billing:write.

No request body.

Responses

HTTP statusContract
200Success. Portal
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PARENT_BILLING_REQUIRED. Error
404Not found for this key or building. Codes: CUSTOMER_NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/buildings/{buildingId}/settings

Read one building’s tone and greeting

Read without an active subscription. Invited members can inspect their number but cannot change its settings; editable is false for them. Unprovisioned buildings return virtual_number/unlock_tone/version null and default greeting values. The path explicitly selects the building; no header is needed.

Authorization: Bearer key; account:read.

Parameters

ParameterContract
buildingId
path · required
Explicit building ID from GET /v1/buildings. Child keys can only use their own building. An optional X-Buzzer-Building-Id header must agree with the path.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. BuildingSettings

ETag: Present for provisioned numbers. Supply as If-Match on PATCH. Also exposed without quotes in response.version. string

Quoted opaque 64-hex snapshot token.

400Invalid request; correct the input. Codes: INVALID_BUILDING, INVALID_BUILDING_CONTEXT. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

PATCH /v1/buildings/{buildingId}/settings

Update one building’s tone and greeting

Requires ownership and a provisioned number. Invited members get 403 NUMBER_OWNER_REQUIRED; check editable from GET first. Primary-building writes need no active subscription; linked-building writes require an active BuzzerAPI subscription. Supply the ETag from GET in If-Match. Strong and weak quoted tokens are accepted. Tone and greeting are saved together in one transaction. Omitted fields stay unchanged; unknown fields, nulls and empty patch objects are rejected. Greeting strings preserve whitespace and must be shorter than 120 JavaScript UTF-16 code units (astral characters count twice). A shared legacy greeting is copied so siblings remain unchanged. One competing save wins; others return 409 SETTINGS_VERSION_CONFLICT. After a timeout or conflict, read back and review before deciding to retry; no automatic mutation retry.

Authorization: Bearer key; setup:write.

Parameters

ParameterContract
buildingId
path · required
Explicit building ID from GET /v1/buildings. Child keys can only use their own building. An optional X-Buzzer-Building-Id header must agree with the path.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

If-Match
header · required
string

Quoted ETag from this building’s GET.

pattern: ^(?:W/)?"[a-f0-9]{64}"$

JSON request body

BuildingSettingsPatch

Responses

HTTP statusContract
200Success. BuildingSettingsUpdated

ETag: Present for provisioned numbers. Supply as If-Match on PATCH. Also exposed without quotes in response.version. string

Quoted opaque 64-hex snapshot token.

400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_BUILDING, INVALID_BUILDING_CONTEXT, INVALID_TONE, INVALID_SETTINGS, INVALID_GREETING. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, MULTI_BUILDING_REQUIRED, NUMBER_OWNER_REQUIRED. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND, NUMBER_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: SETTINGS_VERSION_CONFLICT. Error
428Precondition required. Codes: SETTINGS_VERSION_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/buildings/{buildingId}/setup

Inspect one building’s setup

No subscription required for inspection. previous_buzz_recorded requires a successful access-rule unlock; forwarded calls and owner self-calls do not count. It is not proof of physical door release. The path explicitly selects the building; no header is needed.

Authorization: Bearer key; account:read.

Parameters

ParameterContract
buildingId
path · required
Explicit building ID from GET /v1/buildings. Child keys can only use their own building. An optional X-Buzzer-Building-Id header must agree with the path.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. BuildingSetup
400Invalid request; correct the input. Codes: INVALID_BUILDING, INVALID_BUILDING_CONTEXT. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/buildings

List accessible buildings

No pagination. Ordered by creation time then ID. Parent sees primary and children; child sees only itself.

Authorization: Bearer key; account:read.

No request body.

Responses

HTTP statusContract
200Success. Buildings
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY, AUTHENTICATION_REQUIRED. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/buildings

Execute quoted building addition

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. Only one building change runs at a time per account (409 BUILDING_BUSY). An addition that fully rolled back (no number purchased, building removed, billing restored) returns 409 BUILDING_ADD_FAILED, is recorded as failed, and lets another building change start; replaying it returns the same error.

Authorization: Bearer key; account:read + buildings:write.

Parameters

ParameterContract
Idempotency-Key
header · required
Stable identifier for this action: 16 to 100 letters, digits, hyphens, or underscores. Reuse the identical body and key after a timeout. Keys never expire.string

pattern: ^[a-zA-Z0-9_-]{16,100}$

JSON request body

QuoteRequest

Responses

HTTP statusContract
200Success. BuildingOperation
201Created. BuildingOperation
202Pending or uncertain: inspect status and next_action. BuildingOperation
400Invalid request; correct the input. Codes: INVALID_JSON, IDEMPOTENCY_KEY_REQUIRED, INVALID_QUOTE, INVALID_BUILDING_CONTEXT. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
402Payment failed. Codes: PAYMENT_FAILED. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PARENT_BUILDING_KEY_REQUIRED, MULTI_BUILDING_REQUIRED. Error
404Not found for this key or building. Codes: NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: INVALID_QUOTE, IDEMPOTENCY_CONFLICT, QUOTE_EXPIRED, QUOTE_STALE, BUILDING_BUSY, BUILDING_BILLING_MISMATCH, BUILDING_ADD_FAILED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/buildings/preview

Preview addition or removal

Parent account only; do not send building header. Requires an active BuzzerAPI subscription with a matching billed quantity, maximum 20 buildings including primary. Each additional building adds one unit at the same subscription price. Preview purchases nothing, creates a 15-minute quote and invoice snapshot. US and Canadian addresses are supported; a Canadian building receives a Canadian number. Previews are still issued while another operation awaits reconciliation, but cannot be executed until it is resolved. Removal cannot target primary; review release/deletion effects.

Authorization: Bearer key; account:read + buildings:write.

JSON request body

BuildingPreview

Responses

HTTP statusContract
201Created. BuildingOperation
400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_BUILDING_INPUT, INVALID_BUILDING_CONTEXT, INVALID_PHONE_NUMBER, PHONE_VERIFICATION_REQUIRED, FORWARDING_PHONE_NOT_VERIFIED. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PARENT_BUILDING_KEY_REQUIRED, MULTI_BUILDING_REQUIRED. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_LIMIT, BUILDING_BILLING_MISMATCH. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

DELETE /v1/buildings/{id}

Execute quoted building removal

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. Only one building change runs at a time per account (409 BUILDING_BUSY). An addition that fully rolled back (no number purchased, building removed, billing restored) returns 409 BUILDING_ADD_FAILED, is recorded as failed, and lets another building change start; replaying it returns the same error. The JSON DELETE body is required. Permanently releases the number, deletes child rules/activity, revokes child keys, and reduces subscription quantity.

Authorization: Bearer key; account:read + buildings:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

Idempotency-Key
header · required
Stable identifier for this action: 16 to 100 letters, digits, hyphens, or underscores. Reuse the identical body and key after a timeout. Keys never expire.string

pattern: ^[a-zA-Z0-9_-]{16,100}$

JSON request body

QuoteRequest

Responses

HTTP statusContract
200Success. BuildingOperation
202Pending or uncertain: inspect status and next_action. BuildingOperation
400Invalid request; correct the input. Codes: INVALID_JSON, IDEMPOTENCY_KEY_REQUIRED, INVALID_ID, INVALID_QUOTE, INVALID_BUILDING_CONTEXT. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PARENT_BUILDING_KEY_REQUIRED, MULTI_BUILDING_REQUIRED. Error
404Not found for this key or building. Codes: NOT_FOUND, BUILDING_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: INVALID_QUOTE, IDEMPOTENCY_CONFLICT, QUOTE_EXPIRED, QUOTE_STALE, BUILDING_BUSY, BUILDING_BILLING_MISMATCH. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/buildings/operations/{id}

Read building operation receipt

Parent-owned operation. Recovery remains available without an active subscription. A running operation that stops making progress is reported as reconciliation_required. Quotes/receipts are not erased when the quote expires.

Authorization: Bearer key; account:read + buildings:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. BuildingOperation
400Invalid request; correct the input. Codes: INVALID_ID. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PARENT_BUILDING_KEY_REQUIRED. Error
404Not found for this key or building. Codes: NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/unlock

Create an access grant

No subscription or virtual number is needed: rules can be created before subscribing or connecting a building, and they start opening the door once a virtual number is connected. BuzzerAPI includes timer, passcode, and routine for every building; if the account's current plan doesn't include a rule type, creating it returns 403 PLAN_UPGRADE_REQUIRED. A timer with a future starts_at has status scheduled until then. A timer or passcode with remaining_uses lets in that many buzzes, then becomes exhausted; omit it (or send null on a passcode) for unlimited buzzes; a passcode no longer defaults to one use. A timer's window is set by exactly one of expires_in_minutes or ends_at; a passcode can take either one for an expiry, or neither to never expire. type is required and never inferred. A repeated Idempotency-Key with the same body returns 200 with the current grant and Idempotency-Replayed: true (including revoked state and current remaining_uses), never reopens it; the same key with a different body returns 409 IDEMPOTENCY_CONFLICT. Keys never expire and are scoped to the selected building.

Authorization: Bearer key; access:write.

Parameters

ParameterContract
X-Buzzer-Building-Id
header
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.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

Idempotency-Key
header · required
Stable identifier for this action: 16 to 100 letters, digits, hyphens, or underscores. Reuse the identical body and key after a timeout. Keys never expire.string

pattern: ^[a-zA-Z0-9_-]{16,100}$

JSON request body

UnlockCreate

Responses

HTTP statusContract
200Success. Unlock

Location: On new 201: relative /v1/unlock/{id}. string

Idempotency-Replayed: Present on replay. string

constant: "true"

201Created. Unlock

Location: On new 201: relative /v1/unlock/{id}. string

Idempotency-Replayed: Present on replay. string

constant: "true"

400Invalid request; correct the input. Codes: INVALID_JSON, IDEMPOTENCY_KEY_REQUIRED, INVALID_BUILDING, INVALID_TYPE, INVALID_UNLOCK. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PLAN_UPGRADE_REQUIRED, MULTI_BUILDING_REQUIRED. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_REQUIRED, IDEMPOTENCY_CONFLICT. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/unlock

List access grants

No active subscription required. Excludes API-revoked grants. Ordered by descending ID. enabled filters by the on/off switch; status filters by what the rule does right now (status=active matches a timer within its window, a nonexpired/nonexhausted enabled passcode, or an enabled routine, not necessarily inside its schedule).

Authorization: Bearer key; access:read.

Parameters

ParameterContract
X-Buzzer-Building-Id
header
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.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

enabled
query
string

one of: true, false

status
query
string

one of: active, scheduled, inactive, expired, exhausted

type
query
string

one of: timer, passcode, routine

limit
query
integer

minimum: 1
maximum: 100
default: 50

cursor
query
Use pagination.cursor; this endpoint uses a grant ID cursor.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. UnlockPage
400Invalid request; correct the input. Codes: INVALID_BUILDING, INVALID_FILTER, INVALID_TYPE, INVALID_LIMIT, INVALID_CURSOR. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/unlock/{id}

Read one access grant

Includes revoked grants; ownership is scoped to selected building.

Authorization: Bearer key; access:read.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

X-Buzzer-Building-Id
header
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.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. Unlock

ETag: Supply this exact quoted value as If-Match when updating. string

Quoted decimal version, e.g. "3", or its proxy-generated weak form W/"3".

400Invalid request; correct the input. Codes: INVALID_BUILDING, INVALID_ID. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND, NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

PATCH /v1/unlock/{id}

Update an access grant

Requires If-Match from GET. Strong quoted versions and proxy-generated weak ETags are accepted. Parallel updates using the same version permit one write; the others return 409 VERSION_CONFLICT. Missing/malformed version: 428 VERSION_REQUIRED. Reread, review changes, and decide whether to retry. Revoked grants cannot be reopened. remaining_uses (1 to 100) sets a new count; null removes the use limit on timers and passcodes. Turning a timer on (enabled: true) needs expires_in_minutes or ends_at, plus an optional starts_at. On a passcode, expires_in_minutes or ends_at sets a new expiry and ends_at: null removes it. No subscription or virtual number is needed on the primary building; if the account's current plan doesn't include the rule type, PLAN_UPGRADE_REQUIRED is returned except for the exact body {"enabled":false}. Linked-building PATCH still requires an active BuzzerAPI subscription even for deactivation. DELETE remains the downgrade cleanup path.

Authorization: Bearer key; access:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

X-Buzzer-Building-Id
header
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.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

If-Match
header · required
string

Quoted version from the ETag. A weak W/"3" form, which proxies may add when compressing, is accepted too.

pattern: ^(?:W/)?"[0-9]+"$

JSON request body

UnlockUpdate

Responses

HTTP statusContract
200Success. Unlock

ETag: Supply this exact quoted value as If-Match when updating. string

Quoted decimal version, e.g. "3", or its proxy-generated weak form W/"3".

400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_BUILDING, INVALID_ID, INVALID_UNLOCK. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE, PLAN_UPGRADE_REQUIRED, MULTI_BUILDING_REQUIRED. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND, NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_REQUIRED, VERSION_CONFLICT, GRANT_REVOKED. Error
428Precondition required. Codes: VERSION_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

DELETE /v1/unlock/{id}

Revoke access grant

No active subscription required. Soft revocation is repeatable; never erases create idempotency receipt. Missing grant is 404. Other grants may still allow access.

Authorization: Bearer key; access:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

X-Buzzer-Building-Id
header
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.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. UnlockRevoked
400Invalid request; correct the input. Codes: INVALID_BUILDING, INVALID_ID. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND, NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/logs

Read persisted access activity

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. Passcodes are withheld from logs; use an access:read key to retrieve a grant when needed. Calls forwarded to the resident appear with type call. No server streaming endpoint.

Authorization: Bearer key; logs:read.

Parameters

ParameterContract
X-Buzzer-Building-Id
header
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.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

limit
query
integer

minimum: 1
maximum: 100
default: 50

since
query
string

ISO 8601 timestamp.

format: date-time

until
query
string

ISO 8601 timestamp.

format: date-time

type
query
unlock: access-rule attempts only. call: forwarded calls only. Omit for both.string

one of: unlock, call

unlock_id
query
Only entries for this access rule.string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

q
query
Case-insensitive search of each entry's name, such as an access rule label. Matched literally; 1 to 100 characters after trimming.string

minLength: 1
maxLength: 100

succeeded
query
string

one of: true, false

cursor
query
Opaque base64url cursor; do not construct or decode as part of client logic.string

No request body.

Responses

HTTP statusContract
200Success. LogPage
400Invalid request; correct the input. Codes: INVALID_BUILDING, INVALID_ID, INVALID_LIMIT, INVALID_DATE, INVALID_TYPE, INVALID_QUERY, INVALID_SUCCEEDED, INVALID_CURSOR. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: BUILDING_NOT_FOUND. Error
409Conflicts with current state; inspect it before retrying. Codes: BUILDING_REQUIRED. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: FETCH_FAILED, INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/webhooks

Create a webhook endpoint

Account-level: do not send X-Buzzer-Building-Id; events carry building_id. At most 5 endpoints per account (409 WEBHOOK_LIMIT). secret is returned only by this call; store it to verify BuzzerAPI-Signature. events defaults to every event type. Endpoints can also be managed on the account page. Repeating the call with the same Idempotency-Key and body returns 200 with the original endpoint, including secret, and Idempotency-Replayed: true; a different body with that key returns 409 IDEMPOTENCY_CONFLICT.

Authorization: Bearer key; access:write.

Parameters

ParameterContract
Idempotency-Key
header
Optional. When supplied it works like access creation: the same key and body replay the original endpoint with Idempotency-Replayed: true, and a different body returns 409 IDEMPOTENCY_CONFLICT.string

pattern: ^[a-zA-Z0-9_-]{16,100}$

JSON request body

WebhookCreate

Responses

HTTP statusContract
200Success. WebhookEndpoint

Idempotency-Replayed: Present on replay. string

constant: "true"

201Created. WebhookEndpoint

Idempotency-Replayed: Present on replay. string

constant: "true"

400Invalid request; correct the input. Codes: INVALID_JSON, INVALID_WEBHOOK_URL, INVALID_WEBHOOK_EVENTS. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
409Conflicts with current state; inspect it before retrying. Codes: WEBHOOK_LIMIT, IDEMPOTENCY_CONFLICT. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

GET /v1/webhooks

List webhook endpoints

Every endpoint on the account. secret is never returned here.

Authorization: Bearer key; access:read.

No request body.

Responses

HTTP statusContract
200Success. WebhookEndpointList
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

DELETE /v1/webhooks/{id}

Delete a webhook endpoint

Stops deliveries and cancels pending retries for this endpoint. Repeating the call is harmless.

Authorization: Bearer key; access:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
200Success. Deleted
400Invalid request; correct the input. Codes: INVALID_ID. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

POST /v1/webhooks/{id}/test

Send a test event

Queues a webhook.test event for this endpoint with the normal signature, timeout, and retry rules. 202 means queued, not delivered.

Authorization: Bearer key; access:write.

Parameters

ParameterContract
id
path · required
string

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

No request body.

Responses

HTTP statusContract
202Accepted: the test delivery is queued. WebhookTestQueued
400Invalid request; correct the input. Codes: INVALID_ID. Error
401Missing, invalid, expired, or revoked key. Codes: AUTH_REQUIRED, INVALID_KEY. Error
403Not permitted for this key, account, or plan. Codes: INSUFFICIENT_SCOPE. Error
404Not found for this key or building. Codes: NOT_FOUND. Error
429Rate limited; wait for Retry-After. Codes: RATE_LIMIT_EXCEEDED. Error

Retry-After: Number of seconds to wait before retrying. Absent on PHONE_CODE_ATTEMPTS; request a new code instead. integer

minimum: 1

RateLimit-Limit: Requests allowed in the current window. integer

RateLimit-Remaining: Requests left in the current window. integer

RateLimit-Reset: Seconds until the window resets. integer

5XXServer error; retryable is true, but check a mutation's state before repeating it. Codes: INTERNAL_ERROR. Error
defaultStructured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error

Request and response schemas

Optional response fields can be omitted; null is documented separately. Type-specific grant fields appear only for that type; older app-created records may omit optional fields. Required means required in that schema, not that every supported request variant uses that schema.

Error

object
FieldContract
error requiredobject
error.type requiredstring

one of: permission_error, authentication_error, invalid_request_error, not_found_error, rate_limit_error, internal_error

error.message requiredstring
error.code requiredstring

Stable UPPER_SNAKE_CASE code. Branch on this, not on message. New codes can be added; handle an unknown code by its HTTP status and retryable.

one of: INVALID_SETTINGS, INVALID_GREETING, SETTINGS_VERSION_REQUIRED, SETTINGS_VERSION_CONFLICT, ACCOUNT_NOT_FOUND, ALREADY_VERIFIED, AUTH_RATE_LIMIT, AUTH_REQUIRED, AUTHENTICATION_REQUIRED, BUILDING_ADD_FAILED, BUILDING_BILLING_MISMATCH, BUILDING_BUSY, BUILDING_LIMIT, BUILDING_NOT_FOUND, BUILDING_REQUIRED, CHECKOUT_EXISTS, CHECKOUT_PENDING, CHECKOUT_RECONCILIATION_REQUIRED, CHECKOUT_UNAVAILABLE, CUSTOMER_NOT_FOUND, FETCH_FAILED, FORWARDING_PHONE_NOT_VERIFIED, GRANT_REVOKED, HTTPS_REQUIRED, IDEMPOTENCY_CONFLICT, IDEMPOTENCY_KEY_REQUIRED, INSUFFICIENT_SCOPE, INTERNAL_ERROR, INVALID_FILTER, INVALID_BUILDING, INVALID_BUILDING_CONTEXT, INVALID_BUILDING_INPUT, INVALID_CURSOR, INVALID_DATE, INVALID_EMAIL, INVALID_ID, INVALID_JSON, INVALID_KEY, INVALID_KEY_OPTIONS, INVALID_LIMIT, INVALID_OTP, INVALID_PHONE_CODE, INVALID_PHONE_NUMBER, INVALID_PLAN, INVALID_QUOTE, INVALID_SUCCEEDED, INVALID_TONE, INVALID_TYPE, INVALID_UNLOCK, INVALID_WEBHOOK_EVENTS, INVALID_WEBHOOK_URL, KEY_LIMIT, LINKED_ACCOUNT, MULTI_BUILDING_REQUIRED, NOT_FOUND, NUMBER_NOT_FOUND, NUMBER_OWNER_REQUIRED, NUMBER_NOT_PROVISIONED, OTP_RATE_LIMIT, PARENT_BILLING_REQUIRED, PARENT_BUILDING_KEY_REQUIRED, PAYLOAD_TOO_LARGE, PAYMENT_FAILED, PHONE_CHANGE_LIMIT, PHONE_CODE_ATTEMPTS, PHONE_CODE_EXPIRED, PHONE_CODE_RATE_LIMIT, PHONE_CODE_SEND_FAILED, PHONE_IN_USE, PHONE_VERIFICATION_REQUIRED, PLAN_UNAVAILABLE, PLAN_UPGRADE_REQUIRED, QUOTE_EXPIRED, QUOTE_STALE, RATE_LIMIT_EXCEEDED, SETUP_REQUIRED, SUBSCRIPTION_EXISTS, SUBSCRIPTION_REQUIRED, VERSION_CONFLICT, VERSION_REQUIRED, WEBHOOK_LIMIT, WEB_SESSION_REQUIRED, WEBSITE_SIGN_IN_REQUIRED

error.retryable requiredboolean

True for 429 and 5xx; does not authorize blindly repeating mutations.

error.next_action requiredstring
requestId requiredstring

Also returned as X-Request-ID.

Health

object
FieldContract
status requiredstring

one of: ok

version requiredstring

Backend package version.

uptime requirednumber

Process uptime in seconds.

openapi_url optionalstring

Where to fetch this OpenAPI contract, such as https://www.buzzerapi.com/openapi.json.

format: uri

docs_url optionalstring

AI agent onboarding guide, such as https://www.buzzerapi.com/agents.md.

format: uri

Plans

object
FieldContract
data requiredarray of object
data[].id requiredstring

one of: api_weekly, api_yearly

data[].available requiredboolean
data[].amount requiredinteger

Minor currency units; do not hardcode prices.

| null
data[].currency requiredstring
data[].interval optionalstring
data[].interval_count optionalinteger
data[].access requiredarray of string

one of: timer, passcode, routine

OtpRequest

object
FieldContract
email requiredstring

Trimmed and lowercased.

format: email
maxLength: 254

OtpChallenge

object
FieldContract
challenge requiredstring

Single-use challenge. It expires; request a new one if verification fails.

message requiredstring

VerifyRequest

object
FieldContract
email requiredstring

format: email

challenge requiredstring
otp requiredstring

Six-digit email sign-in code, not a visitor code.

pattern: ^[0-9]{6}$

purpose requiredstring

Required. Email sign-in is only for the website; any other value returns 400 WEBSITE_SIGN_IN_REQUIRED.

one of: web

WebSessionCredential

object
FieldContract
apiKey requiredstring

12-hour website session key, returned once. Holds every scope, including keys:write. The website keeps it server-side.

keyPrefix requiredstring
name requiredstring

KeyRequest

object

Unknown fields rejected.

FieldContract
name optionalstring

Nonblank; trimmed before storage.

maxLength: 120
default: "My AI agent"

scopes optionalarray of string

one of: account:read, access:read, access:write, logs:read, setup:write, billing:write, buildings:write

AI agent keys cannot hold keys:write; requesting it returns 400 INVALID_KEY_OPTIONS.

minItems: 1
default: ["account:read","access:read","access:write","logs:read"]

expires_in_days optionalinteger

Optional lifetime. Defaults to no expiry.

minimum: 1
maximum: 90

| null

AgentCredential

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

api_key requiredstring

Returned once; store in secret settings.

scopes requiredarray of string

one of: account:read, access:read, access:write, logs:read, setup:write, billing:write, keys:write, buildings:write

expires_at requiredstring

ISO 8601 timestamp.

format: date-time

| null
message requiredstring

KeyList

object
FieldContract
data requiredarray of object

Every active key on the account, newest first.

data[].id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

data[].name requiredstring
data[].type requiredstring

web_session: a website sign-in. agent: an AI agent key created on the account page.

one of: web_session, agent

data[].scopes requiredarray of string

one of: account:read, access:read, access:write, logs:read, setup:write, billing:write, keys:write, buildings:write

data[].key_prefix requiredstring

First 12 characters of the key, for recognition only.

data[].created_at requiredstring

ISO 8601 timestamp.

format: date-time

data[].expires_at requiredstring

ISO 8601 timestamp.

format: date-time

| null
data[].last_used_at requiredstring

ISO 8601 timestamp.

format: date-time

| null
data[].current requiredboolean

True only for the website session making this request.

Revoked

object
FieldContract
revoked requiredboolean

constant: true

KeyRevoked

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

revoked requiredboolean

constant: true

KeysRevoked

object
FieldContract
revoked requiredinteger

Number of keys revoked.

minimum: 0

Account

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

email requiredstring
virtual_number requiredstring | null
subscription requiredobject
subscription.type requiredstring

BUZZER_API for a BuzzerAPI subscription, NONE without one. Treat any other value as not a BuzzerAPI subscription.

subscription.status requiredstring

one of: none, active, past_due

usage requiredobject
usage.total_unlocks requiredinteger

Recorded activity count, including failed attempts; not a physical-entry count.

usage.active_routines requiredinteger

Stored activated rules; not necessarily currently effective.

usage.shared_users requiredinteger

Number of users associated with the number.

created_at requiredstring

ISO 8601 timestamp.

format: date-time

buildings requiredobject
buildings.count requiredinteger
buildings.selection_required requiredboolean
buildings.list_command requiredstring

REST request that lists buildings, such as GET /v1/buildings.

capabilities requiredobject
capabilities.multi_building requiredboolean
capabilities.access_types requiredarray of string

one of: timer, passcode, routine

Access rule types this account can create.

capabilities.scopes requiredarray of string

one of: account:read, access:read, access:write, logs:read, setup:write, billing:write, keys:write, buildings:write

readiness requiredobject
readiness.can_create_access requiredboolean

True for any key with access:write. Rules can be created before subscribing or connecting a building; they start opening the door once a virtual number is connected. Creation still validates input and the selected building.

readiness.building_connection requiredstring

previous_buzz_recorded requires a successful access-rule unlock; calls forwarded to the resident do not count.

one of: not_provisioned, previous_buzz_recorded, unverified

readiness.physical_entry_verified requiredboolean

constant: false

next_actions requiredarray of object
next_actions[].command requiredstring

REST request to make next, such as GET /v1/buildings/{buildingId}/setup.

next_actions[].reason requiredstring

PhoneStatus

object
FieldContract
phone_number requiredstring

The owner's verified forwarding phone, or null.

| null
verified requiredboolean

PhoneVerificationRequest

object

Unknown fields rejected.

FieldContract
phone_number requiredstring

US or Canadian resident phone in +1 E.164 format.

pattern: ^\+1[2-9][0-9]{9}$

PhoneCodeSent

object
FieldContract
sent requiredboolean

constant: true

phone_number requiredstring

PhoneConfirmationRequest

object

Unknown fields rejected.

FieldContract
code requiredstring

Code received by SMS. Never log or store this code.

pattern: ^[0-9]{4,10}$

CheckoutRequest

object

Unknown fields rejected.

FieldContract
plan requiredstring

one of: api_weekly, api_yearly

Checkout

object
FieldContract
status requiredstring

constant: "none"

payment_required requiredboolean

constant: false

next_action requiredstring
| object
FieldContract
id requiredstring

Stripe checkout session ID.

| null
request_id requiredstring
plan requiredstring

one of: api_weekly, api_yearly

url requiredstring

format: uri

| null
status requiredstring

one of: preparing, open, complete, expired, failed

payment_required requiredboolean
next_action requiredstring
message requiredstring

CheckoutExpired

object
FieldContract
id requiredstring
status requiredstring

constant: "expired"

payment_required requiredboolean

constant: false

Portal

object
FieldContract
url requiredstring

Hosted Stripe billing-management URL.

format: uri

BuildingGreeting

object

Unknown fields rejected.

FieldContract
custom_greeting requiredstring

Custom greeting. Empty uses the standard instructions. Stored exactly without trimming.

maxLength: 119

prompt_for_passcode requiredboolean

Preserves the existing call-flow option; it does not create or enable access rules.

access_granted_phrase requiredstring

Phrase used on release. Empty makes this phrase silent.

maxLength: 119

access_denied_phrase requiredstring

Phrase used on denied access. Empty makes this phrase silent.

maxLength: 119

BuildingGreetingPatch

object

minProperties: 1

Unknown fields rejected.

FieldContract
custom_greeting optionalstring

Custom greeting. Empty uses the standard instructions. Stored exactly without trimming.

maxLength: 119

prompt_for_passcode optionalboolean

Preserves the existing call-flow option; it does not create or enable access rules.

access_granted_phrase optionalstring

Phrase used on release. Empty makes this phrase silent.

maxLength: 119

access_denied_phrase optionalstring

Phrase used on denied access. Empty makes this phrase silent.

maxLength: 119

BuildingSettings

object
FieldContract
building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

virtual_number requiredstring | null
unlock_tone requiredstring | null
greeting requiredBuildingGreeting
version requiredstring

Opaque settings snapshot token. Use the quoted ETag in If-Match; do not construct a token.

pattern: ^[a-f0-9]{64}$

| null
editable requiredboolean

True when the caller owns the number and can PATCH these settings. False for invited members and unprovisioned buildings. Not part of version.

BuildingSettingsUpdated

object
FieldContract
building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

virtual_number requiredstring | null
unlock_tone requiredstring | null
greeting requiredBuildingGreeting
version requiredstring

Opaque settings snapshot token. Use the quoted ETag in If-Match; do not construct a token.

pattern: ^[a-f0-9]{64}$

| null
editable requiredboolean

True when the caller owns the number and can PATCH these settings. False for invited members and unprovisioned buildings. Not part of version.

physical_test_required requiredboolean

True when unlock_tone was supplied. Test from the entrance yourself.

BuildingSettingsPatch

object

minProperties: 1

Unknown fields rejected.

FieldContract
unlock_tone optionalstring

Exact building release keypress; test at the entrance.

pattern: ^[0-9#*]{1,3}(?![\s\S])

greeting optionalBuildingGreetingPatch

BuildingSetup

object
FieldContract
building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

virtual_number requiredstring | null
unlock_tone requiredstring | null
last_successful_buzz_at requiredstring

Latest successful access-rule unlock; calls forwarded to the resident are excluded.

format: date-time

| null
status requiredstring

one of: awaiting_subscription_or_provisioning, previous_buzz_recorded, building_setup_required

instructions requiredstring

Address

object
FieldContract
street optionalstring
city optionalstring
state optionalstring
zip optionalstring
country optionalstring

Buildings

object
FieldContract
data requiredarray of object
data[].id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

data[].label requiredstring
data[].address requiredAddress | null
data[].virtual_number requiredstring | null
data[].is_primary requiredboolean
data[].provisioned requiredboolean
selection_required requiredboolean
next_action requiredstring

BuildingAddPreview

object

Unknown fields rejected.

FieldContract
action requiredstring

constant: "add"

label requiredstring

Nonblank; trimmed.

maxLength: 120

address requiredobject

Unknown fields rejected.

address.street optionalstring

maxLength: 200

address.city requiredstring

Nonblank; trimmed.

maxLength: 120

address.state requiredstring

Two-letter US state or Canadian province (AB, BC, MB, NB, NL, NS, NT, NU, ON, PE, QC, SK, YT). Normalized uppercase.

pattern: ^[A-Za-z]{2}$

address.zip requiredstring

US ZIP (12345 or 12345-6789) or Canadian postal code (A1A 1A1; normalized to that form).

pattern: ^([0-9]{5}(-[0-9]{4})?|[A-Za-z][0-9][A-Za-z][ -]?[0-9][A-Za-z][0-9])$

address.country optionalstring

Defaults to CA for a Canadian postal code, otherwise US.

one of: US, CA

unlock_tone requiredstring

Exact building release keypress; test at the entrance.

pattern: ^[0-9#*]{1,3}$

phone_number optionalstring

Resident forwarding number. Defaults to parent phone if omitted; a valid number is still required.

pattern: ^\+[1-9][0-9]{6,14}$

BuildingRemovePreview

object

Unknown fields rejected.

FieldContract
action requiredstring

constant: "remove"

building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

QuoteRequest

object

Unknown fields rejected.

FieldContract
quote_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

BuildingOperation

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

action requiredstring

one of: add, remove

status requiredstring

one of: quoted, expired, running, succeeded, failed, reconciliation_required

input requiredobject

Add: normalized label/address/unlock_tone/phone_number. Remove: building_id/label/virtual_number/effects. Inspect the exact snapshot before execution.

input.label optionalstring
input.address optionalAddress
input.unlock_tone optionalstring
input.phone_number optionalstring
input.building_id optionalstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

input.virtual_number optionalstring | null
input.effects optionalarray of string
billing requiredobject
billing.subscription_id requiredstring
billing.price_id requiredstring
billing.current_quantity requiredinteger
billing.new_quantity requiredinteger
billing.currency requiredstring
billing.next_invoice_amount_due requiredinteger

Entire next renewal invoice estimate in minor currency units. Read amount_due_now for the immediate building charge.

billing.per_building_amount requiredinteger

Can be null for tiered pricing. Do not multiply this to reconstruct the invoice.

| null
billing.interval optionalstring
billing.proration_date requiredinteger

Quote timestamp in Unix seconds. Building quantity changes do not prorate.

billing.amount_due_now requiredinteger

Quoted immediate charge in minor currency units. Adding a building bills its full current weekly or annual term; adding during a trial ends it and bills all buildings. Zero for removal.

billing.charge_timing requiredstring

one of: immediate, none

billing.ends_trial requiredboolean

Whether adding this building ends the current trial.

billing.note requiredstring
expires_at requiredstring

ISO 8601 timestamp.

format: date-time

building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

| null
request_id requiredstring | null
result requiredobject

Add result: building_id, label, virtual_number, physical_test_required, charge (invoice_id, amount, currency). Remove result: building_id, deleted, released_number. Null until succeeded.

| null
result.building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

result.label optionalstring
result.virtual_number optionalstring
result.physical_test_required optionalboolean

constant: true

result.deleted optionalboolean

constant: true

result.released_number optionalstring | null
result.charge optionalobject
result.charge.invoice_id requiredstring
result.charge.amount requiredinteger

Amount collected in minor currency units.

result.charge.currency requiredstring
next_action requiredstring

TimerCreate

object

Unknown fields rejected.

FieldContract
type requiredstring

constant: "timer"

label requiredstring

Required. 1 to 120 characters, such as who the access is for.

minLength: 1
maxLength: 120

enabled requiredboolean

Required. Whether the rule is on when it's created. true: it works right away (a timer with starts_at starts at that time). false: it's saved but stays off until you turn it on with PATCH.

expires_in_minutes optionalinteger

Window length in minutes, 1 to 1,440 (1 day). Send this or ends_at, not both. Starts immediately unless starts_at is supplied.

minimum: 1
maximum: 1440

ends_at optionalstring

ISO timestamp with Z or numeric timezone offset for when the window closes: in the future, after starts_at, and 1 minute to 1 day after the start (starts_at, or now). Send this or expires_in_minutes, not both. Only with enabled: true.

format: date-time

starts_at optionalstring

Optional ISO timestamp with Z or numeric timezone offset, now or later and within 1 year; a time in the past is rejected (up to 1 minute late counts as now). Only with enabled: true. Until this time the timer is enabled with status scheduled.

format: date-time

remaining_uses optionalinteger

Optional. Number of buzzes this timer lets in (1 to 100). Omit for unlimited buzzes during the window. Each release uses one; at 0 the timer becomes exhausted.

minimum: 1
maximum: 100

PasscodeCreate

object

Unknown fields rejected.

FieldContract
type requiredstring

constant: "passcode"

label requiredstring

Required. 1 to 120 characters, such as who the access is for.

minLength: 1
maxLength: 120

enabled requiredboolean

Required. Whether the rule is on when it's created. true: it works right away (a timer with starts_at starts at that time). false: it's saved but stays off until you turn it on with PATCH.

access_code optionalstring

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}$

remaining_uses optionalinteger

minimum: 1
maximum: 100

| null

Optional. Number of times this passcode can open the door (1 to 100). Omit it, or send null, for unlimited uses. Each release uses one; at 0 the passcode becomes exhausted.

enter_code_with_voice optionalboolean

When true, callers can speak the code aloud instead of typing it.

default: false

expires_in_minutes optionalinteger

Optional minutes from now until the passcode expires, 1 to 525,600 (1 year). Send this or ends_at, not both. With neither, the passcode never expires.

minimum: 1
maximum: 525600

ends_at optionalstring

format: date-time

| null

Optional expiry: a future ISO timestamp with Z or numeric timezone offset, no more than 1 year ahead. Send this or expires_in_minutes, not both. Omit both, or send null, for a passcode that never expires.

RoutineCreate

object

Unknown fields rejected.

FieldContract
type requiredstring

constant: "routine"

label requiredstring

Required. 1 to 120 characters, such as who the access is for.

minLength: 1
maxLength: 120

enabled requiredboolean

Required. Whether the rule is on when it's created. true: it works right away (a timer with starts_at starts at that time). false: it's saved but stays off until you turn it on with PATCH.

days requiredarray of string

Day name, case-insensitive: sunday, monday, tuesday, wednesday, thursday, friday, saturday.

pattern: ^([sS][uU][nN][dD][aA][yY]|[mM][oO][nN][dD][aA][yY]|[tT][uU][eE][sS][dD][aA][yY]|[wW][eE][dD][nN][eE][sS][dD][aA][yY]|[tT][hH][uU][rR][sS][dD][aA][yY]|[fF][rR][iI][dD][aA][yY]|[sS][aA][tT][uU][rR][dD][aA][yY])(?![\s\S])
maxLength: 9

Any combination of days, each listed once, case-insensitive. For example ["monday","wednesday","friday"] for Monday, Wednesday and Friday.

minItems: 1
maxItems: 7
uniqueItems: true

start_hour_and_minutes requiredstring

24-hour HH:MM. end_hour_and_minutes must be later than start_hour_and_minutes on the same day; no overnight window.

pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$

end_hour_and_minutes requiredstring

24-hour HH:MM. end_hour_and_minutes must be later than start_hour_and_minutes on the same day; no overnight window.

pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$

timezone requiredstring

Valid IANA timezone, such as America/Los_Angeles.

access_code optionalstring

Optional visitor code for the schedule: during its window a caller must enter this code instead of being let in on every buzz. Same 1–4 digit app-compatible validation as a passcode; not generated when omitted. No use limit or expiry.

pattern: ^(?!1$)[0-9]{1,4}$

enter_code_with_voice optionalboolean

When true, callers can speak the code aloud instead of typing it. Requires access_code.

default: false

UnlockCreate

TimerCreate | PasscodeCreate | RoutineCreate

type is required and never inferred, so a missing access_code can't turn a passcode into a timer that lets anyone in. Unknown fields are rejected with 400 INVALID_UNLOCK listing the allowed fields.

TimerUpdate

object

minProperties: 1

Unknown fields rejected.

FieldContract
label optionalstring

Optional replacement label, 1 to 120 characters. It cannot be blanked.

minLength: 1
maxLength: 120

enabled optionalboolean

false turns the timer off and clears its window. true turns it on and requires expires_in_minutes or ends_at in the same request (plus starts_at to start later); if its uses ran out, also send remaining_uses (a count, or null for unlimited).

expires_in_minutes optionalinteger

Window length in minutes, 1 to 1,440 (1 day). Only with enabled: true; send this or ends_at, not both. The new window begins immediately, or at starts_at.

minimum: 1
maximum: 1440

ends_at optionalstring

ISO timestamp with Z or numeric timezone offset for when the new window closes, 1 minute to 1 day after it starts. Only with enabled: true; send this or expires_in_minutes, not both.

format: date-time

starts_at optionalstring

Optional ISO timestamp with Z or numeric timezone offset, now or later and within 1 year; a time in the past is rejected. Only with enabled: true and expires_in_minutes or ends_at: the new window starts then instead of now.

format: date-time

remaining_uses optionalinteger

minimum: 1
maximum: 100

| null

Optional. A number (1 to 100) sets how many buzzes are left; null makes the timer unlimited again. An exhausted timer stops opening the door; send enabled: true and expires_in_minutes or ends_at as well to reuse it.

PasscodeUpdate

object

minProperties: 1

Unknown fields rejected.

FieldContract
label optionalstring

Optional replacement label, 1 to 120 characters. It cannot be blanked.

minLength: 1
maxLength: 120

enabled optionalboolean

True turns the passcode back on. If its uses ran out, also send remaining_uses (a count, or null for unlimited) or the request is rejected.

access_code optionalstring

Optional replacement code; omission leaves the current code unchanged. Same 1–4 digit app-compatible validation as creation.

pattern: ^(?!1$)[0-9]{1,4}$

remaining_uses optionalinteger

minimum: 1
maximum: 100

| null

Optional. Sets how many uses are left (1 to 100); null removes the use limit. A passcode whose uses ran out is exhausted; send enabled: true as well to reuse it.

enter_code_with_voice optionalboolean

true lets callers speak the code aloud instead of typing it; false turns it off. Voice applies to the whole call: while any active code on the building allows voice, callers can speak any active code aloud.

expires_in_minutes optionalinteger

Optional new expiry in minutes from now, 1 to 525,600 (1 year). Send this or ends_at, not both.

minimum: 1
maximum: 525600

ends_at optionalstring

format: date-time

| null

Optional replacement expiry: a future ISO timestamp with Z or numeric timezone offset, no more than 1 year ahead, or null to remove the expiry. Send this or expires_in_minutes, not both.

RoutineUpdate

object

minProperties: 1

Unknown fields rejected.

FieldContract
label optionalstring

Optional replacement label, 1 to 120 characters. It cannot be blanked.

minLength: 1
maxLength: 120

enabled optionalboolean
days optionalarray of string

Day name, case-insensitive: sunday, monday, tuesday, wednesday, thursday, friday, saturday.

pattern: ^([sS][uU][nN][dD][aA][yY]|[mM][oO][nN][dD][aA][yY]|[tT][uU][eE][sS][dD][aA][yY]|[wW][eE][dD][nN][eE][sS][dD][aA][yY]|[tT][hH][uU][rR][sS][dD][aA][yY]|[fF][rR][iI][dD][aA][yY]|[sS][aA][tT][uU][rR][dD][aA][yY])(?![\s\S])
maxLength: 9

Any combination of days, each listed once, case-insensitive. For example ["monday","wednesday","friday"] for Monday, Wednesday and Friday.

minItems: 1
maxItems: 7
uniqueItems: true

start_hour_and_minutes optionalstring

24-hour HH:MM. end_hour_and_minutes must be later than start_hour_and_minutes on the same day; no overnight window.

pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$

end_hour_and_minutes optionalstring

24-hour HH:MM. end_hour_and_minutes must be later than start_hour_and_minutes on the same day; no overnight window.

pattern: ^([01][0-9]|2[0-3]):[0-5][0-9]$

timezone optionalstring

IANA timezone.

access_code optionalstring

pattern: ^(?!1$)[0-9]{1,4}$

| null

Adds or replaces the schedule code; null removes it so every buzz in the window is let in again. Removing the code also turns voice off, so a code added later starts with voice off until enter_code_with_voice is set to true again.

enter_code_with_voice optionalboolean

true lets callers speak the code aloud instead of typing it; false turns it off. Applies only while the schedule has an access_code. Removing the code with access_code: null also turns voice off, so a code added later starts with voice off until this is set to true again. Voice applies to the whole call: while any active code on the building allows voice, callers can speak any active code aloud.

UnlockUpdate

TimerUpdate | PasscodeUpdate | RoutineUpdate

Nonempty body; fields must match existing grant type. Type cannot change. Include start_hour_and_minutes and end_hour_and_minutes together if changing either.

Unlock

object
FieldContract
building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

type requiredstring

one of: timer, passcode, routine

label requiredstring
enabled requiredboolean

The on/off switch the caller set. status says what the rule does right now. A turned-off timer has no window.

version requiredinteger
status requiredstring

active: enabled and working now (for routines, not necessarily inside the schedule). scheduled: an enabled timer whose starts_at is in the future. inactive: turned off. exhausted: a passcode, or a timer with a use limit, has remaining_uses 0.

one of: active, scheduled, inactive, expired, exhausted, revoked

request_id optionalstring

Original create idempotency key, absent on older app-created rules.

created_at requiredstring

ISO 8601 timestamp.

format: date-time

updated_at optionalstring

ISO 8601 timestamp.

format: date-time

expires_in_minutes optionalinteger

Timers: the window length in minutes. A window set with ends_at reports its length rounded up to whole minutes.

starts_at optionalstring

ISO 8601 timestamp.

format: date-time

ends_at optionalstring

ISO 8601 timestamp.

format: date-time

| null

Timer window end, or passcode expiry. null for a passcode that never expires.

access_code optionalstring

Sensitive visitor code, present for passcodes and for routines that require a code.

remaining_uses optionalinteger | null

Uses left. Each buzz that opens the door takes one; at 0 status is exhausted. null (or absent on older records) for an unlimited timer or passcode. A replayed create returns the current value.

enter_code_with_voice optionalboolean

Present for passcodes and for routines that require a code.

days optionalarray of string

The schedule days in lowercase, Sunday first (the weekend pair is returned as ["saturday","sunday"]).

start_hour_and_minutes optionalstring

Routines: 24-hour HH:MM the window opens each scheduled day.

end_hour_and_minutes optionalstring

Routines: 24-hour HH:MM the window closes.

timezone optionalstring

Pagination

object
FieldContract
cursor requiredstring

Pass unchanged into the next request with the same filters and building.

| null
has_more requiredboolean

UnlockPage

object
FieldContract
data requiredarray of Unlock
pagination requiredPagination

UnlockRevoked

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

deleted requiredboolean

constant: true

status requiredstring

constant: "revoked"

Log

object
FieldContract
building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

unlock_id optionalstring

Access rule that matched. Absent on calls, failed passcode attempts, and older records.

pattern: ^[a-fA-F0-9]{24}$

type requiredstring

unlock: an access-rule attempt. call: a call forwarded to the resident (or the owner calling their own number).

one of: unlock, call

unlock_type optionalstring

timer, passcode, routine, or unknown(N) for unrecognized values. Absent on calls. On a failed passcode attempt, the name and unlock_type come from the first active rule.

name requiredstring
succeeded requiredboolean
created_at requiredstring

ISO 8601 timestamp.

format: date-time

LogPage

object
FieldContract
building_id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

data requiredarray of Log
pagination requiredPagination

Deleted

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

deleted requiredboolean

constant: true

WebhookCreate

object

Unknown fields rejected.

FieldContract
url requiredstring

A publicly reachable https:// URL, at most 2048 characters; otherwise 400 INVALID_WEBHOOK_URL.

format: uri
pattern: ^https://
maxLength: 2048

events optionalarray of string

one of: access.granted, access.call_forwarded, access.denied, unlock.no_uses_remaining, webhook.test

Event types to send. Omit to receive every event type.

minItems: 1

WebhookEndpoint

object
FieldContract
id requiredstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

url requiredstring

format: uri

events requiredarray of string

one of: access.granted, access.call_forwarded, access.denied, unlock.no_uses_remaining, webhook.test

created_at requiredstring

ISO 8601 timestamp.

format: date-time

secret optionalstring

Signing secret (whsec_ followed by base64url). Returned only by POST /v1/webhooks (the original 201 and any idempotent 200 replay); store it securely. List responses never include it.

pattern: ^whsec_[A-Za-z0-9_-]+$

WebhookEndpointList

object
FieldContract
data requiredarray of WebhookEndpoint

Every endpoint on the account (at most 5). secret is never included.

WebhookTestQueued

object
FieldContract
delivery_id requiredstring

ID of the queued webhook.test delivery.

WebhookEvent

object
FieldContract
id requiredstring

Unique event ID; deduplicate on it because a retried delivery repeats the same ID.

pattern: ^evt_

type requiredstring

one of: access.granted, access.call_forwarded, access.denied, unlock.no_uses_remaining, webhook.test

created_at requiredstring

ISO 8601 timestamp.

format: date-time

api_version requiredstring

constant: "v1"

data requiredobject

access.granted: building_id, building_label, unlock_id, unlock_type, label, remaining_uses, occurred_at. access.call_forwarded and access.denied: building_id, building_label, occurred_at. unlock.no_uses_remaining: building_id, building_label, unlock_id, unlock_type, label, occurred_at. webhook.test: empty object.

data.building_id optionalstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

data.building_label optionalstring

The building label, as in GET /v1/buildings.

data.unlock_id optionalstring

Opaque resource ID, normally 24 hexadecimal characters.

pattern: ^[a-fA-F0-9]{24}$

data.unlock_type optionalstring

one of: timer, passcode, routine

data.label optionalstring
data.remaining_uses optionalinteger

Uses left after this use; null for rules without a use limit.

| null
data.occurred_at optionalstring

ISO 8601 time the event happened. Delivery order is not guaranteed, so order events by this field.

format: date-time