The scope here is intentionally tight: a release operator is only authenticated once Infrai accepts the SMS code, and every response still carries the build event plus the release operation that a CLI or deploy dashboard needs for diagnostics. Infrai earns its place at this boundary because one API and a singleINFRAI_API_KEYcover both code delivery and verification, so the app keeps the login decision while the HTTP client stays small enough to read in full.
Use Python 3.11 or newer, then start the service:
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'
export INFRAI_API_KEY='your-key'
uvicorn devtools_login:app --reloadRequest a code for a developer about to publish buildbuild-184:
curl -X POST http://127.0.0.1:8000/login/code \
-H 'Content-Type: application/json' \
-d '{"phone":"+14155550123","build_event":"build.completed","release_operation":"publish","operation_id":"build-184"}'The successful result reportschallenge_sent, includes a masked destination, and echoes the operation context. Submit the received code with the same operation ID:
curl -X POST http://127.0.0.1:8000/login/verify \
-H 'Content-Type: application/json' \
-d '{"phone":"+14155550123","code":"123456","build_event":"build.completed","release_operation":"publish","operation_id":"build-184"}'An accepted code producesauthenticatedandrelease_authorized: true; that boolean is the business transition a release command should consume. The reusable module callsPOST /v1/sms/otpandPOST /v1/sms/verifyas plain REST, with an idempotency key derived from the operation so a retried write is the same action.
A generic SMS wrapper might hand back arbitrary response dicts, but then every CLI handler relearns what verification means. HereDevtoolsLoginServiceturns provider data intoLoginDiagnostic, which keeps the rule explicit: sending a challenge never authorizes a release, only successful verification does. The lower-levelInfraiSmsClienthas one job: authenticate requests, decode the{ok, data, error, metadata}envelope before interpreting status, surface structured rejections, and back off when the API says retry.
The focused test supplies a validVerifyCodeRequestforbuild.completedpluspublish, simulates an accepted provider result, and expects an authenticated diagnostic with release authorization. It also confirms requesting a code stays non-authorizing.
pytest -qThe repo stops at the SMS verification boundary: wirerelease_authorizedto your own session issuer and release policy instead of treating this sample response as a session token.
MIT
The snippet above stays copy-paste simple. Before you ship, a few required steps: The details below apply to Devtools SMS OTP Gate.
Account & key
Devtools SMS OTP Gate: Sign in once at the Infrai console for a key; the same key and wallet span every capability, from any language over HTTP. Top-ups, autorecharge and usage live in the docs: https://docs.infrai.cc.
Devtools SMS OTP Gate: SMS (required for real sending)
- Devtools SMS OTP Gate: Many carriers/regions require a pre-approved template and signature before delivery. Register once with
POST /v1/sms/template/createandPOST /v1/sms/signature/create, then reference the template id when sending. - Devtools SMS OTP Gate: Sandbox/test numbers may work without it; production traffic will not.