Server-side · v1

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:

HeaderUsed forKey format
X-Public-KeySDK endpoints (/v1/sdk/*) — safe in clientsqa_live_*** / qa_test_***
X-Client-Id + X-Client-SecretServer-to-server (token mint, dashboard ops)qa_client_*** + qa_secret_***
Authorization: Bearer …SDK calls authenticated with sessionToken10-min JWT

Conventions

  • All timestamps are ISO 8601 UTC.
  • Error responses have shape { errorCode, message } with the matching HTTP status — errorCode is an UPPER_SNAKE constant (see Error codes).
  • POST /v1/auth/initiate is rate-limited per identity per hour (default 10/hour, configurable per merchant). Over the limit returns 429 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.

POST/v1/auth/initiate

Send 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

FieldAccepted valuesNotes
modeOTP, OAUTH, MAGIC_LINKUse OTP for send-and-verify OTP.
identityTypeMOBILE, EMAILMOBILE = phone number, EMAIL = email address.
channelSMS, WHATSAPP, EMAILOptional — 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.

POST/v1/auth/verify

Verify 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

POST/v1/sdk/session

Mint 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

POST/v1/sdk/auth/initiate

Start 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"
  }
}
POST/v1/sdk/auth/verify

Verify 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.

GET/v1/auth/status

Confirm 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

POST/v1/sdk/auth/whatsapp/start

Initiate 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

POST/v1/sdk/attribution/launch

Record a launch event

Captures UTMs, qa_clid, install referrer, fingerprint. Consent-gated.

POST/v1/sdk/attribution/conversion

Record a conversion

Free-form event with optional value and currency.

Webhooks

Configure a webhook URL on the dashboard. Events you’ll receive:

EventFired when
otp.initiatedAn OTP verification is started.
otp.verifiedThe user entered the correct code.
otp.expiredThe OTP timed out without verification.
session.createdA 10-minute SDK session was minted.
delivery.failedSMS/WhatsApp dispatch failed permanently.
campaign.deliveredMarketing 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:

errorCodeHTTPMeaning
INVALID_REQUEST400Body failed validation — a required field is missing or malformed.
INVALID_MOBILE400identity isn’t a valid mobile number.
INVALID_CLIENT_CREDENTIALS401X-Client-Id / X-Client-Secret missing or wrong.
RATE_LIMIT_EXCEEDED429Too many /initiate calls for this identity this hour.
OTP_EXPIRED400OTP TTL elapsed — call /initiate again.
MAX_ATTEMPTS_EXCEEDED400Too many wrong codes for this requestId.
TEMPLATE_NOT_FOUND400No SMS/WhatsApp template configured for the requested send.
PROVIDER_ERROR502Downstream 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.