Get a 200 from /api/auth/login with typical guided setup
Follow these five steps in order. By the end, your app will be receiving real OneTimeLogin access tokens.
-
1
Register your site
Create a business account and add your site. This unlocks the API console where your keys live.
Register your site -
2
Grab your API key
After registration, your Site Key is shown once on the dashboard at
Dashboard → Settings → API Keys. Store it in your server-side secret manager — never commit it. -
3
Configure your callback URL
Tell OneTimeLogin where to redirect users after a successful login. Set it under
Settings → Callback URL. Use HTTPS in production. Localhost is allowed in dev. -
4
Test login with code
Pick your stack and run the snippet. Replace
YOUR_SITE_KEYwith the key from Step 2.curl -X POST https://api.onetimelogin.com/api/auth/login \ -H "Content-Type: application/json" \ -H "X-OTL-Site-Key: YOUR_SITE_KEY" \ -d '{"email":"user@example.com","password":"REDACTED"}'const res = await fetch('https://api.onetimelogin.com/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-OTL-Site-Key': process.env.OTL_SITE_KEY, }, body: JSON.stringify({ email, password }), }); const data = await res.json(); // data.access_token + data.refresh_tokenimport requests res = requests.post( 'https://api.onetimelogin.com/api/auth/login', headers={ 'Content-Type': 'application/json', 'X-OTL-Site-Key': OTL_SITE_KEY, }, json={'email': email, 'password': password}, ) data = res.json() # data['access_token'] + data['refresh_token'] -
5
Verify the response
A successful login returns HTTP 200 and a payload like this:
{ "access_token": "<jwt>", "refresh_token": "<opaque>", "token_type": "Bearer", "expires_in": 1800, "user": { "id": "01HKZ...", "email": "user@example.com", "name": "Jane Doe" } }Send
access_tokenon every authenticated call asAuthorization: Bearer <access_token>.
You just authenticated a user via OneTimeLogin.
Want help with production rollout, custom roles, or bulk migration? Talk to a human.
Need to revoke a token? See Sessions. Building a webhook receiver? See Events. Want SAML instead? See Enterprise SSO.
What you can build
Three of the most common integrations.
SSO sign-in flow
Drop OneTimeLogin in front of any login screen. One registration, every connected partner site.
Read guide →Bulk user registration
Migrate from your existing user table with a CSV. Onboard thousands without resetting passwords.
Read guide →Network-wide profile sync
When a user updates their profile on one partner site, every other site sees the change instantly via webhook.
Read guide →SDKs & libraries
The HTTP API works from any language. We also publish thin wrappers for the common ones.
Node.js / TypeScript
npm install @onetimelogin/sdk
Full types, retry, refresh-token rotation built in.
Python
pip install onetimelogin
Sync + async clients, Pydantic models, FastAPI helper.
Need Go, Ruby, or PHP? See the raw HTTP API — and let us know what to build next at developers@onetimelogin.com.
Sessions & tokens
- Access token: short-lived JWT, 30 minutes. Send as
Authorization: Bearer <access_token>. - Refresh token: opaque string, 30 days. Exchange for a new access token at
POST /api/auth/refresh. - Revoke:
POST /api/auth/logoutrevokes the current session.DELETE /api/sessions/{id}revokes a specific one. Bumps the user's Session Version. - Storage: store access tokens in
sessionStorage, refresh tokens in HttpOnly cookies. NeverlocalStorage.
Full schema in the Sessions API reference. For force-logout-everywhere flows, see Events → session.revoked.
Webhook events
We POST to your registered URL whenever something happens. Verify X-OTL-Signature (HMAC-SHA256) before processing.
| Event | Fires when |
|---|---|
| user.created | A new user registers anywhere in the network. |
| user.updated | Profile fields change on any partner site. |
| session.created | A user logs in to one of your sites. |
| session.revoked | A session is logged out or admin-revoked. |
| role.assigned | A user is granted a role on your site. |
| payment.completed | A paid plan order is fulfilled. |
Retries: up to 5 with exponential backoff. See full payloads in the API webhooks reference. Need to test locally? See the webhooks reference.
Error codes
Every error response is JSON: {"detail": "<message>", "request_id": "<uuid>"}.
Always log request_id — we use it to find the matching server log when you write to support.
| Code | Meaning | Action |
|---|---|---|
| 400 | Bad request | Check the field listed in detail. |
| 401 | Unauthenticated | Token missing, expired, or revoked. Refresh. |
| 403 | Forbidden | Token is valid but lacks the role for this resource. |
| 404 | Not found | Resource ID is wrong, or it belongs to a different site. |
| 409 | Conflict | Duplicate email on register, version mismatch on update. |
| 422 | Validation error | Body parsed, but a field failed validation. See detail. |
| 429 | Rate limited | Honor Retry-After. |
| 5xx | Server error | Retry with backoff. Page status: /status. |
Enterprise SSO (SAML / OIDC)
OneTimeLogin can sit in front of Okta, Azure AD, or any SAML 2.0 / OIDC IdP. Your end users get the network experience; their IT keeps central control.
- Just-in-time provisioning — we create the user on first login.
- SCIM 2.0 user/group sync (Pro plan).
- Per-domain forced SSO — e.g. all
@acme.comusers must use SAML.
Setup is 30 min with a metadata XML or OIDC discovery URL. Email us to start.
FAQ
Do I have to migrate my existing user passwords?
No. Bulk import accepts your existing bcrypt / argon2 / scrypt hashes. Users keep their current password — first login auto-rehashes to our parameters. See Bulk registration.
Where is data stored? Is it GDPR / SOC 2?
EU and US regions. SOC 2 Type II audit underway; target completion Q4 2026. GDPR-aligned with DPA available on request. Full posture on the Security page.
Can a user belong to many partner sites with one account?
Yes — that is the point. The user registers once and chooses which sites in the Network to authorize. Each site sees only the fields the user grants it.
What happens if your service is down?
Existing access tokens stay valid for their full 30-minute lifetime — your app keeps serving authenticated users. Live status: status.onetimelogin.com.
Glossary
- API key
- The long-lived secret your server uses for site-level operations. Distinct from a user's access token.
- Site Key
- The public identifier for one registered site. Sent in
X-OTL-Site-Key. Safe to expose in server-side code; never in browser code. - Access Token
- Short-lived (30 min) signed JWT representing one user's session.
- Refresh Token
- Opaque, long-lived (30 day) credential used to mint new access tokens without re-auth.
- Session Version
- Integer stamped into every access token. Bumping it (e.g. on password change) invalidates every existing token in one move.
- Webhook Event
- A signed POST to your registered URL when something happens — user.created, session.revoked, etc.
- Network
- The set of all partner sites a user has authorized. One identity, many sites.
- Partner Site
- Any site that has registered with OneTimeLogin and accepts our access tokens.
API reference
Every endpoint, every parameter, every response code — with copyable examples.