REST examples
Set BUZZER_BASE_URL to the backend service origin, without /v1. These examples 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 "$BUZZER_BASE_URL/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","max_uses":1,"expires_in_minutes":60}'
# Read the current grant and ETag before an update.
curl -i "$BUZZER_BASE_URL/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 "$BUZZER_BASE_URL/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 '{"active":false}'
# REST requires events even though the CLI supplies a default.
curl -sS "$BUZZER_BASE_URL/v1/webhooks" \
-H "Authorization: Bearer $BUZZER_API_KEY" \
-H "X-Buzzer-Building-Id: $BUILDING_ID" \
-H 'Content-Type: application/json' \
--data '{"url":"https://agent.example/buzzer","events":["unlock.completed"]}'
# Fetch an activity page; repeat with the returned pagination.cursor.
curl -sS -G "$BUZZER_BASE_URL/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", "active": true,
"version": 0, "status": "active", "request_id": "delivery-example-001",
"created_at": "2026-09-19T12:00:00Z", "expires_at": "2026-09-19T13:00:00Z",
"code": "0427", "max_uses": 1, "remaining_uses": 1, "voice_enabled": 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. The CLI's logs --follow is polling, not a separate streaming REST endpoint.
Callback payloads and signature verification
Subscribe separately for each building. Production destinations must use HTTPS and resolve to a supported public IPv4 address; private networks, URL credentials, IPv6-only endpoints, and redirect-based receivers are unsupported. Registrations are capped at five per building, including inactive ones. Secret rotation requires creating a replacement registration and deliberately removing the old one.
| Event | Actual payload and meaning |
unlock.completed | data: routine_id, unlock_id, activity_id, occurred_at, name, numeric unlock_type (1 timer, 2 passcode, 3 routine). Emitted when Lowkey accepted a rule and prepared the release tone. No passcode in this event. This does not prove the door opened or anyone entered. |
unlock.revoked | data: routine_id, name, reason:"uses_exhausted". Emitted when a use-limited passcode is exhausted. It does not include unlock_id/activity_id; use routine_id. Manual API deletion does not currently emit this event. |
access.denied | data.attempted_passcode may contain attempted digits or recognized speech. Treat this as sensitive. This callback does not supply a correlated activity ID. |
unlock.created, unlock.expired | Accepted subscription values but currently not emitted. Do not depend on them for lifecycle tracking; read grant state. |
webhook.test | data.message only; no created_at in the test envelope. Sent by the test endpoint even for an inactive registration. Cannot be selected in events. Never use it to trigger entry actions. |
{
"event": "unlock.completed",
"eventId": "29b4a1b5-69b0-4e40-b9d7-4850a6fcf521",
"building_id": "507f1f77bcf86cd799439011",
"created_at": "2026-09-19T12:05:00Z",
"data": {
"routine_id": "507f1f77bcf86cd799439012",
"unlock_id": "507f1f77bcf86cd799439012",
"activity_id": "507f1f77bcf86cd799439013",
"occurred_at": "2026-09-19T12:05:00Z",
"name": "Delivery", "unlock_type": 2
}
}
Every callback is JSON POST with X-Buzzer-Event, X-Buzzer-Delivery-Id (same as eventId), and X-Buzzer-Signature: t=UNIX_SECONDS,v1=HEX_DIGEST. Verify HMAC-SHA256 over timestamp + "." + raw body bytes using the returned secret as a string. Do not hex-decode the secret or reserialize parsed JSON.
import { createHmac, timingSafeEqual } from 'node:crypto';
// rawBody must be the untouched Buffer, captured before JSON parsing.
export function verifyBuzzerSignature(rawBody, header, secret, now = Date.now()) {
if (!Buffer.isBuffer(rawBody) || typeof header !== 'string') return false;
const match = /^t=([0-9]+),v1=([a-f0-9]{64})$/.exec(header);
if (!match) return false;
const [, timestamp, digest] = match;
if (Math.abs(Math.floor(now / 1000) - Number(timestamp)) > 300) return false;
const expected = createHmac('sha256', secret)
.update(timestamp + '.').update(rawBody).digest();
return timingSafeEqual(expected, Buffer.from(digest, 'hex'));
}
After verification, parse the event, deduplicate by eventId, durably queue processing, and return 2xx promptly. The five-minute tolerance above is a receiver recommendation. Callbacks time out after 10 seconds; network failures, 429, and 5xx retry up to three total attempts, with approximately 5–7.5 and 30–45 seconds between attempts. Other HTTP errors are not retried, and redirects are not followed. Retry signatures use a fresh timestamp but retain the event ID and body.
Ten consecutive failed attempts auto-disable a registration; success resets the counter. Inspect the list, fix delivery, then PATCH active=true. Test deliveries make one attempt and return success, optional statusCode/errorMessage, and latencyMs; HTTP 200 alone does not mean the callback succeeded. Test attempts do not reset failure counters. Delivery is best effort with in-process retries; there is no durable delivery queue, public delivery-history endpoint, or replay API. Recover missed use events through logs; not all callback events have a log equivalent.
Errors and recovery
{
"error": {
"type": "invalid_request_error",
"message": "Access changed since it was read...",
"code": "VERSION_CONFLICT", "retryable": false,
"next_action": "buzzer unlock get <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. For webhook creation, inspect registrations before retrying. Building reconciliation requires support instead of another mutation.
| HTTP / codes | Recovery |
400: INVALID_JSON, INVALID_EMAIL, INVALID_KEY_OPTIONS, INVALID_PLAN, INVALID_TONE, INVALID_ID, INVALID_BUILDING, INVALID_BUILDING_CONTEXT, INVALID_BUILDING_INPUT, INVALID_PHONE_NUMBER, INVALID_QUOTE, INVALID_UNLOCK, INVALID_TYPE, INVALID_ACTIVE, INVALID_LIMIT, INVALID_CURSOR, INVALID_DATE, IDEMPOTENCY_KEY_REQUIRED, MISSING_URL, MISSING_EVENTS, INVALID_EVENTS, INVALID_URL, MAX_WEBHOOKS_EXCEEDED | Correct the named input. Follow returned messages and documented bounds. Use a returned cursor and supported HTTPS destination. Delete an unwanted webhook registration before exceeding the five-registration limit. |
401: AUTH_REQUIRED, INVALID_KEY, INVALID_OTP | Authenticate again or request a valid delegated key. OTP challenges are single-use, expiring, and attempt-limited. |
403: INSUFFICIENT_SCOPE, OWNER_KEY_REQUIRED, PARENT_BILLING_REQUIRED, PARENT_BUILDING_KEY_REQUIRED, SUBSCRIPTION_REQUIRED, PLAN_UPGRADE_REQUIRED, MULTI_BUILDING_REQUIRED | Use the correct owner/parent credential and scopes, or manage the subscription. Do not rotate credentials to bypass a plan requirement. |
404: NOT_FOUND, ACCOUNT_NOT_FOUND, NUMBER_NOT_FOUND, BUILDING_NOT_FOUND, CUSTOMER_NOT_FOUND | Verify 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, GRANT_REVOKED, VERSION_CONFLICT, SETUP_REQUIRED | Inspect existing state. Expired/stale unexecuted quotes need a new preview. Pending checkout needs recovery, not another purchase. Reconciliation/billing mismatch needs support. Revoke an old credential for key limits. A revoked grant requires a deliberately new grant. |
428: VERSION_REQUIRED | GET the grant and send its quoted ETag as If-Match. |
429: AUTH_RATE_LIMIT, OTP_RATE_LIMIT, RATE_LIMIT_EXCEEDED | Respect Retry-After where returned and reduce polling/request frequency. |
500: INTERNAL_ERROR, FETCH_FAILED, CREATE_FAILED, LIST_FAILED, UPDATE_FAILED, DELETE_FAILED, TEST_FAILED, SECRET_DECRYPT_FAILED; 503: CHECKOUT_UNAVAILABLE | Retry reads with backoff. Reconcile mutations using their receipt/resource first. Persist requestId for support. Provider/validation failures not explicitly mapped by a route can surface as a generic 500. |
Some legacy error paths and 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; success does not establish account readiness.
Authorization: Public; no key required.
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Health |
| default | Structured 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 Standard/Premium monthly/yearly are listed. Multi Building is managed in the Lowkey app. Public cache max-age=60, shared cache max-age=300.
Authorization: Public; no key required.
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Plans |
| default | Structured 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
Expires in 15 minutes, five verification attempts, single use. At most three unexpired challenges per email. Delivery is not proof that the account already exists.
Authorization: Public; no key required.
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. OtpChallenge |
| default | Structured 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 obtain owner credential
Creates the account if needed. At most five active non-web owner credentials. Never disclose apiKey. purpose=web is for server-side website sessions; it is not a browser-storage recommendation.
Authorization: Public; no key required.
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. OwnerCredential |
| default | Structured 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. Subsequent use fails authentication. CLI child credentials are invalidated when their parent expires or is revoked. Website account-agent keys survive website logout.
Authorization: Bearer key; no additional scope.
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Revoked |
| default | Structured 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 agent credential
One delegation level; a parentKeyId credential cannot delegate again. Requested scopes must be held by the issuer. Website sessions require an active subscription and may issue only default access scopes plus optional buildings:write, with a limit of 20 active account-agent keys. Website-issued keys are independent of session expiry; CLI-delegated keys depend on their parent.
Authorization: Bearer key; keys:write.
JSON request body
Responses
| HTTP status | Contract |
|---|
| 201 | Created. AgentCredential |
| default | Structured 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 manageable agent credentials
Website sessions list account-agent keys. Other credentials list their own children plus account-agent keys. Excludes revoked keys, but can include expired ones. No secrets or pagination.
Authorization: Bearer key; keys:write.
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. KeyList |
| default | Structured 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 a manageable agent key
Same ownership rules as list. Repeated revocation of a known managed key succeeds.
Authorization: Bearer key; keys:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. KeyRevoked |
| default | Structured 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 status | Contract |
|---|
| 200 | Success. Account |
| default | Structured 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 status | Contract |
|---|
| 200 | Success. Plans |
| default | Structured 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.
Authorization: Bearer key; billing:write.
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Checkout |
| default | Structured 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. Existing subscriptions use the portal. One pending checkout: same plan reuses it, another plan conflicts until the unpaid session is expired. Same request ID with another plan conflicts. After an unresolved provider outcome older than 23 hours, reconciliation is required instead of another purchase. No card details accepted by this API.
Authorization: Bearer key; billing:write.
Parameters
| Parameter | Contract |
|---|
Idempotency-Key header · required | Stable identifier for this action. Reuse the identical body and key after a timeout.stringpattern: ^[a-zA-Z0-9_-]{16,100}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Checkout |
| 201 | Created. Checkout |
| default | Structured 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
| Parameter | Contract |
|---|
id path · required | stringStripe checkout session ID. |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. CheckoutExpired |
| default | Structured 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 status | Contract |
|---|
| 200 | Success. Portal |
| default | Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error |
GET /v1/setup
Inspect selected building setup
No subscription required for inspection. A previous successful buzz is not proof of physical door release.
Authorization: Bearer key; account:read.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Setup |
| default | Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error |
PATCH /v1/setup
Configure selected building release tone
Must own a provisioned number. Linked-building writes require active Multi Building. The API does not program the building call box; its manager must change the destination.
Authorization: Bearer key; setup:write.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. SetupUpdated |
| default | Structured 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 status | Contract |
|---|
| 200 | Success. Buildings |
| default | Structured 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. Account mutations are serialized; uncertain outcomes retain the lock.
Authorization: Bearer key; account:read + buildings:write.
Parameters
| Parameter | Contract |
|---|
Idempotency-Key header · required | Stable identifier for this action. Reuse the identical body and key after a timeout.stringpattern: ^[a-zA-Z0-9_-]{16,100}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. BuildingOperation |
| 201 | Created. BuildingOperation |
| 202 | Pending or uncertain: inspect status and next_action. BuildingOperation |
| default | Structured 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 exactly one active/trialing Multi Building subscription, matching billed quantity, maximum 20 buildings including primary. Preview purchases nothing, creates a 15-minute quote and invoice snapshot. Only US addresses supported for additions. Removal cannot target primary; review release/deletion effects.
Authorization: Bearer key; account:read + buildings:write.
JSON request body
Responses
| HTTP status | Contract |
|---|
| 201 | Created. BuildingOperation |
| default | Structured 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. Account mutations are serialized; uncertain outcomes retain the lock. The JSON DELETE body is required. Permanently releases the number, deletes child rules/activity, revokes child keys, disables child webhooks, and reduces subscription quantity.
Authorization: Bearer key; account:read + buildings:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
Idempotency-Key header · required | Stable identifier for this action. Reuse the identical body and key after a timeout.stringpattern: ^[a-zA-Z0-9_-]{16,100}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. BuildingOperation |
| 202 | Pending or uncertain: inspect status and next_action. BuildingOperation |
| default | Structured 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 older than five minutes is reported as reconciliation_required. Quotes/receipts are not erased when the quote expires.
Authorization: Bearer key; account:read + buildings:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. BuildingOperation |
| default | Structured 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
Requires active subscription and provisioned selected number. Lite: timer; Standard: timer/passcode; Premium, Business, Multi Building: all three. Linked buildings require Multi Building. New grants start enabled. A repeated idempotency key returns the current grant (including revoked state), never reopens it; a changed body conflicts.
Authorization: Bearer key; access:write.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
Idempotency-Key header · required | Stable identifier for this action. Reuse the identical body and key after a timeout.stringpattern: ^[a-zA-Z0-9_-]{16,100}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Unlock Location: On new 201: relative /v1/unlock/{id}. string
Idempotency-Replayed: Present on replay. string
constant: "true" |
| 201 | Created. Unlock Location: On new 201: relative /v1/unlock/{id}. string
Idempotency-Replayed: Present on replay. string
constant: "true" |
| default | Structured 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. active=true means timer within window, nonexpired/nonexhausted enabled passcode, or enabled routine (not necessarily inside its schedule).
Authorization: Bearer key; access:read.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
active query | stringone of: true, false |
type query | stringone of: timer, passcode, routine |
limit query | integerminimum: 1 maximum: 100 default: 50 |
cursor query | Use pagination.cursor; this endpoint uses a grant ID cursor.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. UnlockPage |
| default | Structured 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
| Parameter | Contract |
|---|
id path · required | stringOpaque 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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Unlock ETag: Supply this exact quoted value as If-Match when updating. string
Quoted decimal version, e.g. "3". |
| default | Structured 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. Missing/malformed version: 428 VERSION_REQUIRED; changed version: 409 VERSION_CONFLICT. Reread before retrying. Revoked grants cannot be reopened. Changing max_uses resets remaining uses. Creation plan/setup checks apply except body exactly {"active":false}; linked-building PATCH still requires active Multi Building even for deactivation. DELETE remains the downgrade cleanup path.
Authorization: Bearer key; access:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque 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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
If-Match header · required | stringpattern: ^"[0-9]+"$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Unlock ETag: Supply this exact quoted value as If-Match when updating. string
Quoted decimal version, e.g. "3". |
| default | Structured 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. No manual-revocation callback is currently dispatched.
Authorization: Bearer key; access:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque 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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. UnlockRevoked |
| default | Structured 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. Failed entries and sensitive passcodes may be included. Older records may lack unlock_id. No server streaming endpoint.
Authorization: Bearer key; logs:read.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
limit query | integerminimum: 1 maximum: 100 default: 50 |
since query | stringISO 8601 timestamp. format: date-time |
until query | stringISO 8601 timestamp. format: date-time |
type query | stringconstant: "unlock" |
cursor query | Opaque base64url cursor; do not construct or decode as part of client logic.string |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. LogPage |
| default | Structured 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
Register callback
Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Maximum five registrations per selected building, including inactive registrations. events is required in REST. Save the secret once. No idempotency support: after uncertain creation, inspect list before repeating; if the secret was lost, delete/recreate deliberately.
Authorization: Bearer key; webhooks:write.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 201 | Created. WebhookCreated |
| default | Structured 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 callback registrations
Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Newest first, no pagination. Secret is never returned; optional status timestamps are omitted until set.
Authorization: Bearer key; webhooks:write.
Parameters
| Parameter | Contract |
|---|
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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Webhooks |
| default | Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error |
PATCH /v1/webhooks/{id}
Update callback registration
Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Returns updated fields, failure counters, last success/failure and created/updated timestamps. disabledAt/disabledReason are list-only. No signing-secret rotation endpoint; create a replacement registration.
Authorization: Bearer key; webhooks:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque 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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
JSON request body
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Webhook |
| default | Structured 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 callback registration
Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. Hard delete; repeating returns 404.
Authorization: Bearer key; webhooks:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque 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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. Deleted |
| default | Structured 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 callback
Requires active subscription for every webhook operation, including list, delete, and test. Linked building requires Multi Building. One attempt, ten-second timeout; no automatic retries for test. Sends even to inactive registrations. HTTP 200 describes the attempt: inspect success and statusCode. Test does not create access or reset automatic failure state.
Authorization: Bearer key; webhooks:write.
Parameters
| Parameter | Contract |
|---|
id path · required | stringOpaque 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.stringOpaque resource ID, normally 24 hexadecimal characters. pattern: ^[a-fA-F0-9]{24}$ |
No request body.
Responses
| HTTP status | Contract |
|---|
| 200 | Success. WebhookTest |
| default | Structured API error; see error handling and the error code catalog. Hosting/proxy failures and unknown paths can return non-JSON bodies. Error |