platform.api_tokens
Personal Access Tokens (PATs) for programmatic API access — CLIs, scripts, agents, and integrations. The plaintext token (pharus_pat_<32 base62 chars>, 43 chars) is shown to the user once at creation; the backend stores only the SHA-256 hash, never the secret.
Columns
| Column | Type | Notes |
|---|---|---|
id | UUID PK | Default gen_random_uuid(). |
user_id | UUID FK | → platform.users(id) ON DELETE CASCADE. The token's owner. |
org_id | UUID FK | → platform.organizations(id) ON DELETE CASCADE. The org the token is bound to. |
name | VARCHAR(255) | NOT NULL. User-supplied label, e.g. "CI deploy". |
token_prefix | VARCHAR(20) | NOT NULL. Display-safe first 12 chars (e.g. pharus_pat_a) so lists can show the token without the secret. |
token_hash | TEXT | NOT NULL, UNIQUE. SHA-256 of the plaintext. The plaintext itself is never stored. |
last_used_at | TIMESTAMPTZ | NULL until first use. Bumped (fire-and-forget) on every authenticated PAT request. |
expires_at | TIMESTAMPTZ | NULL = never expires. |
revoked_at | TIMESTAMPTZ | NULL = active. Set on revoke — a soft delete; revoked tokens are kept but never authenticate. |
created_at | TIMESTAMPTZ | NOT NULL. Default NOW(). |
updated_at | TIMESTAMPTZ | NOT NULL. Default NOW(). |
Indexes: api_tokens_user_idx (user_id), api_tokens_org_idx (org_id).
Org scoping
org_id is nullable at the schema level, but PATs are org-scoped: the create endpoint (E005) requires it and verifies the caller's membership. A token carries its org, so requests authenticated with a PAT don't send X-Org-Id — the bound org wins.