REST API reference
Every QuickAuth feature is reachable over plain HTTPS — handy when you’re on an unsupported stack, doing server-to-server orchestration, or just want to curl-test before you wire the SDK.
Base URL
https://api.quickauth.in
All endpoints accept and return application/json.
Authentication
Three header schemes depending on which surface you’re hitting:
| Header | Used for | Key format |
|---|---|---|
X-Public-Key | SDK endpoints (/v1/sdk/*) — safe in clients | qa_live_*** / qa_test_*** |
X-Client-Id + X-Client-Secret | Server-to-server (token mint, dashboard ops) | qa_client_*** + qa_secret_*** |
Authorization: Bearer … | SDK calls authenticated with sessionToken | 10-min JWT |
Conventions
- All timestamps are ISO 8601 UTC.
- Error responses have shape
{ errorCode, message }with the matching HTTP status —errorCodeis an UPPER_SNAKE constant (see Error codes). POST /v1/auth/initiateis rate-limited per identity per hour (default 10/hour, configurable per merchant). Over the limit returns429 RATE_LIMIT_EXCEEDED.
Direct OTP API (server-to-server)
The raw server endpoints — authenticate with X-Client-Id + X-Client-Secret. Use these when you send and verify OTPs entirely from your own backend (no SDK). This is the pair behind a typical curl integration.
/v1/auth/initiateSend an OTP
Generates and dispatches the OTP over the chosen channel and returns a requestId you pass to /verify.
curl https://api.quickauth.in/v1/auth/initiate \
-H "Content-Type: application/json" \
-H "X-Client-Id: qc_xxx" \
-H "X-Client-Secret: qa_secret_yyy" \
-d '{
"identity": "+919876543210",
"identityType": "MOBILE",
"mode": "OTP",
"channel": "WHATSAPP"
}'Allowed values
| Field | Accepted values | Notes |
|---|---|---|
mode | OTP, OAUTH, MAGIC_LINK | Use OTP for send-and-verify OTP. |
identityType | MOBILE, EMAIL | MOBILE = phone number, EMAIL = email address. |
channel | SMS, WHATSAPP, EMAIL | Optional — omit to let the router pick. (RCS / TRUECALLER exist but aren’t OTP-send channels.) |
Valid combinations
Not every combination works: phone OTP is identityType: "MOBILE" with channel: "SMS" or "WHATSAPP"; email OTP is identityType: "EMAIL" with channel: "EMAIL". Keep mode: "OTP" for the send/verify flow — OAUTH and TRUECALLER belong to a different (identity-token) flow.
/v1/auth/verifyVerify the OTP
Checks the user-entered code against the requestId. Returns verified: true/false.
curl https://api.quickauth.in/v1/auth/verify \
-H "Content-Type: application/json" \
-H "X-Client-Id: qc_xxx" \
-H "X-Client-Secret: qa_secret_yyy" \
-d '{
"requestId": "req-abc123",
"otp": "123456"
}'requestId vs orderId
Pass either requestId (returned by /initiate) or the orderId you supplied at initiate — not both.otp is the code the user received.
Session
/v1/sdk/sessionMint a 10-minute session token
Called by your backend so SDKs can authenticate. Requires server credentials.
curl https://api.quickauth.in/v1/sdk/session \
-H "Content-Type: application/json" \
-H "X-Client-Id: qa_client_xxx" \
-H "X-Client-Secret: qa_secret_yyy" \
-d '{"scope":"sdk"}'OTP
/v1/sdk/auth/initiateStart an OTP verification
Returns a sessionId. The SDK uses this to verify the user-entered code.
POST /v1/sdk/auth/initiate
Content-Type: application/json
X-Public-Key: qa_live_***
Idempotency-Key: <uuid>
{
"phone": "+919876543210",
"channel": "auto", // "auto" | "sms" | "whatsapp"
"template": "login_otp", // optional, defaults to your default template
"deviceInfo": { // optional, used for trusted-device / OneTap
"platform": "ios",
"model": "iPhone 14",
"osVersion": "18.2",
"appVersion":"3.4.1",
"locale": "en-IN"
}
}/v1/sdk/auth/verifyVerify the OTP code
Returns verified: true/false and a requestId. QuickAuth deliberately does not issue a session token — the merchant's backend confirms the verification server-to-server and mints its own session JWT. See Backend integration.
POST /v1/sdk/auth/verify
Content-Type: application/json
Authorization: Bearer <sdk-session-token>
Idempotency-Key: <uuid>
{
"sessionId": "qas_5fA2…",
"code": "123456"
}Why no JWT here
QuickAuth is a verification provider, not an identity provider. We tell you whether the phone was verified — your backend owns the user table and the session. The merchant's app forwards the requestId to its own backend, which confirms the result via GET /v1/auth/status (below) and then mints its own session JWT against its own user table.
/v1/auth/statusConfirm a verification (server-to-server)
Your backend calls this with the requestId the app forwarded. The response reveals the authoritative auth status. Use server credentials — never call this from the app.
curl 'https://api.quickauth.in/v1/auth/status?requestId=req_8h3k…' \
-H 'X-Client-Id: qa_client_***' \
-H 'X-Client-Secret: qa_secret_***'WhatsApp OTP
/v1/sdk/auth/whatsapp/startInitiate WhatsApp OTP
Returns a wa.me URL the SDK opens. User taps Send, we capture the message, the SDK gets the same { verified, requestId, message } shape as the OTP verify endpoint — your backend confirms with GET /v1/auth/status exactly the same way.
Attribution
/v1/sdk/attribution/launchRecord a launch event
Captures UTMs, qa_clid, install referrer, fingerprint. Consent-gated.
/v1/sdk/attribution/conversionRecord a conversion
Free-form event with optional value and currency.
Webhooks
Configure a webhook URL on the dashboard. Events you’ll receive:
| Event | Fired when |
|---|---|
otp.initiated | An OTP verification is started. |
otp.verified | The user entered the correct code. |
otp.expired | The OTP timed out without verification. |
session.created | A 10-minute SDK session was minted. |
delivery.failed | SMS/WhatsApp dispatch failed permanently. |
campaign.delivered | Marketing campaign batch finished. |
Signing
Every webhook carries an X-QuickAuth-Signature header — HMAC-SHA256 of the raw body with your webhook secret. Verify it before trusting the payload.
Error codes
Every error returns { "errorCode": "…", "message": "…" }. Common errorCode values:
| errorCode | HTTP | Meaning |
|---|---|---|
INVALID_REQUEST | 400 | Body failed validation — a required field is missing or malformed. |
INVALID_MOBILE | 400 | identity isn’t a valid mobile number. |
INVALID_CLIENT_CREDENTIALS | 401 | X-Client-Id / X-Client-Secret missing or wrong. |
RATE_LIMIT_EXCEEDED | 429 | Too many /initiate calls for this identity this hour. |
OTP_EXPIRED | 400 | OTP TTL elapsed — call /initiate again. |
MAX_ATTEMPTS_EXCEEDED | 400 | Too many wrong codes for this requestId. |
TEMPLATE_NOT_FOUND | 400 | No SMS/WhatsApp template configured for the requested send. |
PROVIDER_ERROR | 502 | Downstream SMS/WhatsApp provider failed to accept the send. |
SDK quickstart from API
If you’ve worked through this page directly, the SDK pages give you the same calls wrapped with native idioms, auto-fill helpers, and consent plumbing. Pick a stack from the docs home.