Key Introspection
Look up what a key is allowed to record, without needing dashboard access.
GET /api/v1/keys/self
Returns metadata about the key making the request. Useful for confirming a key works and discovering what it is scoped to record.
Required scope: none — any valid key can introspect itself.
Request
curl https://app.trenchcoat.io/api/v1/keys/self \
-H "X-API-Key: ct_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"Response — 200
{
"data": {
"key_prefix": "ct_live_a1b2",
"name": "My MacBook",
"scopes": ["read:full"],
"rate_limit_tier": "ingestion",
"expires_at": null
},
"meta": {
"requestId": "req_msdf3l4f_lmjnos",
"timestamp": "2026-01-15T10:30:00.000Z"
}
}| Field | Type | Description |
|---|---|---|
key_prefix | string | First 12 characters of the key, matching what the dashboard displays. Enough to identify which key this is. |
name | string | The name you gave the key. |
scopes | string[] | The scopes as stored — see the caveat below. |
rate_limit_tier | string | standard, premium, or ingestion. |
expires_at | string | null | Expiry timestamp, or null if the key does not expire. |
The response never contains the key itself, only its prefix.
Scopes are returned unexpanded
scopes lists what was stored when the key was created, not everything it effectively grants. A full-capture key returns exactly:
{ "scopes": ["read:full"] }even though read:full implies read:prompts, read:events, and read:sessions.
If you are deciding what a key can do, expand the implications first:
| Stored scope | Also grants |
|---|---|
read:full | read:prompts, read:events, read:sessions |
read:prompts | read:sessions |
read:events | read:sessions |
admin | everything |
Reading scopes literally is a real source of bugs. A checker asking "does this key have read:prompts?" gets false for a read:full key, which is exactly backwards — that key has more access, not less.
Errors
401 — Invalid or missing key
Returned when the header is absent, or the key does not exist, is revoked, or has expired. Retrying will not help; issue a new key.
{
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid API key."
}
}Because this endpoint requires no scope, a 401 here means the key itself is bad — which makes it a clean way to distinguish "the key is rejected" from "the key lacks the scope this other endpoint needed."