Send the admin to our sign-in page
Redirect the admin's browser to:
JavaScriptGET https://api.leadferno.com/v0/oauth2/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.example.com/leadferno/callback
&response_type=code
&state=RANDOM_PER_ATTEMPT_VALUEDo not build your own login form. The Swagger spec shows /v0/oauth2/authorize taking a username and password in a POST body, and there are POST /v0/oauth2 and POST /v0/sessions password endpoints — those exist for our own first-party apps. If you collect Leadferno credentials yourself you take on the liability of handling other companies' admin passwords. Use the redirect above.
The state parameter is required
You generate it, we echo it back untouched. Per sign-in attempt:
Generate a random, unguessable value (16 random bytes hex-encoded is fine).
Store it against the browser session, server-side.
When the callback fires, compare the returned
stateto the stored value before exchanging the code. If it doesn't match, stop.
This check is what protects your callback from CSRF and replay. Never send a blank or fixed value.
What the admin sees
They sign in on api.leadferno.com. On success we redirect the browser to:
JavaScript{redirect_uri}?code=THE_CODE&state=YOUR_STATEOn bad credentials we re-render our own sign-in page with the error shown — you don't build that screen. Note that client_id and redirect_uri are validated after the admin submits, not when the page loads, so always test the full round trip.
Exchange the code for tokens
JavaScriptPOST https://api.leadferno.com/v0/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
code=THE_CODE_FROM_THE_CALLBACK
client_id=YOUR_CLIENT_ID
client_secret=YOUR_CLIENT_SECRET
redirect_uri=https://yourapp.example.com/leadferno/callbackclient_secretis required for this grant.Authorization codes expire 5 minutes after issue and are single-use.
You get back an access token and a refresh token (both are signed JWTs). Every subsequent API call uses:
JavaScriptAuthorization: Bearer <access_token>Refresh before the access token expires
JavaScriptPOST https://api.leadferno.com/v0/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
refresh_token=YOUR_REFRESH_TOKEN
client_id=YOUR_CLIENT_ID
client_secret=YOUR_CLIENT_SECRETRefresh tokens rotate on every use. Each successful refresh returns a new refresh token and immediately invalidates the previous one. Because of that:
Persist the new refresh token from every refresh response before you use it.
Never run two refreshes at once for the same connection — the second one presents an already-invalidated token and fails. Put a per-connection lock around refresh.
If a refresh fails with an auth error, the connection is dead — send the admin back through sign in.
Refresh proactively (on a timer, or when you get a 401), behind that lock. Store all tokens encrypted at rest, one set per connected Leadferno account.