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

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.

ScopeAllowsDefault agent key
account:readAccount readiness, building inventory, setup statusYes
access:readRead access rulesYes
access:writeCreate, update, revoke accessYes
logs:readRead activityYes
webhooks:writeManage and test callbacksYes
buildings:writePreview and execute paid building additions and permanent removalsExplicit opt-in
setup:writeChange release toneNo
billing:writeCheckout and billing portalNo
keys:writeManage delegated keysNo

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.

StatusNext step
quotedReview and execute before expiration
expiredObtain a fresh preview; no mutation started
runningPoll buildings operation ID
succeededRead the stored result; a retry returns the same receipt
reconciliation_requiredContact 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 pathPurpose / body
GET /v1/healthService health
GET /v1/plansPublic live Standard/Premium prices
POST /v1/auth/otpRequest email challenge: {email}
POST /v1/auth/verifyVerify: {email,challenge,otp}; returns owner credential
DELETE /v1/auth/keyRevoke current key
GET, POST /v1/auth/keysList/create delegated keys; create: {name,scopes,expires_in_days}
DELETE /v1/auth/keys/:idRevoke a delegated key
GET /v1/accountSubscription, number, capabilities, readiness, next actions
GET /v1/billing/plansAuthenticated plan catalog
GET, POST /v1/billing/checkoutRecover/create checkout; create: {plan}
POST /v1/billing/checkout/:id/expireExpire unpaid checkout
POST /v1/billing/portalHosted billing portal
GET, PATCH /v1/setupInspect setup / configure {unlock_tone}
GET /v1/buildingsAllowed building inventory and virtual numbers
POST /v1/buildings/previewAdd: {action:"add",label,address:{street,city,state,zip,country:"US"},unlock_tone,phone_number}; remove: {action:"remove",building_id}
POST /v1/buildingsExecute addition: {quote_id}
DELETE /v1/buildings/:idExecute removal with JSON body {quote_id}
GET /v1/buildings/operations/:idDurable operation status and receipt
GET, POST /v1/unlockList/create timer, passcode, routine
GET, PATCH, DELETE /v1/unlock/:idRead, update with If-Match, revoke
GET /v1/logsActivity; filter since,until,type,limit,cursor
GET, POST /v1/webhooksList/register; create: {url,events,description}
PATCH, DELETE /v1/webhooks/:idUpdate URL/events/active/description, or delete
POST /v1/webhooks/:id/testSend 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 codeMeaning
0Request succeeded; inspect operation status for pending work
2Invalid input; fix the request
3Authentication or permission failure
4Conflict; reread state or inspect the original operation
5Temporary 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.

OptionMeaning
-V, --versionoutput the version number
--jsonOutput 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

OptionMeaning
--api-key <key>Authenticate directly with an API key

buzzer auth request-code

Run buzzer auth request-code --help for usage.

OptionMeaning
--email <email> (required)Account email

buzzer auth verify-code

Run buzzer auth verify-code --help for usage.

OptionMeaning
--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)

OptionMeaning
--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

OptionMeaning
--revokeAlso 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

OptionMeaning
--activeShow 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

OptionMeaning
--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

OptionMeaning
--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

OptionMeaning
--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
--followPoll 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

OptionMeaning
--url <url> (required)Webhook URL
--events <events>Comma-separated event types Default: unlock.completed.
--description <text>Description

buzzer webhooks update <id>

Update a webhook

OptionMeaning
--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

OptionMeaning
--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

OptionMeaning
--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

OptionMeaning
--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.

OptionMeaning
--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.

OptionMeaning
--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