# GET /api/v1/oauth/grants

Auth: Bearer

HTML: https://cooperemail.com/docs/reference/oauth-grants

## Summary

List OAuth apps that currently hold access on this account. One row per client with at least one usable token. An expired access token is included while its refresh token can still be used. Each row is object oauth_grant with id, client_id, client_name (empty when the client row is missing), redirect_hosts (hostnames only), scope, resource, connected_at, last_refreshed_at, and active_tokens. Newest last_refreshed_at first. Token hashes and client secrets are not returned. Requires key_read. An inbox-scoped key receives 403 permission_denied. A sub-account sees only its own grants. MCP: cooper_list_oauth_grants.

## Request

This call has no JSON body.

## Response

HTTP 200. Sample values illustrate the schema.

```json
{
  "object": "list",
  "data": [
    {
      "object": "oauth_grant",
      "id": "inb_example",
      "client_id": "inb_example",
      "client_name": "string",
      "redirect_hosts": [
        "string"
      ],
      "scope": "string",
      "resource": "string",
      "connected_at": "2026-10-18T12:00:00.000Z",
      "last_refreshed_at": "2026-10-18T12:00:00.000Z",
      "active_tokens": 1
    }
  ]
}
```

## Errors

- HTTP 403 permission_denied

The live `message` is the sentence for that code. Branch on `code`.

```json
{
  "error": {
    "type": "authentication_error",
    "code": "permission_denied",
    "docs_url": "https://cooperemail.com/docs#permission_denied"
  }
}
```

## Idempotency

client_id makes this write idempotent. Sending the same client_id again returns the original object instead of creating a duplicate.
