BUZZER API / DOCUMENTATION
Connect an agent.
Let someone in.
From the first sign-in to managing multiple buildings: the complete agent workflow and command reference.
Apartment access, through software
Buzzer API is the product and REST interface. Buzzer CLI is the buzzer command-line tool. An MCP server is not currently included.
Use it when a delivery driver, guest, cleaner, dog walker, or contractor needs access through an apartment or condo buzzer, telephone intercom, call box, or entry system that dials a phone number. It does not control a hardware-only intercom. A building manager must connect the provisioned virtual number and someone must test the release tone at the entrance.
This documentation describes the connector implementation. Deployment and live provider verification are separate release steps. Use your configured service origin; do not assume the planned https://api.buzzerapi.com endpoint or a public npm package is available.
For agents: read the onboarding guide, retrieve the machine-readable command contract, or run buzzer describe without credentials.
Sign in, subscribe, connect
- Open Account setup, or use the CLI email-code flow below. The account owner supplies the six-digit email sign-in code through the secure authentication flow.
- Read live plan prices, choose the returned plan ID, and complete the returned Stripe hosted checkout with the owner's authorization. Standard and Premium are available through this checkout. Subscribe to or upgrade to Multi Building in the Lowkey app.
- Check account and billing status until the subscription is active and a virtual number is provisioned. A checkout URL or return page is not proof of activation.
- Have the building manager set the call box to dial that virtual number. Set its exact release keypress (1–3 characters from digits,
#, *), then test at the entrance. - Issue a scoped agent key, save its secret once, and use it for ongoing access management.
# From an authorized checkout of the private buzzer-cli repository:
npm ci
npm run build
npm link # makes the local buzzer command available
export BUZZER_BASE_URL="https://YOUR_CONFIGURED_SERVICE_ORIGIN"
buzzer describe
buzzer auth request-code --email owner@example.com --json
buzzer auth verify-code --email owner@example.com --challenge CHALLENGE --code EMAIL_CODE --json
buzzer billing plans --json
buzzer billing checkout --plan PLAN_ID --request-id checkout-attempt-001 --json
# Open the returned hosted checkout; complete the authorized payment.
buzzer billing status --json
buzzer account --json
buzzer setup status --json
buzzer setup configure --unlock-tone 9 --json
buzzer auth delegate --name "Delivery agent" --expires-in-days 30 --json
Verification saves the owner credential locally; it does not print the secret. auth login is the interactive alternative. Agents should use request-code and verify-code. A hosted payment step and building-manager setup remain necessary; the CLI cannot bypass either. Use billing status to recover a checkout after a timeout, billing expire SESSION_ID to discard an unpaid checkout before selecting another plan, and billing portal for a hosted billing-management URL.
Credentials and permissions
Set BUZZER_API_KEY in your agent's secret environment or use its protected connector settings. Never put secrets in a URL, public repository, or agent prompt. auth status reports local configuration; account checks the server. auth logout clears the local credential; add --revoke to revoke it on the server.
| Scope | Allows | Default agent key |
account:read | Account readiness, building inventory, setup status | Yes |
access:read | Read access rules | Yes |
access:write | Create, update, revoke access | Yes |
logs:read | Read activity | Yes |
webhooks:write | Manage and test callbacks | Yes |
buildings:write | Preview and execute paid building additions and permanent removals | Explicit opt-in |
setup:write | Change release tone | No |
billing:write | Checkout and billing portal | No |
keys:write | Manage delegated keys | No |
Agent keys expire in 1–90 days. Secrets are returned once. Use auth keys and auth revoke-key KEY_ID to manage keys delegated by the current owner key. A delegated key cannot grant permissions its parent lacks. The website keeps its short-lived owner credential in an encrypted HttpOnly cookie and only issues agent keys after an active subscription. Its optional Allow building management checkbox adds buildings:write; ordinary keys cannot add paid numbers.
# Run with a parent-account owner credential, then store the returned agent key.
buzzer auth delegate --name "Building manager" --expires-in-days 30 \
--scopes account:read,access:read,access:write,logs:read,webhooks:write,buildings:write --json
Multiple buildings and virtual numbers
An active Multi Building plan permits a parent-account agent to manage linked buildings, each with its own virtual number, access rules, activity, and webhook subscriptions. List buildings and select the intended entrance by ID, label, and number.
buzzer buildings list --json
buzzer --building BUILDING_ID setup status --json
buzzer --building BUILDING_ID unlock list --json
buzzer --building BUILDING_ID logs --follow --json
buzzer --building BUILDING_ID webhooks create --url https://agent.example/buzzer --json
Accounts with linked buildings must select explicitly, including for the primary building. Single-building accounts can omit selection. REST clients send X-Buzzer-Building-Id on setup, unlock, logs, and webhook requests. Access responses and callbacks identify building_id. Register a webhook for each building you want to monitor; the destination can be shared.
A child-account key is confined to that child. Linked-building writes require the Multi Building plan; reading and revoking existing access remain available after downgrade. Billing, key ownership, and building management stay account-level: do not pass --building to those commands.
Add a building
Use a parent-account key with both account:read and buildings:write. The account must already have one active or trialing Multi Building subscription whose billed quantity matches its building inventory. The limit is 20 buildings including the primary. New-number provisioning currently requires a US city, two-letter state, and ZIP code. The resident forwarding number must be E.164; if omitted, the parent's configured number is used.
buzzer buildings preview-add --label "East entrance" \
--street "100 Example Street" --city Seattle --state WA --zip 98101 \
--unlock-tone 9 --phone-number +12065550123 --json
# Inspect input, billing, and expires_at. Execute only the authorized quote.
buzzer buildings add --quote QUOTE_ID --json
buzzer buildings operation QUOTE_ID --json
buzzer buildings list --json
A preview purchases nothing. It shows current/new subscription quantity and Stripe's upcoming invoice estimate, in minor currency units (for example, cents for USD). It is not an immediate charge or refund promise. Quotes last 15 minutes and become invalid if the building inventory or billing snapshot changes. Executing an add increases subscription quantity and provisions a number through Lowkey's existing workflow. Connect and physically test the new number before granting visitors access.
Remove a building
buzzer buildings preview-remove BUILDING_ID --json
# Verify the exact building and number and every listed effect.
buzzer buildings remove BUILDING_ID --quote QUOTE_ID --json
buzzer buildings operation QUOTE_ID --json
Removal releases the phone number, permanently deletes the linked building's rules and activity history, revokes its API keys, disables its webhooks, and reduces billed quantity with the existing proration behavior. The primary building cannot be removed through this flow. This is different from revoking one visitor's access.
Retries and operation recovery
The quote ID is also the operation ID. The CLI uses building-QUOTE_ID as its stable request ID unless --request-id is supplied. After a timeout, inspect the operation and reuse the original quote and request ID. Never create a new preview to bypass an uncertain operation.
| Status | Next step |
quoted | Review and execute before expiration |
expired | Obtain a fresh preview; no mutation started |
running | Poll buildings operation ID |
succeeded | Read the stored result; a retry returns the same receipt |
reconciliation_required | Contact support with the operation ID. Do not start another request. |
App and API building mutations share an account lock. An uncertain provider outcome keeps the lock for reconciliation instead of risking another number purchase or release. HTTP 202 and a successful CLI exit mean the operation status was returned, not that the building change completed; inspect status and next_action.
Visitor access rules
A timer allows entry during a short window without a visitor code. A passcode is useful for a specific guest or delivery. A recurring routine allows a weekly schedule and requires Premium or Multi Building. Confirm the owner's intended visitor and time window before creating access.
buzzer --building BUILDING_ID unlock create --type timer --duration 15 \
--label "Package delivery" --request-id delivery-timer-001 --json
buzzer --building BUILDING_ID unlock create --type passcode --expires-in 60 \
--max-uses 1 --label "Guest" --request-id guest-passcode-001 --json
buzzer --building BUILDING_ID unlock create --type routine --days weekdays \
--start 09:00 --end 17:00 --timezone America/Los_Angeles \
--label "Cleaner" --request-id cleaner-routine-001 --json
buzzer --building BUILDING_ID unlock get UNLOCK_ID --json
buzzer --building BUILDING_ID unlock update UNLOCK_ID --label "Updated label" --if-version VERSION --json
buzzer --building BUILDING_ID unlock delete UNLOCK_ID --json
Omitting a passcode generates four digits; the default is one use and one hour. Explicit custom codes preserve the app's rule: 1–4 numeric digits except 1 alone, which calls the resident. Six-digit visitor codes are not supported. This is separate from the six-digit email sign-in code. Passcodes support 1–100 uses and expiration within seven days. The visitor calls the unit and enters the code when prompted.
Use --expires-at with an ISO timestamp and timezone when an absolute expiry is needed. Recurring schedules use an IANA timezone; --days accepts one weekday, weekdays, weekends, or daily. Enabled does not mean a routine is currently inside its scheduled window. List with --active, --type, --limit, and --cursor.
Creation supports a stable request ID of 16–100 letters, digits, hyphens, or underscores. Reuse it after timeouts; a replay does not reopen revoked access. Updates use the current version (If-Match in REST); on conflict, reread and decide whether the change is still appropriate. Repeated deletion is safe. Revocation affects that rule, not unrelated rules that may also allow entry.
Know when access is used
buzzer --building BUILDING_ID webhooks create --url https://agent.example/buzzer \
--events unlock.completed --description "Routine use" --json
buzzer --building BUILDING_ID webhooks test WEBHOOK_ID --json
buzzer --building BUILDING_ID webhooks list --json
buzzer --building BUILDING_ID webhooks update WEBHOOK_ID --active true --json
buzzer --building BUILDING_ID webhooks delete WEBHOOK_ID --json
# No public callback endpoint? Poll persisted activity instead.
buzzer --building BUILDING_ID logs --follow --json
buzzer --building BUILDING_ID logs --since 2026-09-19T00:00:00Z --json
The CLI default callback is unlock.completed: Lowkey accepted a timer, passcode, or routine and prepared the release tone. This is not proof that the physical door opened or someone entered. The envelope contains event, eventId, building_id, created_at, and data. For use events, data.unlock_id identifies the rule, data.activity_id matches the log ID, and data.occurred_at gives the recorded activity time. Successful-use callbacks omit passcodes; denied-access callbacks may include the attempted code. See the exact callback payloads and signature example.
Incoming-call handling also emits access.denied and unlock.revoked when a use-limited rule is exhausted. Although the event validator accepts unlock.created and unlock.expired, those lifecycle callbacks are not currently emitted. Do not rely on them or assume every manual deletion emits a revocation callback. The test callback is webhook.test and must not trigger an access action.
REST registration requires an explicit nonempty events array. All webhook routes require an active subscription and webhooks:write, including listing and deletion. There are at most five registrations per building, including inactive ones. Save the signing secret returned on creation; it is shown once. Verify X-Buzzer-Signature: t=TIMESTAMP,v1=HEX_DIGEST with HMAC-SHA256 over TIMESTAMP.raw_request_body. Use the unmodified request bytes and a constant-time digest comparison; reject timestamps more than five minutes from your clock. Deduplicate by eventId, queue processing, and promptly return HTTP 2xx.
Network failures, HTTP 429, and 5xx retry up to three total attempts. Delivery is best effort: retries run in-process and a restart can interrupt them. Ten failed attempts disable an endpoint; inspect the webhook list, fix the endpoint, and reactivate it. Recover missed use events from persisted logs with an overlapping timestamp checkpoint, every pagination cursor, and deduplication by log ID. Filter successful entries and unlock_id; older records may lack that field. logs --follow --json polls and outputs one JSON event per line.
REST API reference
Read every endpoint, request/response field, scope, limit, and error, or download the OpenAPI 3.1 contract. The table below is an overview.
Use Authorization: Bearer YOUR_API_KEY, Content-Type: application/json for JSON bodies, and the configured service origin. Public plans, health, and email-code authentication do not require a key. Building-scoped requests use X-Buzzer-Building-Id. Mutating access creation, checkout, and building execution use Idempotency-Key. Treat IDs as opaque.
curl "$BUZZER_BASE_URL/v1/buildings" \
-H "Authorization: Bearer $BUZZER_API_KEY"
curl -X POST "$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: guest-passcode-001" \
-d '{"type":"passcode","expires_in_minutes":60,"max_uses":1,"label":"Guest"}'
| Method and path | Purpose / body |
| GET /v1/health | Service health |
| GET /v1/plans | Public live Standard/Premium prices |
| POST /v1/auth/otp | Request email challenge: {email} |
| POST /v1/auth/verify | Verify: {email,challenge,otp}; returns owner credential |
| DELETE /v1/auth/key | Revoke current key |
| GET, POST /v1/auth/keys | List/create delegated keys; create: {name,scopes,expires_in_days} |
| DELETE /v1/auth/keys/:id | Revoke a delegated key |
| GET /v1/account | Subscription, number, capabilities, readiness, next actions |
| GET /v1/billing/plans | Authenticated plan catalog |
| GET, POST /v1/billing/checkout | Recover/create checkout; create: {plan} |
| POST /v1/billing/checkout/:id/expire | Expire unpaid checkout |
| POST /v1/billing/portal | Hosted billing portal |
| GET, PATCH /v1/setup | Inspect setup / configure {unlock_tone} |
| GET /v1/buildings | Allowed building inventory and virtual numbers |
| POST /v1/buildings/preview | Add: {action:"add",label,address:{street,city,state,zip,country:"US"},unlock_tone,phone_number}; remove: {action:"remove",building_id} |
| POST /v1/buildings | Execute addition: {quote_id} |
| DELETE /v1/buildings/:id | Execute removal with JSON body {quote_id} |
| GET /v1/buildings/operations/:id | Durable operation status and receipt |
| GET, POST /v1/unlock | List/create timer, passcode, routine |
| GET, PATCH, DELETE /v1/unlock/:id | Read, update with If-Match, revoke |
| GET /v1/logs | Activity; filter since,until,type,limit,cursor |
| GET, POST /v1/webhooks | List/register; create: {url,events,description} |
| PATCH, DELETE /v1/webhooks/:id | Update URL/events/active/description, or delete |
| POST /v1/webhooks/:id/test | Send test callback |
Use the CLI reference below for every available command, argument, filter, and option. buzzer COMMAND --help and buzzer describe expose the same command tree locally.
Errors and reliable automation
Run agents with --json. Successful commands return JSON on stdout, errors go to stderr, and following logs produces JSONL. Inspect structured errors and their code, requestId, retryable, and next_action where supplied. Retain request IDs for support without logging secrets. Respect rate-limit and retry headers.
| CLI exit code | Meaning |
|---|
| 0 | Request succeeded; inspect operation status for pending work |
| 2 | Invalid input; fix the request |
| 3 | Authentication or permission failure |
| 4 | Conflict; reread state or inspect the original operation |
| 5 | Temporary server or transport failure; recover with the original request ID |
QUOTE_EXPIRED or QUOTE_STALE requires a fresh preview before execution. BUILDING_BUSY requires checking the current operation, not spinning up parallel requests. BUILDING_BILLING_MISMATCH requires support reconciliation. A reconciliation_required receipt blocks further building changes until the provider outcome is resolved.
Complete CLI command reference
Generated from buzzer describe. Global options apply before the command; use --json for automation. Building selection applies only to setup, access, logs, and webhooks.
buzzer
Lowkey: Arrange authorized entry for any visitor through an apartment or condo buzzer, intercom, or call box that dials a phone number.
| Option | Meaning |
|---|
-V, --version | output the version number |
--json | Output raw JSON |
--base-url <url> | Override API base URL |
--api-key <key> | Override API key |
--building <id> | Building ID from buildings list; required for multi-building operations |
buzzer auth
Manage authentication
buzzer auth login
Authenticate with email/OTP or directly with an API key
| Option | Meaning |
|---|
--api-key <key> | Authenticate directly with an API key |
buzzer auth request-code
Run buzzer auth request-code --help for usage.
| Option | Meaning |
|---|
--email <email> (required) | Account email |
buzzer auth verify-code
Run buzzer auth verify-code --help for usage.
| Option | Meaning |
|---|
--email <email> (required) | Account email |
--challenge <id> (required) | Challenge from request-code |
--code <code> (required) | User-provided sign-in code |
buzzer auth delegate
Create a scoped, expiring agent key (secret shown once)
| Option | Meaning |
|---|
--name <name> | Name Default: Delivery agent. |
--scopes <scopes> | Comma-separated scopes; default: account/access/logs/webhooks without billing or setup |
--expires-in-days <days> | Lifetime from 1 to 90 days Default: 30. |
buzzer auth keys
List agent keys delegated by this owner key
buzzer auth revoke-key <id>
Revoke a delegated agent key
buzzer auth logout
Remove stored API key
| Option | Meaning |
|---|
--revoke | Also revoke this key on the server |
buzzer auth status
Show current authentication status
buzzer health
Check BuzzerAPI health status
buzzer account
Show account details and usage
Aliases: status
buzzer unlock
Manage buzzer unlocks (timers, passcodes, routines)
buzzer unlock list
List all unlocks
| Option | Meaning |
|---|
--active | Show only active unlocks |
--type <type> | Filter by type (timer, passcode, routine) |
--limit <n> | Page size (1-100) |
--cursor <cursor> | Continue from the previous page |
buzzer unlock get <id>
Inspect current access, expiry, uses, and version
buzzer unlock create
Create a new unlock
| Option | Meaning |
|---|
--type <type> | Access type: passcode, timer, routine Default: passcode. |
--request-id <id> | Reuse an explicit request ID to recover an earlier create |
--expires-in <minutes> | Passcode lifetime, defaults to 60 minutes (max 10080) |
--expires-at <iso> | Passcode expiry as ISO timestamp with timezone |
--duration <minutes> | Duration in minutes (timer) |
--code <code> | 1-4 digits, except 1 alone (default: random 4-digit code) |
--max-uses <n> | Max uses (default: 1) |
--days <days> | Day or preset: mon-sun, weekdays, weekends, daily (routine) |
--start <time> | Start time HH:MM (routine) |
--end <time> | End time HH:MM (routine) |
--timezone <tz> | IANA timezone (routine) |
--label <text> | Label for the unlock |
buzzer unlock update <id>
Update an existing unlock
| Option | Meaning |
|---|
--label <text> | New label |
--if-version <version> | Only update this version (default: read current version) |
--expires-at <iso> | New passcode expiry, within 7 days |
--active <bool> | Set active status (true/false) |
--duration <minutes> | New duration (timer) |
--code <code> | New 1-4 digit passcode, except 1 alone |
--max-uses <n> | New max uses (passcode) |
--days <days> | New days (routine) |
--start <time> | New start time (routine) |
--end <time> | New end time (routine) |
--timezone <tz> | New timezone (routine) |
buzzer unlock delete <id>
Delete an unlock
Aliases: revoke
buzzer logs
View activity logs
| Option | Meaning |
|---|
--limit <n> | Number of entries (1-100) Default: 50. |
--since <iso> | Start date (ISO 8601) |
--until <iso> | End date (ISO 8601) |
--type <type> | Filter by type (unlock) |
--cursor <cursor> | Continue from a pagination cursor |
--follow | Poll for new entries every 5s |
buzzer webhooks
Receive signed callbacks when an access rule is used
buzzer webhooks list
List all webhooks
buzzer webhooks create
Create a new webhook
| Option | Meaning |
|---|
--url <url> (required) | Webhook URL |
--events <events> | Comma-separated event types Default: unlock.completed. |
--description <text> | Description |
buzzer webhooks update <id>
Update a webhook
| Option | Meaning |
|---|
--url <url> | New URL |
--events <events> | New comma-separated event types |
--active <bool> | Set active status (true/false) |
--description <text> | New description |
buzzer webhooks delete <id>
Delete a webhook
buzzer webhooks test <id>
Send a test event to a webhook
buzzer buildings
List, preview, add, and remove linked buildings on Multi Building
buzzer buildings list
List permitted buildings, labels, and virtual numbers
buzzer buildings preview-add
Preview a new building and its subscription change; no number is purchased
| Option | Meaning |
|---|
--label <name> (required) | Building name |
--street <street> | Street address |
--city <city> (required) | US city |
--state <state> (required) | Two-letter US state |
--zip <zip> (required) | US ZIP code |
--unlock-tone <tone> (required) | Building release keypress: 1–3 digits, # or * |
--phone-number <e164> | Resident call-forwarding number; defaults to parent's configured number |
buzzer buildings preview-remove <id>
Preview releasing a linked number, deleting its rules/history, and reducing billed quantity
buzzer buildings add
Execute an approved add preview: purchases a number and increases subscription quantity
| Option | Meaning |
|---|
--quote <id> (required) | Fresh quote from preview-add |
--request-id <id> | Stable retry ID; defaults to building-QUOTE_ID |
buzzer buildings remove <id>
Execute an approved removal: releases the number and permanently deletes building rules/history
| Option | Meaning |
|---|
--quote <id> (required) | Fresh quote from preview-remove for this building |
--request-id <id> | Stable retry ID; defaults to building-QUOTE_ID |
buzzer buildings operation <id>
Recover a building change result after timeout; never blindly repeat an uncertain provider operation
buzzer setup
Phone-based buzzer setup
buzzer setup status
Run buzzer setup status --help for usage.
buzzer setup configure
Run buzzer setup configure --help for usage.
| Option | Meaning |
|---|
--unlock-tone <tone> (required) | Exact building unlock digits |
buzzer billing
Lowkey subscription plans and hosted checkout
buzzer billing plans
Run buzzer billing plans --help for usage.
buzzer billing checkout
Run buzzer billing checkout --help for usage.
| Option | Meaning |
|---|
--plan <plan> (required) | Plan ID returned by billing plans |
--request-id <id> | Stable ID for this checkout attempt; reuse when retrying |
buzzer billing status
Recover the latest checkout URL and next step
buzzer billing expire <id>
Expire an unpaid checkout so a different plan can be selected
buzzer billing portal
Run buzzer billing portal --help for usage.
buzzer describe
Show product fit, compatibility, and CLI commands as JSON, without authentication