Resumes
All resume routes live under /api/resumes and require auth (@Roles('user', 'admin')), except the public shared-resume read. The authenticated user is taken from your token — you never pass userId yourself; ownership is enforced server-side.
CRUD
Create a resume
POST /api/resumes
{
"title": "Frontend Resume",
"content": "{\"basics\":{\"name\":\"Ada\"},\"work\":[]}",
"isPublic": false
}| Field | Type | Required | Notes |
|---|---|---|---|
title | string | ✅ | Display name |
content | string | ✅ | Serialized JSON of the resume document |
isPublic | boolean | — | Defaults to false |
Returns the created resume (inside the data envelope).
List my resumes
GET /api/resumes/mine — returns the current user’s resumes, with content included. Accepts list query params (pagination/filtering via ResumeListQueryDto).
List all (admin / filtered)
GET /api/resumes — admin-style listing; honours query filters.
Get one
GET /api/resumes/:id — scoped to the requesting user.
Update metadata
PATCH /api/resumes/:id — throttled 60/min
{ "title": "Updated title", "content": "{...}", "isPublic": true, "shareRole": "COMMENTER", "shareId": "abc123" }All fields optional. shareRole is one of VIEWER · COMMENTER · EDITOR.
Duplicate
POST /api/resumes/:id/duplicate — throttled 10/min — deep-copies a resume.
Delete
DELETE /api/resumes/:id
Versions
Each resume keeps a version history.
| Action | Endpoint | Notes |
|---|---|---|
| Create version | POST /api/resumes/:id/versions | throttled 60/min |
| Delete version | DELETE /api/resumes/:id/versions/:versionId |
// POST /api/resumes/:id/versions
{ "content": "{...full serialized resume...}", "changelog": "Reworded summary" }content is required; changelog is an optional note.
Sharing (public short links)
A resume can be shared via a short-link shareId (set through PATCH). The read route is public — no auth needed:
curl http://localhost:3111/api/resumes/shared/abc123Viewers of a shared resume can leave comments:
POST /api/resumes/shared/:shareId/comments (the commenter must be authenticated)
{ "content": "Great experience section!", "position": { "block": "work-0" }, "color": "#ffcc00" }Collaboration
Invite other users with a role:
POST /api/resumes/:id/collaborators
{ "userId": "user_clerk_id", "role": "EDITOR" }role ∈ VIEWER · COMMENTER · EDITOR. Remove with DELETE /api/resumes/:id/collaborators/:userId.
Comments & annotations
| Action | Endpoint |
|---|---|
| Add comment | POST /api/resumes/:id/comments |
| Reply to a comment | POST /api/resumes/:id/comments/:commentId/replies |
| Resolve a comment | PATCH /api/resumes/comments/:commentId/resolve |
| Delete a comment | DELETE /api/resumes/:id/comments/:commentId |
// POST /api/resumes/:id/comments
{ "content": "Quantify this bullet", "position": { "block": "work-1" }, "color": "#4f46e5" }position and color are optional and stored as-is — your UI decides how to anchor and render them.
Comment author is always taken from the token (req.auth().userId); you never send it in the body. The CreateCommentDto whitelist strips anything other than content, position, color, and the optional quoted snippet.