Zoho’s redirect target
const url = 'https://api.sloose.com/auth/zoho/callback';const options = {method: 'GET'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url https://api.sloose.com/auth/zoho/callbackZoho sends the browser here — the one redirect URI registered on the Zoho client. For a sign-in it hands the browser to Better Auth’s callback for the data centre that issued the code (location), which exchanges it, records the identity, and creates the session in the organisation the person was last in, or in none. The browser lands on /auth/zoho/done.
For the offline grant (POST /auth/orgs/{org}/connect) this exchanges the code itself, reads which CRM it is, makes a connection for it — or renews the one a reconnect named; adding a CRM this organisation already holds is refused, naming the connection to reconnect instead (another organisation holding the same CRM is no reason to refuse: each organisation connects with its own grant) — stores the refresh token on it, and reads the CRM’s modules and fields. A connect started in a window of its own (mode: popup, a CRM tab’s) then answers with the popup page, which posts the outcome to its opener; one started outside a CRM tab (mode: redirect) answers 303 back to the page it began on, with ?connected=<id> or ?connect_error=<why>.
You never call this directly; Zoho does.
Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”Zoho’s authorization code.
Zoho’s authorization code.
The OAuth state.
The OAuth state.
The data centre Zoho actually issued the code in. Honoured over the requested one.
The data centre Zoho actually issued the code in. Honoured over the requested one.
The accounts host that issued the code; the data centre when location is absent.
The accounts host that issued the code; the data centre when location is absent.
Set when the user declined or Zoho refused.
Set when the user declined or Zoho refused.
Responses
Section titled “Responses”A small HTML page that posts the outcome to window.opener and closes itself. Not a JSON API.
A sign-in: on to Better Auth’s callback, then /auth/zoho/done — with ?error=sign_in_failed when something failed inside the callback, which the done page says in fixed words.
A connect started outside a CRM tab (mode: redirect): back to the page it began on, with ?connected=<id> or ?connect_error=<why>.
A small HTML page that posts the outcome to window.opener and closes itself. Not a JSON API.
A small HTML page that posts the outcome to window.opener and closes itself. Not a JSON API.
Adding a CRM this organisation already holds as a live connection — whether it was connected long ago or by another window a moment ago — naming that connection and its Reconnect; a reconnect whose organisation on Zoho’s screen was not the connection’s own; or the connection being reconnected is no longer the organisation’s. The popup page carries sloose:auth-error.
A small HTML page that posts the outcome to window.opener and closes itself. Not a JSON API.
The restore switch is on (code: MAINTENANCE): every route but /health and /robots.txt is refused before anything is read, except the operator’s own POST /staff/people/{id}/delete with the ADMIN_TOKEN. Sloose is down for maintenance. Please try again in a few minutes.
object
Example
{ "code": "MAINTENANCE"}Headers
Section titled “Headers”Seconds until asking again is worth it (300 while the switch is on).