Quickstart
You’ll provision a tenant with the admin key, copy the three credentials it
returns, then make a first signed call (prepare-pairing). By the end you’ll
have a pairing_proof — the short token you forward to the device to start a
pairing.
Step 1 — Provision a tenant
Section titled “Step 1 — Provision a tenant”Only tenant_name is required. All other fields (rate limits, QR-login
origins, branding, WebAuthn configuration) are optional at provisioning and
editable afterward via /update/tenant.
curl $BASE_URL/api/v1/provision/tenant \ -X POST \ -H "X-Admin-Key: YOUR_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{ "tenant_name": "example_backend", "rate_limit_per_min": 120, "qr_login_allowed_origins": ["https://example.com"], "callback_url_base": "https://api.example.com", "branding_display_name": "Example", "branding_logo_url": "https://example.com/logo.png" }'The response contains the tenant_secret — it is shown only once:
{ "success": true, "status_code": 201, "message": "Tenant provisioned. Save tenant_secret now — it will not be shown again.", "data": { "tenant_id": "tnt_...", "tenant_secret": "sk_...", "tenant_name": "example_backend", "rate_limit_per_min": 120, "created_at": "2026-06-21T..." }}Step 2 — Store the credentials
Section titled “Step 2 — Store the credentials”Keep the three values in the calling backend’s .env:
RELAY_TENANT_ID=tnt_...RELAY_TENANT_SECRET=sk_...RELAY_BASE_URL=https://auth-relay.bloonio.comStep 3 — Make a signed call
Section titled “Step 3 — Make a signed call”Every /relay/* call carries three HMAC headers. The signature is
hex(hmac_sha256(tenant_secret, f"{ts_ms}.{sha256_hex(body)}")), the timestamp
is in Unix milliseconds and must be within ±30 s of the relay’s clock.
# First compute the headers (see the Python tab for the full# recipe), then:curl $BASE_URL/api/v1/relay/prepare-pairing \ -X POST \ -H "X-Bloonio-Tenant-Id: tnt_..." \ -H "X-Bloonio-Timestamp: 1718966400000" \ -H "X-Bloonio-Signature: <hex_hmac_sha256>" \ -H "Content-Type: application/json" \ -d '{ "user_socket_hash": "9f86d081884c7d659a2feaa0c55ad015", "backend_user_id": "507f1f77bcf86cd799439011", "user_email": "jane@example.com", "display_name": "Example" }'import hashlibimport hmacimport jsonimport time
import requests
BASE_URL = "https://auth-relay.bloonio.com"TENANT_ID = "tnt_..."TENANT_SECRET = "sk_..."
body = json.dumps( { "user_socket_hash": "9f86d081884c7d659a2feaa0c55ad015", "backend_user_id": "507f1f77bcf86cd799439011", "user_email": "jane@example.com", "display_name": "Example", }, separators=(",", ":"),).encode()
ts_ms = str(int(time.time() * 1000))body_sha = hashlib.sha256(body).hexdigest()signature = hmac.new( TENANT_SECRET.encode(), f"{ts_ms}.{body_sha}".encode(), hashlib.sha256,).hexdigest()
resp = requests.post( f"{BASE_URL}/api/v1/relay/prepare-pairing", headers={ "X-Bloonio-Tenant-Id": TENANT_ID, "X-Bloonio-Timestamp": ts_ms, "X-Bloonio-Signature": signature, "Content-Type": "application/json", }, data=body, # IMPORTANT: send exactly the bytes you signed timeout=10,)resp.raise_for_status()print(resp.json()["data"]["pairing_proof"])On success, you get back:
{ "success": true, "status_code": 201, "message": "Pairing prepared. Forward pairing_proof to the device.", "data": { "pairing_proof": "eyJhbGciOi...", "expires_in": 300 }}What’s next?
Section titled “What’s next?”- Finish the pairing. Forward the
pairing_proofto the device, which exchanges it for adevice_session_token. See Pairing & devices. - Push challenges. Once the device is paired, send approval prompts with Push & security alerts.
- Avoid signing by hand. If your backend is in Python, the Python SDK handles HMAC signing, the sudo protocol, and callbacks for you.