A site signs one of its people in
const url = 'https://example.com/v1/auth/site-sign-in';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"statement":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://example.com/v1/auth/site-sign-in \ --header 'Content-Type: application/json' \ --data '{ "statement": "example" }'Public: the statement is the credential. A registered site’s statement that a person is signed in there, exchanged for a session limited to the site’s project: it reads nothing above the project, mints no keys, isn’t refreshed, and ends early when the site is paused or unregistered, or one of its keys revoked. The person is the one the site created for that sub (a first statement creates them; never matched by email), with the grants the site’s role map gives their roles. Every refusal is 401 site-sign-in-refused, with details.reason. A statement whose signature wasn’t verified gets not-verified alone, whatever was wrong (the site, the key, the signature, the format), since its sender has proved nothing; after a verified signature: site-paused, site-not-vouching, wrong-audience, expired, issued-in-the-future, too-long-lived, replayed, role-not-allowed, person-removed or people-cap. Rate-limited per client and per site (429 rate-limit-exceeded, with Retry-After). The body is at most 4096 bytes.
Request Bodyrequired
Section titled “Request Bodyrequired”object
The person’s statement, signed by the site: a compact JWS, alg: EdDSA, typ: kindgi-site+jwt, with the site’s kid. Claims: iss (the site id), aud, sub (the person’s stable id on the site), iat, exp (at most 60 s after iat), jti (used once), name, roles, and an optional email for notifications.
Examplegenerated
{ "statement": "example"}Responses
Section titled “Responses”A session for the person.
object
A session token (kgi_sk_…) limited to the site’s project. Shown once; not refreshable.
The person as approvals name them: user:<userId>.
Examplegenerated
{ "token": "example", "expiresAt": "2026-04-15T12:00:00Z", "userId": "example", "actor": "example", "projectId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}A malformed body, or a statement that isn’t a compact JWS.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The statement was refused (site-sign-in-refused, details.reason).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}The body is over 4096 bytes (site-request-too-large).
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}Too many requests from this client or for this site.
object
object
Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).
Optional, kind-specific.
object
Server-assigned request id; also echoed via X-Request-Id header.
Examplegenerated
{ "error": { "code": "example", "message": "example", "details": {}, "requestId": "example" }}