OAuth 2.0
Kyo is an OAuth 2.0 provider. Your application sends a user to Kyo's consent screen and receives scoped, revocable tokens to call the REST API on their behalf. The authorization-code flow with PKCE is required for every client — public and confidential alike.
Register an application
Applications are registered inside the Kyo app under Settings → API
(workspace admins). Registration is self-serve and gives you a client id
(kyoapp_…) plus up to 10 redirect URIs.
- Public clients — desktop, CLI, and mobile apps that can't keep a secret. No client secret is issued; PKCE alone protects the exchange.
- Confidential clients — server-side apps. A client secret (
kyo_sk_…) is shown once at creation and stored only as a hash — save it immediately.
Apps that haven't been verified by Kyo show a warning banner on the consent screen. Users can still authorize them.
Endpoints
| Endpoint | URL |
|---|---|
| Authorization | https://app.trykyo.com/oauth/authorize |
| Token | https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-token |
| Revocation | https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-revoke |
Token and revocation requests are form-encoded POSTs and also need the public
apikey header — the same anon key used for
REST API requests.
Authorization flow
Before starting, generate a PKCE pair: a random code_verifier (43–128
characters) and its code_challenge = base64url(SHA-256(verifier)). Only the
S256 challenge method is accepted.
1. Send the user to the consent screen
https://app.trykyo.com/oauth/authorize
?client_id=kyoapp_…
&redirect_uri=https://yourapp.com/callback
&response_type=code
&scope=deals:read%20tasks:write
&state=…
&code_challenge=…
&code_challenge_method=S256
The user signs in to Kyo (if needed), reviews the requested scopes, and approves. Kyo
redirects back to your redirect_uri with ?code=kyo_ac_…&state=….
Always verify state matches what you sent.
Authorization codes are single-use and expire after 60 seconds — exchange them immediately.
2. Exchange the code for tokens
curl -X POST "https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "apikey: $KYO_ANON_KEY" \
-d grant_type=authorization_code \
-d code=kyo_ac_… \
-d code_verifier=… \
-d client_id=kyoapp_… \
-d redirect_uri=https://yourapp.com/callback
{
"access_token": "kyo_at_…",
"refresh_token": "kyo_rt_…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "deals:read tasks:write"
}
Confidential clients also authenticate this request — either HTTP Basic
(client_id:client_secret) or a client_secret body parameter. The
PKCE code_verifier is required for all clients. OAuth errors use the standard
{ "error": "…", "error_description": "…" } shape.
3. Call the API
curl "https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/api-v1/v1/deals" \
-H "Authorization: Bearer kyo_at_…" \
-H "apikey: $KYO_ANON_KEY"
See the REST API reference for everything you can call.
Refreshing tokens
Access tokens live for 1 hour. Use the refresh grant to get a new pair:
curl -X POST "https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "apikey: $KYO_ANON_KEY" \
-d grant_type=refresh_token \
-d refresh_token=kyo_rt_… \
-d client_id=kyoapp_…
- Rotation — every refresh returns a new access + refresh pair and invalidates the old refresh token. Always persist the new one.
- Reuse detection — replaying an already-rotated refresh token is treated as a compromise: the entire token family is revoked and the user must authorize again.
- Scope narrowing — pass
scopeto downgrade; scopes can never be widened on refresh.
Refresh tokens expire after 30 days.
Revoking access
Kyo implements RFC 7009 token revocation:
curl -X POST "https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-revoke" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "apikey: $KYO_ANON_KEY" \
-d token=kyo_rt_… \
-d token_type_hint=refresh_token \
-d client_id=kyoapp_…
Revoking a refresh token revokes its whole family. Users can also revoke any authorized app themselves from Settings → API in Kyo.
Scopes
Request scopes as a space-separated list. Scopes are resource:read /
resource:write; the wildcard resource:* is also accepted. A token
can never exceed what the authorizing user can do in the app.
| Scope | Grants |
|---|---|
deals:read / deals:write | Deals, plus their person and label links |
people:read / people:write | CRM contacts |
companies:read / companies:write | Companies |
tasks:read / tasks:write | Tasks, including deal tasks |
pipelines:read / pipelines:write | Pipelines and pipeline stages |
labels:read / labels:write | Labels |
comments:read / comments:write | Comments on deals and tasks |
spaces:read / spaces:write | Spaces and projects |
activity:read | The activity (audit) feed — read only |
credits:read | Workspace credit balance — read only |
enrich:write | Metered company enrichment |
Token reference
| Token | Prefix | Lifetime |
|---|---|---|
| Authorization code | kyo_ac_ | 60 seconds, single-use |
| Access token | kyo_at_ | 1 hour |
| Refresh token | kyo_rt_ | 30 days, rotates on every refresh |
| Client secret | kyo_sk_ | Until deleted; shown once at creation |
Kyo tokens are opaque strings, not JWTs — don't try to decode them. They're stored hashed at rest on Kyo's side; treat them like passwords on yours.
Redirect URI rules
httpsis required for web apps, and matching is exact — scheme, host, path, and query.- Loopback is allowed for native apps —
http://127.0.0.1andhttp://localhostURIs may use any ephemeral port: the port is ignored at match time (RFC 8252), so registerhttp://127.0.0.1/callbackonce. - No URL fragments; up to 10 URIs per application.