Base URL: rescanvas.resilientdb.com/ or http://localhost:10010 (for development)
API Version: v1
Authentication: JWT Bearer tokens are required for most endpoints.
ResCanvas API v1 provides a clean, RESTful interface organized around these core concepts:
- Canvas API (
/api/v1/canvases/*) - Canvas management, strokes, and history operations - Collaborations API (
/api/v1/collaborations/*) - Invitations and collaboration management - Notifications API (
/api/v1/notifications/*) - User notifications - Users API (
/api/v1/users/*) - User search and suggestions - Auth API (
/api/v1/auth/*) - Authentication and user management
- Generic terminology: Uses "canvas" instead of frontend-specific terminology
- Consolidated endpoints: Related operations grouped under logical paths
- Proper HTTP methods: Uses appropriate verbs (GET, POST, PATCH, DELETE)
- RESTful structure: Resources are clearly defined and hierarchical
All authentication endpoints are prefixed with /api/v1/auth.
POST /api/v1/auth/register
Create a new user account.
Request Body:
{
"username": "alice",
"password": "securePassword123",
"walletPubKey": "optional_wallet_public_key_for_secure_canvases"
}Validation Rules:
username: 3-128 characters, alphanumeric + underscore, hyphen, dot onlypassword: Minimum 6 characters, maximum 72 bytes (bcrypt limit)walletPubKey: Optional, up to 500 characters
Response (201 Created):
{
"status": "ok",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"username": "alice",
"walletPubKey": "optional_wallet_key"
}
}Error Responses:
400: Validation failed409: Username already taken
POST /api/v1/auth/login
Authenticate and receive access token.
Request Body:
{
"username": "alice",
"password": "securePassword123"
}Response (200 OK):
{
"status": "ok",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"username": "alice",
"walletPubKey": "optional_wallet_key"
}
}Error Responses:
400: Validation failed401: Invalid credentials
GET /api/v1/auth/me
Get information about the currently authenticated user.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"user": {
"username": "alice",
"_id": "user_id_here",
"createdAt": "2024-01-01T00:00:00.000Z"
}
}POST /api/v1/auth/logout
Invalidate the current session.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok"
}All canvas endpoints are prefixed with /api/v1/canvases.
Canvases are collaborative drawing spaces where multiple users can work together.
- public: Visible to all users, anyone can join
- private: Hidden from listings, requires invitation or canvas ID
- secure: Like private, but requires wallet signatures for all strokes
POST /api/v1/canvases
Create a new canvas.
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"name": "My Canvas",
"type": "public",
"description": "Optional canvas description"
}Validation Rules:
name: 1-256 characters, requiredtype: One of ["public", "private", "secure"], requireddescription: Optional, max 500 characters
Response (201 Created):
{
"status": "ok",
"room": {
"id": "canvas_id_here",
"name": "My Canvas",
"type": "public"
}
}Error Responses:
400: Validation failed401: Not authenticated
GET /api/v1/canvases
List canvases accessible to the authenticated user.
Headers:
Authorization: Bearer <access_token>
Query Parameters:
archived: Include archived canvases (0 or 1, default: 0)sort_by: Sort field ("updatedAt", "createdAt", "name", "memberCount", default: "updatedAt")order: Sort order ("asc" or "desc", default: "desc")page: Page number (default: 1)per_page: Items per page (1-500, default: 200)type: Filter by canvas type ("public", "private", "secure")
Response (200 OK):
{
"rooms": [
{
"id": "canvas_id",
"name": "Canvas Name",
"type": "public",
"ownerName": "alice",
"description": "Canvas description",
"archived": false,
"myRole": "owner",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z",
"memberCount": 5
}
],
"total": 1,
"page": 1,
"per_page": 200
}GET /api/v1/canvases/{canvasId}
Get detailed information about a specific canvas.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"room": {
"id": "canvas_id",
"name": "Canvas Name",
"type": "public",
"ownerName": "alice",
"ownerId": "user_id",
"description": "Canvas description",
"archived": false,
"myRole": "editor",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-01T00:00:00.000Z"
}
}Error Responses:
401: Not authenticated403: Access denied404: Canvas not found
PATCH /api/v1/canvases/{canvasId}
Update canvas properties (owner/admin only).
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"name": "Updated Canvas Name",
"description": "Updated description",
"archived": false
}Response (200 OK):
{
"status": "ok",
"room": {
"id": "canvas_id",
"name": "Updated Canvas Name",
"description": "Updated description"
}
}Error Responses:
400: Validation failed401: Not authenticated403: Not owner/admin
DELETE /api/v1/canvases/{canvasId}
Delete a canvas (owner only).
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok"
}Error Responses:
401: Not authenticated403: Not owner404: Canvas not found
POST /api/v1/canvases/{canvasId}/share
Share canvas with specific users.
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"users": [
{
"username": "bob",
"role": "editor"
},
{
"username": "charlie",
"role": "viewer"
}
]
}Roles:
owner: Full control (only one per canvas)admin: Can manage members and settingseditor: Can draw and modify contentviewer: Read-only access
Response (201 Created):
{
"status": "ok",
"shared": 2
}Error Responses:
400: Invalid users or roles401: Not authenticated403: Not owner/admin
GET /api/v1/canvases/{canvasId}/members
Get list of canvas members and their roles.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"members": [
{
"userId": "user_id",
"username": "alice",
"role": "owner",
"joinedAt": "2024-01-01T00:00:00.000Z"
},
{
"userId": "user_id_2",
"username": "bob",
"role": "editor",
"joinedAt": "2024-01-02T00:00:00.000Z"
}
]
}POST /api/v1/canvases/{canvasId}/invite
Invite a user to join the canvas.
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"username": "bob",
"role": "editor"
}Response (201 Created):
{
"status": "ok",
"inviteId": "invite_id_here"
}Error Responses:
400: Invalid username or role401: Not authenticated403: Not owner/admin404: User not found
POST /api/v1/canvases/{canvasId}/transfer
Transfer canvas ownership to another member.
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"username": "bob"
}Response (200 OK):
{
"status": "ok"
}Error Responses:
400: Target user not a member401: Not authenticated403: Not current owner404: Target user not found
POST /api/v1/canvases/{canvasId}/leave
Leave a canvas (non-owners only).
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok"
}Error Responses:
400: Owner cannot leave (must transfer first)401: Not authenticated403: Not a member
GET /api/v1/canvases/{canvasId}/strokes
Retrieve all drawing strokes for a canvas.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"strokes": [
{
"id": "stroke_id",
"userId": "user_id",
"username": "alice",
"points": [[10, 20], [15, 25], [20, 30]],
"color": "#000000",
"width": 2,
"tool": "pen",
"timestamp": "2024-01-01T00:00:00.000Z"
}
]
}POST /api/v1/canvases/{canvasId}/strokes
Submit a new drawing stroke to the canvas.
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"points": [[10, 20], [15, 25], [20, 30]],
"color": "#FF0000",
"width": 3,
"tool": "pen"
}Response (201 Created):
{
"status": "ok",
"strokeId": "stroke_id_here"
}Error Responses:
400: Invalid stroke data401: Not authenticated403: No write access
DELETE /api/v1/canvases/{canvasId}/strokes
Clear all strokes from the canvas.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"cleared": true
}Error Responses:
401: Not authenticated403: No write access
All history operations are grouped under /api/v1/canvases/{canvasId}/history.
POST /api/v1/canvases/{canvasId}/history/undo
Undo the last stroke made by the current user.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"undone": true
}POST /api/v1/canvases/{canvasId}/history/redo
Redo the last undone stroke.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"redone": true
}GET /api/v1/canvases/{canvasId}/history/status
Get the current undo/redo status for the user.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"undo_available": true,
"redo_available": false,
"undo_count": 5,
"redo_count": 0
}POST /api/v1/canvases/{canvasId}/history/reset
Reset the undo/redo stacks for the current user.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"reset": true
}All collaboration endpoints are prefixed with /api/v1/collaborations.
GET /api/v1/collaborations/invitations
List all pending invitations for the current user.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"invitations": [
{
"id": "invite_id",
"canvasId": "canvas_id",
"canvasName": "Shared Canvas",
"inviterName": "alice",
"role": "editor",
"status": "pending",
"createdAt": "2024-01-01T00:00:00.000Z"
}
]
}POST /api/v1/collaborations/invitations/{inviteId}/accept
Accept a pending invitation.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"canvasId": "canvas_id_here"
}POST /api/v1/collaborations/invitations/{inviteId}/decline
Decline a pending invitation.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok"
}GET /api/v1/canvases/suggest
Get canvas suggestions for autocomplete (searches by name).
Headers:
Authorization: Bearer <access_token>
Query Parameters:
q: Search query string
Response (200 OK):
{
"suggestions": [
{
"id": "canvas_id",
"name": "Canvas Name",
"type": "public"
}
]
}All user endpoints are prefixed with /api/v1/users.
GET /api/v1/users/search
Search for users by username.
Headers:
Authorization: Bearer <access_token>
Query Parameters:
q: Search query string
Response (200 OK):
{
"users": [
{
"username": "alice",
"id": "user_id"
}
]
}GET /api/v1/users/suggest
Get user suggestions for autocomplete.
Headers:
Authorization: Bearer <access_token>
Query Parameters:
q: Search query string
Response (200 OK):
{
"suggestions": [
{
"username": "alice",
"id": "user_id"
}
]
}All notification endpoints are prefixed with /api/v1/notifications.
GET /api/v1/notifications
Get all notifications for the current user.
Headers:
Authorization: Bearer <access_token>
Query Parameters:
unreadOnly: Only show unread (true/false, default: false)
Response (200 OK):
{
"notifications": [
{
"id": "notif_id",
"type": "invite",
"message": "You were invited to join 'Canvas Name'",
"link": "/canvases/canvas_id",
"read": false,
"createdAt": "2024-01-01T00:00:00.000Z"
}
]
}PATCH /api/v1/notifications/{notificationId}
Mark a notification as read.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok"
}DELETE /api/v1/notifications/{notificationId}
Delete a notification.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok"
}DELETE /api/v1/notifications
Delete all notifications for the current user.
Headers:
Authorization: Bearer <access_token>
Response (200 OK):
{
"status": "ok",
"deleted": 5
}All API endpoints return consistent error responses:
{
"status": "error",
"message": "Description of what went wrong"
}Common HTTP status codes:
200: Success201: Created400: Bad Request (validation error)401: Unauthorized (authentication required)403: Forbidden (insufficient permissions)404: Not Found409: Conflict (e.g., duplicate username)500: Internal Server Error
ResCanvas uses Socket.IO for real-time collaboration. Connect to the WebSocket server with your JWT token:
import io from 'socket.io-client';
const socket = io('https://rescanvas.resilientdb.com', {
auth: {
token: 'your_jwt_token_here'
}
});
// Join a canvas
socket.emit('join', { canvasId: 'canvas_id_here' });
// Listen for new strokes
socket.on('newStroke', (data) => {
console.log('New stroke:', data);
});Client → Server:
join: Join a canvas for real-time updatesleave: Leave a canvas
Server → Client:
newStroke: New stroke added to canvascanvasCleared: Canvas was clearedmemberJoined: New member joinedmemberLeft: Member left
For issues or questions:
- Documentation: https://github.com/resilientdb/ResCanvas/blob/main/README.md
- Raise an issue: https://github.com/resilientdb/ResCanvas