Complete REST API reference for CollabDocs. Swagger/OpenAPI documentation is available at GET /api/swagger when running the server.
All protected endpoints require one of:
-
Access Token (recommended)
Authorization: Bearer <accessToken>- Short-lived (15 minutes)
- Stored in memory on client (XSS-safe)
- Required for API calls
-
Refresh Token (automatic)
Cookie: refreshToken=<token>- Long-lived (7 days)
- HttpOnly cookie (cannot be read by JavaScript)
- Automatically sent with requests
- Used to obtain new access tokens
401 Unauthorized— Missing or invalid token403 Forbidden— Valid token but insufficient permissions429 Too Many Requests— Rate limited
Create a new user account with email/password.
Request Body:
{
"email": "user@example.com",
"password": "SecurePassword123",
"displayName": "John Doe"
}Response: 201 Created
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com",
"displayName": "John Doe",
"emailVerified": false
}
}Error Responses:
400— Missing fields or password < 8 characters409— Email already in use500— Server error
Headers Set:
Set-Cookie: refreshToken=...; HttpOnly; Secure; SameSite=Strict
Authenticate with email and password.
Request Body:
{
"email": "user@example.com",
"password": "SecurePassword123"
}Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com",
"displayName": "John Doe",
"emailVerified": true,
"avatarUrl": null
}
}Error Responses:
401— Invalid credentials429— Too many login attempts (rate limited: 5/15min per IP)
Obtain a new access token using refresh token from cookie.
Request: No body required. Refresh token sent via cookie automatically.
Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Error Responses:
401— Missing or invalid refresh token401— Token version mismatch (user logged out elsewhere)
Logout current session and invalidate refresh tokens.
Request: No body required.
Response: 200 OK
{
"message": "Logged out"
}Headers Set:
Set-Cookie: refreshToken=; Max-Age=0; HttpOnly; Secure
Get current authenticated user's profile.
Authentication Required: Yes (Bearer token)
Response: 200 OK
{
"id": "507f1f77bcf86cd799439011",
"email": "user@example.com",
"displayName": "John Doe",
"avatarUrl": null,
"emailVerified": true
}Error Responses:
401— Missing or invalid token404— User not found (token is for deleted user)
Verify user's email address via link sent to their inbox.
Parameters:
token— Verification token from email link
Response: 302 Redirect
- Success:
${CLIENT_URL}/login?verified=1 - Failure:
${CLIENT_URL}/login?error=invalid-verification-token
Resend email verification link to unverified user.
Authentication Required: Yes (Bearer token)
Response: 200 OK
{
"message": "If your email is unverified, a new link has been sent."
}Redirect to Google OAuth consent screen.
Redirects to: Google login page
Google OAuth callback (handled by Passport.js).
Returns: 302 Redirect to ${CLIENT_URL}/auth/callback
Sets refresh token cookie on success.
Create a new document.
Authentication Required: Yes
Request Body:
{
"title": "My Document" // optional, defaults to "Untitled"
}Response: 201 Created
{
"_id": "507f1f77bcf86cd799439012",
"title": "My Document",
"ownerId": "507f1f77bcf86cd799439011",
"collaborators": [],
"createdAt": "2025-04-30T10:00:00Z",
"updatedAt": "2025-04-30T10:00:00Z"
}List all documents user has access to (owned or collaborated on).
Authentication Required: Yes
Response: 200 OK
[
{
"_id": "507f1f77bcf86cd799439012",
"title": "My Document",
"ownerId": "507f1f77bcf86cd799439011",
"collaborators": [
{
"userId": "507f1f77bcf86cd799439013",
"permission": "edit"
}
],
"updatedAt": "2025-04-30T10:00:00Z"
}
]Note: yjsState (binary CRDT state) is excluded from list responses for performance.
Get single document with full content.
Authentication Required: Yes
Response: 200 OK
{
"document": {
"_id": "507f1f77bcf86cd799439012",
"title": "My Document",
"ownerId": "507f1f77bcf86cd799439011",
"yjsState": "<binary Buffer>", // CRDT state for editor
"collaborators": []
},
"permission": "owner" // owner | edit | view
}Error Responses:
404— Document not found403— User doesn't have access to this document
Update document title.
Authentication Required: Yes (require edit permission)
Request Body:
{
"title": "Updated Title"
}Response: 200 OK
{
"_id": "507f1f77bcf86cd799439012",
"title": "Updated Title",
"updatedAt": "2025-04-30T10:30:00Z"
}Soft delete document (move to trash). Only owner can delete.
Authentication Required: Yes
Response: 200 OK
{
"message": "Document moved to trash"
}Auto-purged after 7 days.
List deleted documents (for current user, who is the owner).
Authentication Required: Yes
Response: 200 OK
[
{
"_id": "507f1f77bcf86cd799439012",
"title": "Deleted Doc",
"deletedAt": "2025-04-30T11:00:00Z"
}
]Restore document from trash.
Authentication Required: Yes (owner only)
Response: 200 OK
{
"message": "Document restored",
"document": { /* ... */ }
}Permanently delete document from trash (cannot be undone).
Authentication Required: Yes (owner only)
Response: 200 OK
{
"message": "Document permanently deleted"
}Create or update share link for document.
Authentication Required: Yes (owner only)
Request Body:
{
"permission": "view", // view | edit
"disable": false
}Response: 200 OK
{
"shareLink": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"shareLinkPermission": "view",
"shareUrl": "http://localhost:3000/doc/507f1f77bcf86cd799439012?share=a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}Disable sharing:
{
"disable": true
}Add or update collaborator permissions.
Authentication Required: Yes (owner only)
Request Body:
{
"userId": "507f1f77bcf86cd799439013",
"permission": "edit" // view | edit | comment
}Response: 200 OK
[
{
"userId": "507f1f77bcf86cd799439013",
"permission": "edit"
}
]Create a comment on a document.
Authentication Required: Yes
Request Body:
{
"documentId": "507f1f77bcf86cd799439012",
"anchorText": "highlighted text",
"body": "This needs revision",
"parentId": null // optional, for replies
}Response: 201 Created
{
"_id": "507f1f77bcf86cd799439014",
"documentId": "507f1f77bcf86cd799439012",
"authorId": "507f1f77bcf86cd799439011",
"anchorText": "highlighted text",
"body": "This needs revision",
"resolved": false,
"createdAt": "2025-04-30T10:00:00Z"
}Get all comments for a document.
Authentication Required: Yes
Response: 200 OK
[
{
"_id": "507f1f77bcf86cd799439014",
"documentId": "507f1f77bcf86cd799439012",
"authorId": {
"displayName": "John Doe",
"avatarUrl": null
},
"body": "This needs revision",
"resolved": false
}
]Mark comment as resolved or reopen it.
Authentication Required: Yes
Request Body:
{
"resolved": true // toggle resolved status
}Response: 200 OK
{
"_id": "507f1f77bcf86cd799439014",
"resolved": true,
"updatedAt": "2025-04-30T10:30:00Z"
}Delete a comment (comment author only).
Authentication Required: Yes
Response: 200 OK
{
"message": "Deleted"
}Error Responses:
403— Not the comment author
Real-time collaboration happens via WebSocket. Connect with:
POST /socket.io/?EIO=4&transport=websocket
Authorization: Bearer <accessToken>
Client → Server:
doc:join {docId}— Join a document roomyjs:update {data}— Send Y.js CRDT delta
Server → Client:
yjs:sync {data}— Full Y.js document state (on join)yjs:update {data}— Y.js delta from peersdoc:saved— Document persisted to DBdoc:awareness {data}— User presence/cursor updates
Endpoints are rate limited to prevent abuse:
| Endpoint | Limit | Window |
|---|---|---|
POST /api/auth/signup |
10 requests | 1 hour |
POST /api/auth/login |
5 requests | 15 minutes |
POST /api/auth/resend-verification |
3 requests | 1 hour |
POST /api/ai/* |
30 requests | 1 minute |
Exceeding limits returns 429 Too Many Requests.
All errors follow this format:
{
"error": "Description of what went wrong"
}Common HTTP status codes:
400— Bad Request (validation error)401— Unauthorized (missing/invalid auth)403— Forbidden (insufficient permissions)404— Not Found409— Conflict (duplicate email, etc)429— Too Many Requests (rate limited)500— Internal Server Error
CORS is enabled for the configured client URL (via CLIENT_URL env var).
Include credentials in cross-origin requests:
fetch('/api/auth/me', {
credentials: 'include' // Include cookies
})GET /health
{
"status": "ok",
"timestamp": "2025-04-30T10:00:00.000Z"
}No authentication required. Use to verify server is running.
For local development and testing, you can:
- Get Swagger UI:
http://localhost:4000/api/swagger - Download OpenAPI spec:
http://localhost:4000/api/swagger/swagger.json - Use example curl commands:
# Signup
curl -X POST http://localhost:4000/api/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "test@example.com",
"password": "TestPass123",
"displayName": "Test User"
}'
# Login
curl -X POST http://localhost:4000/api/auth/login \
-H "Content-Type: application/json" \
-c cookies.txt \
-d '{
"email": "test@example.com",
"password": "TestPass123"
}'
# Get current user (with token)
curl -X GET http://localhost:4000/api/auth/me \
-H "Authorization: Bearer <accessToken>"
# List documents
curl -X GET http://localhost:4000/api/docs \
-H "Authorization: Bearer <accessToken>"- Ensure access token is valid and not expired (15 min TTL)
- Check
Authorizationheader format:Bearer <token> - Verify refresh token in cookie if using token refresh
- Verify you're the owner (for delete, share operations)
- Check collaborator permissions (view/edit/comment)
- Wait for rate limit window to reset
- Check error response headers for
Retry-After
- Ensure valid JWT access token in connection
- Verify
NEXT_PUBLIC_SOCKET_URLin client env vars - Check CORS configuration
Last updated: 2025-04-30
For questions or contributions, see CONTRIBUTING.md