Get Started

Authentication & Credentials

The API has two independent auth models depending on who's calling.

Console auth (Firebase ID token)

Used by the developer console for everything you manage about your own account — projects, applications, webhook registrations, logs, analytics. Every request carries a Firebase Authentication ID token:

Authorization: Bearer <firebase-id-token>

The token is verified against Google's public signing keys on every request — issuer, audience, expiry, and signature are all checked before anything touches Firestore.

Application auth (Client ID + Client Secret + API Key)

Used by your own backend server when calling the OTP endpoints on behalf of one of your applications. All three headers are required together:

x-client-id: ph_live_client_xxxxxxxxxxxxxxxxxxxxxxxx
x-client-secret: ph_live_secret_xxxxxxxxxxxxxxxxxxxxxxxx
x-api-key: ph_live_api_xxxxxxxxxxxxxxxxxxxxxxxx

The API validates the API Key first (fast rejection of malformed or unknown keys), then confirms the Client ID matches that application, then verifies the Client Secret against its stored hash. All three must match the same application or the request is rejected with 401 UNAUTHENTICATED.

Never call OTP endpoints directly from a browser or mobile app — the Client Secret and API Key must stay server-side. The Client ID alone is safe to embed client-side, but by itself it authenticates nothing.

API-key-only endpoints

A small number of read endpoints — currently GET /v1/users/:phoneNumber — accept just x-api-key without the full triple, since they're read-only lookups rather than OTP-issuing actions.

Credential formats

CredentialFormatRetrievable after creation?
Client IDph_(live|test)_client_<48 hex chars>Yes — always visible in the console
Client Secretph_(live|test)_secret_<48 hex chars>No — shown once, then only a masked preview
API Keyph_(live|test)_api_<48 hex chars>No — shown once, then only a masked preview
Webhook Secretwhsec_<48 hex chars>Yes — always retrievable in full from the console

Every credential is stored server-side only as a salted SHA-256 hash — except the Webhook Secret, which the platform must keep in plaintext to sign outgoing webhook deliveries.

Rotating credentials

From Credentials in the console, each of Client Secret, API Key, and Webhook Secret can be rotated independently via POST /v1/applications/:id/rotate/:type, where :type is secret, api, or webhook_secret. Rotation invalidates the old value immediately — anything still using it will start failing until updated.

Ownership scoping

Every console-authenticated resource (project, application, webhook, OTP request) is checked against the calling developer's UID. Attempting to access a resource you don't own returns 404 NOT_FOUND rather than 403 FORBIDDEN — this avoids revealing whether a resource ID exists at all.