Wave Passport
Wave Unlock SDK

Open a door from your app in five lines.

Embed Wave Passport Bluetooth door unlock into your product. Native SDKs for engineers, a drop-in button for vibe coders, and an MCP server so your AI agent does the integration for you.

Built for AI builders MCP-first

Add the Wave MCP server to your coding agent and say "add Wave door unlock." It scaffolds, validates, diagnoses, and tests the full flow — with no hardware.

{
  "mcpServers": {
    "wave": {
      "command": "npx",
      "args": ["-y", "@wave/mcp"],
      "env": {
        "WAVE_GATEWAY_URL": "https://app.wavepassport.com/api"
      }
    }
  }
}

wave_docs

The whole SDK as agent-ingestible text.

wave_scaffold

Writes a working starter into your project.

wave_doctor

Catches missing BLE permissions before you build.

wave_simulate_unlock

Proves grant/deny end-to-end — no reader needed.

wave_validate_config

Checks your key + sites are correct.

wave_register_app

Provisions your partner keys.

Quickstart

Prove the whole unlock flow in your terminal, no door required.

  1. Get keys. Ask your agent to run wave_register_app, or use the keys from your Wave dashboard. You get a wave_pub_* (in-app) and a wave_test_* (testing).
  2. Exchange for a token. POST /partner-auth/token with your test key returns a short-lived session token.
  3. Simulate an unlock. POST /unlock-mock with a scenario, then read /unlock-stream — the exact result a real door would return.
  4. Wire the real thing. Use wave_scaffold web (runs today via Web Bluetooth) or a native SDK; embed your wave_pub_* key.

Try it live now — copy, paste, run

A public demo test key. It only drives the mock (synthetic events on an isolated demo site) — it cannot open a real door. You'll get a real granted back from the live gateway.

BASE="https://app.wavepassport.com/api"
TEST_KEY="wave_test_9097529120bb93a92d058d65007fb555"

TOKEN=$(curl -s "$BASE/partner-auth/token" \
  -H "Content-Type: application/json" -d "{\"key\":\"$TEST_KEY\"}" | jq -r .token)

curl -s "$BASE/unlock-mock" -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"card_id":"10001","scenario":"granted"}'

curl -s "$BASE/unlock-stream" -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" -d '{"card_id":"10001"}'
# -> {"status":"granted","reason":"[mock] Access Granted"}

# try a denial: swap scenario for member_not_found | outside_hours |
#                membership_expired | outstanding_balance

How the unlock works

Five steps, hidden behind one SDK call.

  1. Scan for the reader — name SKBluTag / service 496B2C43-….
  2. Proximity gate on signal strength (RSSI), tunable per site.
  3. Write the credential — 0x01 + member number, write-without-response.
  4. Await the cloud result (grant/deny) for up to 5s.
  5. Show "Access Granted" or a friendly denial reason.

Platforms

iOS · Swift

Native background BLE. Swift Package + WaveUnlockButton.

Android · Kotlin

Foreground-service BLE. AAR + Compose button.

React Native

One JS API bridging the native cores.

Flutter

Dart plugin over the native cores.

Web

Web Bluetooth, foreground-only. Runs today.

Gateway API

Base https://app.wavepassport.com/api — no API key header required; the gateway handles it.

EndpointAuthReturns
POST /partner-auth/tokensession token from a pub/test key
POST /unlock-streamBearer token{status, reason}, tenant-scoped
POST /unlock-mockBearer test tokenemits a synthetic event
POST /partner-auth/registeradmin keypartner keys (once)

Full machine-readable spec: openapi.yaml · agent context: llms.txt / llms-full.txt