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.

Dynamic registration

Clients that can't be pre-registered by an admin — MCP connectors and agent tooling, above all — can register themselves with RFC 7591 dynamic client registration. No authentication is needed: registration grants nothing by itself, since the user still approves every scope on the consent screen.

bash
curl -X POST "https://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-register" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My Agent",
    "redirect_uris": ["https://myagent.example/callback"],
    "token_endpoint_auth_method": "none"
  }'
response 201
{
  "client_id": "kyoapp_…",
  "client_id_issued_at": 1771000000,
  "client_name": "My Agent",
  "redirect_uris": ["https://myagent.example/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "deals:read deals:write …"
}
  • Public clients only. token_endpoint_auth_method must be "none" — no client secret is ever issued this way. Confidential apps are registered in Settings → API.
  • redirect_uris is required, max 10, each https or a localhost/127.0.0.1 loopback URL with no fragment.
  • scope is optional. Omit it and the client may request the full catalogue; the user decides what is actually granted.
  • logo_uri and client_uri are shown on the consent screen and must be https.
  • Dynamically registered clients are always unverified, so users see the warning banner. Registration is rate-limited to 20 per hour per IP.

Discovery

Both metadata documents are served from the app origin, so a client can find every endpoint on this page automatically:

discovery
https://app.trykyo.com/.well-known/oauth-authorization-server
https://app.trykyo.com/.well-known/oauth-protected-resource

The first is RFC 8414 authorization-server metadata (authorization, token, registration and revocation endpoints, S256 only, supported scopes). The second is RFC 9728 protected-resource metadata for the hosted MCP server — an unauthenticated call to it also returns a WWW-Authenticate header pointing here, which is how MCP clients start the flow on their own.

Endpoints

EndpointURL
Authorizationhttps://app.trykyo.com/oauth/authorize
Tokenhttps://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-token
Revocationhttps://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-revoke
Registrationhttps://pvozbkuhjofzitsmpspf.supabase.co/functions/v1/oauth-register
Metadatahttps://app.trykyo.com/.well-known/oauth-authorization-server

Token and revocation requests are form-encoded POSTs and also need the public apikey header — the same anon key used for REST API requests. (JSON bodies are accepted too, as a convenience.) Registration takes JSON and needs no headers beyond Content-Type. Only S256 PKCE is supported — plain is rejected.

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

url
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

bash
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
response
{
  "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

bash
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:

bash
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 scope to downgrade; scopes can never be widened on refresh.

Refresh tokens expire after 30 days.

Revoking access

Kyo implements RFC 7009 token revocation:

bash
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 — see permissions. There is no :delete scope: v1 of the API cannot delete anything.

ScopeGrants
deals:read / deals:writeDeals, plus their contact links, label links and file metadata
people:read / people:writeCRM contacts
companies:read / companies:writeCompanies
pipelines:read / pipelines:writePipelines and their stages
tasks:read / tasks:writeTasks and deal tasks, plus task attachment metadata. Private tasks stay hidden per the authorizing user's access
spaces:read / spaces:writeSpaces, projects, project stages, space members and space updates
labels:read / labels:writeLabels
comments:read / comments:writeComments on deals and tasks
metrics:read / metrics:writeMetrics, their sub-pages and logged entries
finance:read / finance:writeIncome, expenses, categories, debts and debt payments
hr:read / hr:writeTeam directory, departments, contracts, change requests, the org chart and time-off requests
docs:read / docs:writeDocuments, knowledge-base files and canvases
automations:read / automations:writeAutomations and their run history (writes also require admin)
competitors:readCompetitor workflows and their reports — read only
agents:readThe workspace's AI agents and their instructions — read only
directory:read / directory:writeWorkspace members and workspace settings. Writes cover profile fields and workspace settings only — never roles, permissions or billing
activity:readThe activity (audit) feed — read only
credits:readWorkspace credit balance — read only
enrich:writeMetered company enrichment

Token reference

TokenPrefixLifetime
Authorization codekyo_ac_60 seconds, single-use
Access tokenkyo_at_1 hour
Refresh tokenkyo_rt_30 days, rotates on every refresh
Client secretkyo_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

  • https is required for web apps, and matching is exact — scheme, host, path, and query.
  • Loopback is allowed for native appshttp://127.0.0.1 and http://localhost URIs may use any ephemeral port: the port is ignored at match time (RFC 8252), so register http://127.0.0.1/callback once.
  • No URL fragments; up to 10 URIs per application.