Users & Tokens
The /api/users controller covers three things: user administration, your own Personal Access Tokens, and feedback. All routes are guarded by ClerkAuthGuard; admin-only routes additionally require the admin role.
Personal Access Tokens
These are the endpoints your integration uses to manage its own credentials. They require the user (or admin) role.
| Action | Method & path |
|---|---|
| Create | POST /api/users/me/personal-access-tokens |
| List | GET /api/users/me/personal-access-tokens |
| Revoke | PATCH /api/users/me/personal-access-tokens/:id/revoke |
// POST body
{ "name": "CI pipeline", "expiresAt": "2027-01-01T00:00:00.000Z", "scopes": ["resume:read"] }The full plaintext token is returned only in the create response (data.token). Listing returns tokenPreview (mr_pat_AbCd1234...), lastUsedAt, expiresAt, and revokedAt. See Authentication for the complete flow.
Revoking is immediate and irreversible — a revoked token’s revokedAt is set and it stops authenticating on the next request. Rotate by creating a new token, deploying it, then revoking the old one.
User administration
| Action | Method & path | Role |
|---|---|---|
| List users (paginated, searchable) | GET /api/users?current=1&size=10&q=ada | guarded |
| User detail (rich Clerk data + stats) | GET /api/users/:id | guarded |
| Sync all users from Clerk | POST /api/users/sync | guarded |
| Sync a single user | POST /api/users/sync/:id | guarded |
GET /api/users maps the frontend’s current / size pagination params to limit/offset internally, and q is a free-text search.
GET /api/users/:id is declared last in the controller on purpose, so it doesn’t shadow /api/users/feedback and /api/users/me/*. Keep that in mind if you proxy or mirror these routes.
Feedback
| Action | Method & path | Role |
|---|---|---|
| Submit feedback | POST /api/users/feedback | user / admin, throttled 3/min |
| List all feedback | GET /api/users/feedback?status=open | admin |
| Resolve feedback | PATCH /api/users/feedback/:id/resolve | admin |
// POST /api/users/feedback
{ "content": "The PDF export cut off my last job." }The submitter’s userId is taken from the token when present (falling back to the DTO). Resolving attaches a reply:
// PATCH /api/users/feedback/:id/resolve
{ "replyContent": "Fixed in the latest deploy — thanks!" }