> ## Documentation Index
> Fetch the complete documentation index at: https://docs.launchmystore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Revoke Token

> Invalidate an access token (and its paired refresh token)

# Revoke Token

Revokes an OAuth access token. The endpoint follows RFC 7009 semantics:
revocation is **idempotent** and **silent** — unknown or already-revoked
tokens still return `200 success`. On a successful revoke the server:

1. Locates the app installation that owns this `accessToken`.
2. Blacklists **both** the access token and its paired refresh token
   for 31 days (only a hash of each token is stored, never the raw value).
3. Clears the stored access token, refresh token, and their expiry
   timestamps on the installation.

The installation itself is **not** deleted — `status` remains
`active`. The merchant must call `POST /apps/store/uninstall/:appId` for
a full uninstall.

This endpoint is rate-limited to **5 requests per minute per IP**.

## Request

```bash theme={null}
curl -X POST "https://api.launchmystore.io/apps/oauth/revoke" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "lms_token_9fbb1c3e8d4a7b2e..."
  }'
```

## Body Parameters

<ParamField body="token" type="string" required>
  The access token to revoke. Refresh tokens cannot be revoked
  directly — passing one returns `200` (silent no-op) because no
  installation row matches a refresh token in the `accessToken` lookup.
  To kill a refresh token, revoke its paired access token (which
  blacklists both) or wait 30 days for natural expiry.
</ParamField>

## Response

<ResponseField name="status" type="integer">Always 200.</ResponseField>
<ResponseField name="state" type="string">Always `success`.</ResponseField>

<ResponseField name="message" type="string">
  `Token revoked successfully`. Returned for both genuine revocations and
  unknown tokens (RFC 7009 §2.2 idempotency).
</ResponseField>

## Example Response

```json theme={null}
{
  "status": 200,
  "state": "success",
  "message": "Token revoked successfully"
}
```

## What gets revoked

When the server finds an installation matching the supplied access token,
**both** tokens on that row are blacklisted and cleared in a single
update:

| State                           | Before            | After        |
| ------------------------------- | ----------------- | ------------ |
| Access token                    | `lms_token_...`   | cleared      |
| Refresh token                   | `lms_refresh_...` | cleared      |
| Access-token expiry             | `<24h from now>`  | cleared      |
| Refresh-token expiry            | `<30d from now>`  | cleared      |
| Blacklist entry (access token)  | –                 | set, 31 days |
| Blacklist entry (refresh token) | –                 | set, 31 days |

Subsequent API calls with either token return:

```json theme={null}
{ "status": 401, "state": "error", "message": "Token has been revoked" }
```

## When to revoke

* **User-initiated sign-out** of your app's embedded UI.
* **Compromised token** suspicion — pair with rotating
  `client_secret` via `POST /apps/developer/:appId/regenerate-secret`.
* **Switching environments** between dev/staging/prod for the same app
  installation.

Revoking does **not** uninstall the app, surrender granted scopes, or
notify the merchant. For a full uninstall + webhook fan-out, see
[`POST /apps/store/uninstall/:appId`](/api-reference/apps/installations#uninstall).

## Ending a session (alternatives)

| Operation                 | Endpoint                                                 | Effect                                                                                                                   |
| ------------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Revoke current token only | `POST /apps/oauth/revoke`                                | Both tokens blacklisted; installation kept.                                                                              |
| Rotate refresh token      | `POST /apps/oauth/token` with `grant_type=refresh_token` | Old refresh token auto-blacklisted; new pair issued.                                                                     |
| Uninstall app             | `POST /apps/store/uninstall/:appId`                      | Installation removed (or status → `pending-uninstall`); deployed extension files removed; uninstall webhooks dispatched. |
| Re-authenticate           | `GET /apps/oauth/authorize` → `POST /apps/oauth/token`   | Reuses the same installation; new tokens overwrite old ones.                                                             |

## Error Codes

| HTTP  | Error message                | When                                                                   |
| ----- | ---------------------------- | ---------------------------------------------------------------------- |
| `200` | `Token revoked successfully` | Always returned — including for unknown tokens (RFC 7009 idempotency). |
| `429` | (throttler)                  | More than 5 requests/minute from this IP.                              |
| `500` | (generic)                    | Server-side write failed. The token may or may not be revoked — retry. |

## Security Notes

* Token blacklist entries live for **31 days** — one day longer
  than the longest possible refresh-token lifetime — so a revoked token
  can never come back via a stale cache.
* Only a SHA-256 hash of the token is stored in the blacklist; the raw
  token is never persisted.
* This endpoint has no auth requirement beyond rate limiting — anyone
  holding a valid access token can revoke it. This is intentional: a
  leaked token should be invalidatable from any environment without
  needing the original `client_secret`.
