Authentication
OAuth 2.0
So a third-party app can act on a broker's behalf without ever seeing their password.
Orkasa is an OAuth 2.0 authorization server (RFC 6749). It is how Zapier connects, and how the MCP connector connects.
| URL | |
|---|---|
| Authorization | https://orkasa.app/oauth/authorize |
| Token | https://orkasa.app/api/oauth/token |
| Refresh | https://orkasa.app/api/oauth/token |
Grants: authorization_code (with PKCE S256) and refresh_token.
The flow
Send the broker to authorize
https://orkasa.app/oauth/authorize
?response_type=code
&client_id=ork_client_…
&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback
&scope=leads%3Aread%20leads%3Awrite%20viewings%3Awrite
&state=<random anti-CSRF value>
&code_challenge=…&code_challenge_method=S256
The consent screen names the app, the brokerage it will reach, and each permission in plain language. If the broker is not signed in they log in first and come back to this exact URL.
Orkasa redirects back with a code
…/callback?code=ork_ac_…&state=<your state>
On refusal: ?error=access_denied&state=…. Your state comes back
verbatim.
Exchange the code for tokens
curl -X POST https://orkasa.app/api/oauth/token \
-d grant_type=authorization_code \
-d client_id=ork_client_… \
-d client_secret=ork_cs_… \
-d code=ork_ac_… \
-d code_verifier=… \
--data-urlencode "redirect_uri=https://example.com/callback"{
"access_token": "ork_at_…",
"refresh_token": "ork_rt_…",
"token_type": "bearer",
"expires_in": 3600,
"scope": "leads:read leads:write viewings:write"
}Client credentials may also be sent as HTTP Basic.
Call the API
Authorization: Bearer ork_at_…, on every endpoint, unchanged.
Refresh after an hour
curl -X POST https://orkasa.app/api/oauth/token \
-u "ork_client_…:ork_cs_…" \
-d grant_type=refresh_token \
-d refresh_token=ork_rt_…Refresh tokens rotate: each refresh returns a new pair and revokes the old
one. Store the new refresh_token every time. They last 60 days.
Errors
The OAuth endpoints speak RFC 6749, not the /api/v1 envelope:
{ "error": "invalid_grant", "error_description": "…" }
invalid_client (401) · invalid_grant, invalid_request, invalid_scope,
unsupported_grant_type (400).
Every authorization-code failure — unknown, expired, already used, wrong client,
wrong redirect_uri, bad PKCE verifier — returns the same invalid_grant, so a
caller cannot probe which it was.
What holds the flow up
- Codes are single-use, expire in 5 minutes, and are bound to the issuing
client_idand the exactredirect_uri. Redemption is atomic: a race cannot redeem one twice. redirect_uriis matched by exact string equality against the registered whitelist. Never by prefix, never wildcarded. When a client or redirect URI fails validation, Orkasa renders the error on its own origin and redirects nowhere.- PKCE is verified whenever a
code_challengewas registered, and onlyS256is accepted. - Tokens, codes and secrets are all stored as SHA-256 digests. Nothing is logged in the clear, and nothing is recoverable after the response that issued it.
- Revoking a token row kills the access token and its refresh token together — which is also how rotation retires the previous pair.