InfoLang Inc. Keys API
API key lifecycle. Session-only. Secrets are returned exactly once, at mint time, and never again.
API key lifecycle. Session-only. Secrets are returned exactly once, at mint time, and never again.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/infolang-ai:infolang-ai-keys-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: InfoLang Keys API
version: 2.0.0
description: 'The InfoLang gateway. `/v2` is the console + runtime API; the raw Kairos runtime surface stays proxied at `/v1` and `/mcp`, and **Canon** — the coordination layer — at `/coord/v1` where the deployment serves it (`capabilities.canon` on `GET /v2/me`). ## Three credential lanes
Every `/v2` request carries a bearer token, and its prefix decides the lane:
| Lane | Credential | For |
|---|---|---|
| **session** (`sessionAuth`) | Supabase session JWT | the website / console |
| **oauth** (`oauthTokenAuth`) | `il_oauth_…` | a HUMAN who connected an MCP client (Claude, an IDE) |
| **key** (`apiKeyAuth`) | `il_live_…` / `il_test_…` | MACHINES — SDKs, servers, CI |
The gateway verifies only the session lane, minting a separate Kairos runtime token for upstream calls; the two `il_*` credentials are classified and forwarded verbatim, and Kairos verifies them and enforces the credential↔workspace binding. ## Which lanes a route accepts
The general rule: **anything scoped to a workspace accepts all three lanes; the routes with no workspace in the path are session-only.** Concretely —
- **All three lanes:** the runtime ops (`Runtime`), memories and namespaces (`Memories`), `GET /v2/whoami`, the read-only `Usage` and `Activity` views, and — since credential scoping — every `/v2/workspaces/{id}/…` route: keys, members, workspace CRUD, billing, ingest, webhooks, plus `GET /v2/me`. - **Session-only:** `POST /v2/bootstrap`, the workspace collection (`GET`/`POST /v2/workspaces`), the principal-keyed `/v2/billing` routes, and the console''s playground. These answer 403 `lane_not_supported` to an `il_*` credential: with no workspace in the path there is nothing for the gateway to bind the credential to. Each route''s `security` block is authoritative. **Accepting a lane is not granting access** — an `il_*` credential also has to carry the route''s scope (below). ## Scopes
A credential may carry LESS authority than the principal behind it. Scopes are how you say so, and they are written `resource:action`:
- `write` on a resource implies `read` on the same resource — `memories:write` covers recall. `workspace:delete` implies nothing else. - The resources are `memories`, `coord`, `compute`, `ingest`, `usage`, `activity`, `keys`, `members`, `workspace`, `billing` and `webhooks`. `compute:read` (encode, similarity) is stateless — it touches no stored data. `coord:read`/`coord:write` cover **Canon**, the coordination layer, and are enforced by Kairos on `/coord/v1/*` rather than by the gateway. - Wildcards (`*`, `:*`, `*:read`) are accepted **when creating** a credential and are resolved right then, into the concrete set. A wildcard will NOT silently widen to cover resources InfoLang adds later; to pick up a new resource, create a new credential. - A credential can be restricted to specific workspaces and given `not_before` and/or `expires_at`. Outside `[not_before, expires_at)` it is rejected with 401 `credential_expired`. - A credential can also be restricted to specific **namespaces** — the resource axis below workspace. See below; it has consequences worth reading before you grant one. - A credential minted before scoping shipped carries no scope record and resolves to a fixed legacy default, so nothing broke when scopes arrived. Each scoped route says below whether that default covers it. ### Two ceilings
A request is allowed only if **both** hold:
1. the credential''s scopes permit the operation, **and**
2. the role of the principal behind the credential permits it on that workspace (Owner > Admin > Member > ReadOnly). **Neither ceiling can raise the other.** A key carrying `keys:write` held by a principal whose role is ReadOnly cannot manage keys; equally, an Owner presenting a key scoped only to `memories:read` cannot manage keys with it.'
servers:
- url: https://api.infolang.ai
description: Production
tags:
- name: Keys
description: API key lifecycle. Session-only. Secrets are returned exactly once, at mint time, and never again.
paths:
/v2/workspaces/{id}/keys:
get:
tags:
- Keys
summary: List API keys
description: 'Key metadata only (prefix, label, last_used, and the scope record when the key has one) — never secrets. A key with no `scopes` field resolves to `LEGACY_DEFAULT`.
Requires the `keys:read` scope. Accepts all three lanes: a session JWT (bounded by the caller''s role on this workspace) or an `il_*` machine credential whose own scope set, workspace set and validity window cover it — both ceilings apply and neither can raise the other, so a credential carrying `keys:read` is still refused where the role does not permit it.
A credential with no scope record resolves to `LEGACY_DEFAULT`, which does NOT include `keys:read` — mint a key with it explicitly (`POST /v2/workspaces/{id}/keys`).'
security:
- sessionAuth: []
- apiKeyAuth: []
- oauthTokenAuth: []
parameters:
- schema:
type: string
required: true
name: id
in: path
responses:
'200':
description: Keys in the workspace
content:
application/json:
schema:
type: object
properties:
keys:
type: array
items:
type: object
properties:
key_id:
type: string
env:
type: string
enum:
- live
- test
prefix:
type: string
label:
type: string
nullable: true
created:
type: string
last_used:
type: string
nullable: true
secret:
type: string
scopes:
type: array
items:
type: string
scoped_workspace_ids:
type: array
items:
type: string
scoped_namespaces:
type: array
items:
type: string
expires_at:
type: string
nullable: true
not_before:
type: string
nullable: true
principal:
type: string
principal_kind:
type: string
enum:
- user
- service
required:
- key_id
- env
- prefix
- created
required:
- keys
'401':
description: Missing/invalid credential, or `credential_expired` — the credential is outside its `[not_before, expires_at)` window and must be replaced
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
'403':
description: '`insufficient_scope` — the credential''s own scope set, or the caller''s role on this workspace, does not cover the scope this route requires (the missing scope is named in the message). Or `forbidden` — the credential is not scoped to this workspace, or holds no grant on it'
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
operationId: getV2WorkspacesByIdKeys
x-operation-id-source: derived
post:
tags:
- Keys
summary: Mint an API key
description: 'The key `secret` is returned once, in this response only — it cannot be retrieved again.
ATTENUATION: the optional `scopes`, `scoped_workspace_ids`, `scoped_namespaces` and `expires_at` must each be within the MINTING credential''s own effective authority (scopes ⊆, workspaces ⊆, namespaces ⊆, expiry ≤), and within the minter''s role permissions. A credential can never mint a stronger credential, so a machine key that can mint keys is safe: it can only ever mint weaker ones. Wildcards (`*`, `memories:*`, `*:read`) are accepted and stored FROZEN — the literal plus its expansion as of today — so a stored `*` is never widened by a resource added later; re-mint to pick new resources up.
Omitting every scoping field reproduces the pre-scoping behavior: on the session lane no scope record is written and the key resolves to `LEGACY_DEFAULT`; on the machine lanes the new key INHERITS the minter''s own authority (never `LEGACY_DEFAULT`, which could be broader).
`scoped_namespaces` restricts the key to a set of namespaces (`null` or omitted inherits the minter''s, which for an unrestricted minter is unrestricted; an empty array is REJECTED rather than read as unrestricted). An unrestricted minter may grant any set; a restricted one may only narrow. A namespace-restricted key is **/v2-only** — the `/v1/*` and `/mcp` passthrough denies it with 403, because the gateway does not parse those bodies and Kairos has no per-credential namespace scope, so the restriction would be bypassable by changing the URL prefix.
Requires the `keys:write` scope. Accepts all three lanes: a session JWT (bounded by the caller''s role on this workspace) or an `il_*` machine credential whose own scope set, workspace set and validity window cover it — both ceilings apply and neither can raise the other, so a credential carrying `keys:write` is still refused where the role does not permit it.
A credential with no scope record resolves to `LEGACY_DEFAULT`, which does NOT include `keys:write` — mint a key with it explicitly (`POST /v2/workspaces/{id}/keys`).'
security:
- sessionAuth: []
- apiKeyAuth: []
- oauthTokenAuth: []
parameters:
- schema:
type: string
required: true
name: id
in: path
requestBody:
description: Key to mint
content:
application/json:
schema:
type: object
properties:
env:
type: string
enum:
- live
- test
label:
type: string
scopes:
type: array
items:
type: string
minLength: 1
scoped_workspace_ids:
type: array
items:
type: string
minLength: 1
scoped_namespaces:
type: array
nullable: true
items:
type: string
minLength: 1
minItems: 1
expires_at:
type: string
nullable: true
format: date-time
not_before:
type: string
nullable: true
format: date-time
required:
- env
responses:
'201':
description: Minted key, including the one-time secret
content:
application/json:
schema:
type: object
properties:
key_id:
type: string
env:
type: string
enum:
- live
- test
prefix:
type: string
label:
type: string
nullable: true
created:
type: string
last_used:
type: string
nullable: true
secret:
type: string
scopes:
type: array
items:
type: string
scoped_workspace_ids:
type: array
items:
type: string
scoped_namespaces:
type: array
items:
type: string
expires_at:
type: string
nullable: true
not_before:
type: string
nullable: true
principal:
type: string
principal_kind:
type: string
enum:
- user
- service
required:
- key_id
- env
- prefix
- created
'401':
description: Missing/invalid credential, or `credential_expired` — the credential is outside its `[not_before, expires_at)` window and must be replaced
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
'403':
description: '`insufficient_scope` — the credential''s own scope set, or the caller''s role on this workspace, does not cover the scope this route requires (the missing scope is named in the message). Or `forbidden` — the credential is not scoped to this workspace, or holds no grant on it'
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
'422':
description: Invalid body, or an unknown scope not in the vocabulary
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
operationId: postV2WorkspacesByIdKeys
x-operation-id-source: derived
/v2/workspaces/{id}/keys/{keyId}:
delete:
tags:
- Keys
summary: Revoke an API key
description: '404 if the key is not in this workspace.
Requires the `keys:write` scope. Accepts all three lanes: a session JWT (bounded by the caller''s role on this workspace) or an `il_*` machine credential whose own scope set, workspace set and validity window cover it — both ceilings apply and neither can raise the other, so a credential carrying `keys:write` is still refused where the role does not permit it.
A credential with no scope record resolves to `LEGACY_DEFAULT`, which does NOT include `keys:write` — mint a key with it explicitly (`POST /v2/workspaces/{id}/keys`).'
security:
- sessionAuth: []
- apiKeyAuth: []
- oauthTokenAuth: []
parameters:
- schema:
type: string
required: true
name: id
in: path
- schema:
type: string
required: true
name: keyId
in: path
responses:
'200':
description: Revocation result
content:
application/json:
schema:
type: object
properties:
revoked:
type: boolean
required:
- revoked
'401':
description: Missing/invalid credential, or `credential_expired` — the credential is outside its `[not_before, expires_at)` window and must be replaced
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
'403':
description: '`insufficient_scope` — the credential''s own scope set, or the caller''s role on this workspace, does not cover the scope this route requires (the missing scope is named in the message). Or `forbidden` — the credential is not scoped to this workspace, or holds no grant on it'
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
'404':
description: Key not found in this workspace
content:
application/json:
schema:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
required:
- code
- message
required:
- error
operationId: deleteV2WorkspacesByIdKeysByKeyId
x-operation-id-source: derived
components:
securitySchemes:
sessionAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: '**Use this when the caller is a signed-in person in the InfoLang console (the website).** Not for anything you deploy: a session token belongs to a browser session, cannot be minted programmatically, and expires with the session. For a server or CI job use `apiKeyAuth`; for an end user connecting a tool to their own workspace use `oauthTokenAuth`.
Supabase session JWT — the website lane. Accepted on EVERY /v2 route, and the ROOT of authority: it carries no scope ceiling of its own (the caller''s role on each workspace is the only limit) and it is what mints every attenuated machine credential. The gateway verifies the session and mints a separate Kairos runtime token for upstream calls — session JWTs are never forwarded to Kairos.'
apiKeyAuth:
type: http
scheme: bearer
description: '**Use this for machines — SDKs, your backend, cron jobs, CI.** Create one per workspace in the console, restricted to the scopes that job actually needs (and, if you want, to specific workspaces and a validity window). `il_test_` and `il_live_` are separate environments. This is the credential to reach for whenever there is no human in the loop; if a human IS in the loop and connecting their own tool, use `oauthTokenAuth` instead so the action is attributed to them.
InfoLang API key (il_live_… / il_test_…) — the machine lane, for SDKs, servers and CI. Used by the /v1 and /mcp passthrough surface and by the /v2 routes it carries the scope for; verified by Kairos, not the gateway. Authority is carried by the key — an explicit `(scopes × workspaces × TTL)` policy set at mint time and never wider than the credential that minted it. A key minted before scoping carries no policy and resolves to the named `LEGACY_DEFAULT` set (`memories:*`, `compute:read`, `usage:read`, `activity:read`, `ingest:write`, all workspaces its principal holds).'
oauthTokenAuth:
type: http
scheme: bearer
description: '**Use this when a PERSON is connecting a tool to their own workspace** — an MCP client, an IDE, a third-party app acting on their behalf. The token is issued to that person, so what it can do is bounded by their role on the workspace, and revoking the connection revokes the access. Do not ask an end user to paste an API key into a tool to achieve this; that hands the tool a machine credential and attributes its writes to whoever created the key.
OAuth access token (il_oauth_…) — the human lane: a token issued to a PERSON who connected an MCP client (Claude, an IDE) rather than to a machine. Opaque; verified by Kairos, not the gateway. Issuance (authorization server, consent, PKCE, DCR) is a separate build — this documents the credential''s acceptance on the routes that already take it.'