---
title: "Revoke Endpoint"
method: POST
path: "/v1/auth/oauth/revoke"
tags: ["auth-oauth"]
---

# Revoke Endpoint

`POST /v1/auth/oauth/revoke`

## Request

- Base URL: `https://api.usekeel.io` (the document declares no server; this is the origin it was published from)
- URL: `https://api.usekeel.io/v1/auth/oauth/revoke`
- Auth: none declared

RFC 7009 token revocation — the server half of a client's logout.

Q-1575: until this existed the AS advertised no ``revocation_endpoint``,
so a compliant client could only forget its copy of the token. Measured
on prod 2026-09-19: ``codex mcp logout keel`` cleared the local keychain
entry while the org's ``platform.agent_credentials`` row kept
``revoked_at = NULL`` — a live refresh credential, carrying whatever
scopes it was minted with, on an org whose owner believed they had
disconnected that agent.

Posture, deliberately identical to ``/token``:

* **Public client, no client authentication.** That is what
  ``token_endpoint_auth_methods_supported: ["none"]`` already advertises
  and what every MCP client here is. The bearer of the token is the only
  party who can present it, and presenting it IS the authorization to
  destroy it — revocation only ever removes capability, so an attacker
  who already holds the token gains nothing by calling this.
* **Form-encoded or JSON body** (:func:`_parse_token_body`), and the same
  ``invalid_request`` 401 as the token endpoint for a malformed call, so
  this router has one error posture rather than two.

RFC 7009 §2.2: an unknown, expired, or already-revoked token answers
**200 with an empty body**. The endpoint never discloses whether a token
was ever valid, so it cannot be used as a validity oracle.

A refresh token revokes its **whole rotation chain** (``lineage_id``),
not merely the row presented. Rotation means the token a client holds is
a descendant of the one it first received; killing one link while its
siblings live would leave the grant alive and the user's "disconnect"
still a lie. This is the same lineage burn reuse detection performs in
:func:`_do_refresh`.

Access tokens are stateless 1h JWTs with no row to revoke, so a
``token_type_hint=access_token`` call still answers 200 (RFC 7009 §2.1:
the server extends its search across its supported token types; finding
nothing is not an error). Revoking the refresh chain stops renewal, and
the last access token minted from it expires on its own within the hour.

## Response `200`

Successful Response

- unknown

---

[API](https://skmtc.dev/usekeel/apis/keel-api.md) · [All operations](https://skmtc.dev/usekeel/apis/keel-api/llms.txt) · [OpenAPI document](https://skmtc.dev/usekeel/apis/keel-api/revisions/3191ca10b0ab?raw)
