{"ok": true, "service": "droidfleet BankID", "what": "Complete a Swedish BankID order on a fleet phone that holds a real Mobile BankID identity. You bring the autoStartToken; your own browser stays yours.", "auth": "Authorization: Bearer <key> on everything except /, /ui, /live, /manifest, /providers, /qr", "input": "ONE value: the order's autoStartToken. A bare UUID, a bankid:///?autostarttoken=<uuid> link, an app.bankid.com URL or a JSON blob containing one \u2014 all accepted, anything else is rejected with 400 before the phone is touched.", "endpoints": {"POST /login": "{token, env?, rp?, source_url?, browser_ip?} -> a verdict. The alias /solve is identical. This is the whole API for most callers.", "POST /arm": "{env?, rp?, source_url?, browser_ip?} -> pre-warm the phone BEFORE the order exists. Optional but worth ~25s of a token's short life; call it the moment you see a login start. Send browser_ip here too: this is the only moment the SAME-IP answer is still actionable \u2014 the same finding on a /login verdict arrives after the PIN, when the order is already spent.", "POST /pin": "{device, wait?} -> type the PIN on a pad YOUR driver already opened. For a caller that owns the browser and cannot cross into a native app.", "GET /health": "is the phone reachable (per device/env). Also carries `egress` \u2014 the address THIS PHONE's BankID app exits as, so you can compare it to your own browser's before spending an order.", "GET /status": "the full phone/BankID state", "GET /events?since=i": "what the phone is doing right now, for a 1s poll", "GET /providers": "relying parties we can drive end-to-end (CLI, not this API)", "GET /qr": "human QR screenshot relay surface; creates no BankID order", "POST /qr-sessions": "{rp_url?} -> short-lived session with opaque view token", "POST /qr-sessions/<id>/snapshot": "raw image/* body, max 4 MiB -> classification and stored image", "GET /qr-sessions/<id>": "session state; QR pixels are never included in JSON", "GET /qr-sessions/<id>/image?view=<token>": "latest image, authorized by API key and view token", "POST /qr-sessions/<id>/event": "{event,url?}; explicit RP progress event"}, "env": {"meaning": "which BankID environment the ORDER lives in; we route it to the handset holding that identity, so you never learn which phone that is", "values": ["auto", "production", "test"], "default": "auto \u2014 inferred from source_url / the order's issuing host", "why": "a test order fired at the production identity dies as 'order not found', which reads exactly like an expired token"}, "response": {"ok": "the PIN went in. Your own page landing logged-in is what proves the login.", "stage": "identified | pin_not_submitted | no_pad | blocked | busy | error", "retryable": "TRUE = fire this same order again (bounded, with backoff). FALSE = stop; another attempt only burns the phone. Never re-derive this from `stage` \u2014 this flag is the contract.", "replayed": "present when we had already completed this exact order and returned the stored verdict instead of firing a spent token. A repeat POST is safe.", "device/env": "which handset and identity actually answered", "egress": "SAME-IP check. state = aligned | mismatch | unknown | n/a. BankID cancels an order AFTER the PIN when the app and the browser exit different addresses, so `ok:true, stage:identified` with state=mismatch is an order that will die. Fix the BROWSER, never the phone's pin."}, "browser_ip": {"what": "your browser's public egress IP, e.g. from https://api.ipify.org. Optional, one fetch, and the only thing a client is asked to measure.", "why": "the phone's BankID app egresses a pinned residential IP; a browser that exits somewhere else makes the two halves of one login look like two people. Without it we answer state=unknown \u2014 we will not guess."}, "clients": {"python": "bankid/client.py \u2014 `from bankid.client import login; login(token)`, or `python3 -m bankid.client <token>`", "curl": "curl -sX POST $BACKEND/login -H 'Authorization: Bearer $KEY' -H 'Content-Type: application/json' -d '{\"token\":\"<uuid>\"}'", "browser": "bankid/extension \u2014 detects the order on any site and posts it for you", "macos": "bankid/macapp \u2014 registers as the bankid:// handler"}, "qr_relay": {"purpose": "move a screenshot of the live BankID QR to a screen the S21 can scan", "workflow": "create session -> upload screenshot -> scan with BankID on S21 -> verify the original RP page", "does_not": "generate QR data, receive qrStartSecret, inject camera input, or claim RP login completion", "states": ["waiting_for_qr", "qr_detected", "qr_unreadable", "rp_polling", "bankid_approved", "website_login_confirmed", "cancelled", "expired"], "limits": {"session_ttl_seconds": 120, "snapshot_bytes": 4194304}}, "devices": {"s21": {"env": "production", "identity": "Philip R\u00f6nnmark", "platform": "android"}, "a14": {"env": "test", "identity": "Herbert Jonsson (test)", "platform": "android"}}}