https://api.offlineprotocol.com/api/v1.
These endpoints use a user JWT, not a portal API key. Never put an administrative API secret in a browser or mobile app. Send your public App ID in X-App-Id: app_...; obtain it from the developer portal.
Authenticate with email
POST /auth/send-codewith JSON{ "email": "person@example.com" }.- Ask the user for the code.
POST /auth/verify-codewith{ "email": "person@example.com", "otp": "123456" }. - Read
data.tokenfrom the response. SendAuthorization: Bearer <token>on authenticated requests, alongsideX-App-Id.
Content-Type: application/json for JSON bodies. User tokens are RS256 JWTs with audience user; public verification keys are at https://api.offlineprotocol.com/api/v1/auth/.well-known/jwks.json. Treat tokens as credentials and keep them out of URLs and logs.
Sessions and sign-out
A user token expires after 30 days by default. Guest sessions also receive a token with audienceuser; developer and admin tokens use the audiences developer and admin and are not accepted by OfflineID endpoints or the hosted relay. To sign out, send POST /auth/users/sessions/revoke with the token; add { "all": true } to sign the user out on every device. Confirming an account deletion also signs out every session. After that, hosted services answer the token with 401 and “Session has ended. Sign in again.” Clear the stored token and sign the user in again.
Response envelopes
Most endpoints return{ "message": "OK", "data": ... }. Auth verification returns data.token and a snake_case data.user. Account/profile endpoints use the endpoint-specific camelCase schemas shown in the reference.
Most service errors use { "message": "...", "data": {} }; gateway errors can use data: null. Rate-limit errors can include a top-level error with code and retryAfter. Do not require every error to have identical fields. Credential issuance and issuer-key success responses are unwrapped JSON objects.
Rate limits
The telemetry ingest limit is shared by every installation that uses the key. The SDK uploads about every 30 seconds, so the budgets fit roughly 300, 1,500, 6,000 and 30,000 installations uploading at once. It protects ingest from floods; monthly usage is governed by your plan’s quotas. A throttled upload gets 429 with
Retry-After in seconds, and the SDK keeps the batch and retries after that delay.
Limits are independently enforced and can be configured by deployment. Handle 429, honor Retry-After when present and use structured retry guidance (error.retryAfter or the endpoint’s documented data.retry_after_seconds). Do not immediately retry an OTP request. A network timeout does not prove that a mutation was rejected.
Deployment compatibility
The gateway maps older SDKs’x-project-id header to X-App-Id, so sign-ins count toward the correct application. React 0.2.0 and React Native 0.3.3 send x-app-id directly. UUID connection deletion is available through DELETE /connections/{uuid}.
Allowed web origins are enforced for app-attributed browser requests with an Origin header and a configured origin list. Origin checks are being rolled out in log-only mode before blocking. Requests without an Origin header and apps with no configured origins are not checked. See allowed web origins; this is not a security boundary for non-browser callers. Proof of Location runs on the Ethereum Sepolia testnet.
Gateway failures and usage limits
A gateway 502 can use{ "message": "...", "error": "...", "upstream": "...", "code": "...", "detail": "..." } without a data envelope. Handle the status and available fields without assuming the service error shape.
Hosted usage limits are active. A Free organization without a card that uses a full quota, or an organization whose payment failed after Stripe’s retries, has all metered hosted services paused. With a card on file, reaching a meter’s billing limit pauses that meter’s service. A paused request answers HTTP 402: sign-in and code requests with “Sign-in is paused for this app. Please contact the app’s developer.”, telemetry ingest with error usage_limit_reached, and Proof of Location commitments with a message naming the cause. Treat 402 as final for that request, not as a retryable failure. See usage warnings, billing limits and pauses for recovery. Local peer-to-peer traffic is never metered or paused.
The consumer leaderboard, referrals and wallet/account-update operations are outside this developer API reference.
