openapi: 3.0.3 info: title: Excalidraw FULL API description: API for the visual workspace platform version: 1.0.0 servers: - url: http://localhost:3000 paths: /health: get: operationId: getHealth summary: Health check responses: '200': { description: OK, content: { application/json: { schema: { $ref: '#/components/schemas/HealthResponse' } } } } '503': { description: Unhealthy, content: { application/json: { schema: { $ref: '#/components/schemas/HealthResponse' } } } } /api/auth/signup: post: operationId: signup requestBody: required: true content: application/json: schema: type: object required: [name, email, password] properties: name: { type: string } email: { type: string, format: email } password: { type: string, minLength: 6 } responses: '201': description: Created content: application/json: schema: type: object properties: user: { $ref: '#/components/schemas/User' } session: { $ref: '#/components/schemas/Session' } '429': { description: Rate limited } /api/auth/login: post: operationId: login requestBody: required: true content: application/json: schema: type: object required: [email, password] properties: email: { type: string, format: email } password: { type: string } responses: '200': description: OK content: application/json: schema: type: object properties: user: { $ref: '#/components/schemas/User' } session: { $ref: '#/components/schemas/Session' } '401': { description: Invalid credentials } /api/auth/logout: post: operationId: logout responses: '200': { description: Logged out } /api/auth/me: get: operationId: getMe security: [ { cookieAuth: [] } ] responses: '200': description: Current user content: application/json: schema: { $ref: '#/components/schemas/User' } '401': { description: Not authenticated } /api/teams: get: operationId: listTeams security: [ { cookieAuth: [] } ] responses: '200': description: List of teams content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Team' } } post: operationId: createTeam security: [ { cookieAuth: [] } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateTeamRequest' } responses: '201': description: Created content: application/json: schema: { $ref: '#/components/schemas/Team' } /api/teams/{teamID}/members: get: operationId: listTeamMembers security: [ { cookieAuth: [] } ] parameters: [ { name: teamID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Members content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/TeamMember' } } /api/teams/{teamID}/invites: get: operationId: listTeamInvites security: [ { cookieAuth: [] } ] parameters: [ { name: teamID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Invites content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/TeamInvite' } } post: operationId: createTeamInvite security: [ { cookieAuth: [] } ] parameters: [ { name: teamID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateInviteRequest' } responses: '201': description: Created content: application/json: schema: type: object properties: invite: { $ref: '#/components/schemas/TeamInvite' } token: { type: string } /api/invites/accept: post: operationId: acceptInvite security: [ { cookieAuth: [] } ] requestBody: required: true content: application/json: schema: type: object required: [token] properties: token: { type: string } responses: '200': description: Membership created content: application/json: schema: { $ref: '#/components/schemas/TeamMember' } /api/drawings: get: operationId: listDrawings security: [ { cookieAuth: [] } ] parameters: - { name: team_id, in: query, schema: { type: string } } - { name: folder_id, in: query, schema: { type: string } } responses: '200': description: Drawings content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Drawing' } } post: operationId: createDrawing security: [ { cookieAuth: [] } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateDrawingRequest' } responses: '201': description: Created content: application/json: schema: type: object properties: id: { type: string } /api/drawings/{drawingID}: get: operationId: getDrawing security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Drawing content: application/json: schema: { $ref: '#/components/schemas/Drawing' } '404': { description: Not found } patch: operationId: updateDrawing security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/UpdateDrawingRequest' } responses: '200': description: Updated content: application/json: schema: type: object properties: updated_at: { type: string, format: date-time } '404': { description: Not found } delete: operationId: archiveDrawing security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '204': { description: Archived } /api/drawings/{drawingID}/revisions: get: operationId: listRevisions security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Revisions content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/DrawingRevision' } } post: operationId: createRevision security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: type: object required: [change_summary, snapshot] properties: change_summary: { type: string, maxLength: 240 } snapshot: { type: string } responses: '201': description: Created content: application/json: schema: { $ref: '#/components/schemas/DrawingRevision' } /api/drawings/{drawingID}/permissions: get: operationId: listPermissions security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Grants content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/PermissionGrant' } } post: operationId: createPermission security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreatePermissionGrantRequest' } responses: '201': description: Created content: application/json: schema: { $ref: '#/components/schemas/PermissionGrant' } /api/drawings/{drawingID}/share-links: get: operationId: listShareLinks security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Links content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/ShareLink' } } post: operationId: createShareLink security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateShareLinkRequest' } responses: '201': description: Created content: application/json: schema: type: object properties: share_link: { $ref: '#/components/schemas/ShareLink' } token: { type: string } /api/drawings/{drawingID}/assets: get: operationId: listAssets security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Assets content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Asset' } } post: operationId: createAsset security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateAssetRequest' } responses: '201': description: Created content: application/json: schema: { $ref: '#/components/schemas/Asset' } /api/drawings/{drawingID}/embeds: get: operationId: listEmbeds security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Embeds content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Embed' } } post: operationId: createEmbed security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateEmbedRequest' } responses: '201': description: Created content: application/json: schema: { $ref: '#/components/schemas/Embed' } /api/drawings/{drawingID}/links: get: operationId: listLinks security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] responses: '200': description: Links content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/LinkReference' } } post: operationId: createLink security: [ { cookieAuth: [] } ] parameters: [ { name: drawingID, in: path, required: true, schema: { type: string } } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateLinkRequest' } responses: '201': description: Created content: application/json: schema: { $ref: '#/components/schemas/LinkReference' } /api/shared/{token}: get: operationId: getSharedResource parameters: [ { name: token, in: path, required: true, schema: { type: string } } ] responses: '200': description: Shared drawing content: application/json: schema: { $ref: '#/components/schemas/SharedResource' } '404': { description: Invalid or expired token } /api/search: get: operationId: searchDrawings security: [ { cookieAuth: [] } ] parameters: [ { name: q, in: query, required: true, schema: { type: string } } ] responses: '200': description: Results content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Drawing' } } /api/templates: get: operationId: listTemplates security: [ { cookieAuth: [] } ] responses: '200': description: Templates content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Template' } } /api/activity: get: operationId: listActivity security: [ { cookieAuth: [] } ] responses: '200': description: Activity content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Activity' } } /api/stats: get: operationId: getStats security: [ { cookieAuth: [] } ] responses: '200': description: Stats content: application/json: schema: { $ref: '#/components/schemas/WorkspaceStats' } /api/folders: get: operationId: listFolders security: [ { cookieAuth: [] } ] responses: '200': description: Folders content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Folder' } } post: operationId: createFolder security: [ { cookieAuth: [] } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateFolderRequest' } responses: '201': description: Created content: application/json: schema: type: object properties: id: { type: string } /api/projects: get: operationId: listProjects security: [ { cookieAuth: [] } ] responses: '200': description: Projects content: application/json: schema: { type: array, items: { $ref: '#/components/schemas/Project' } } post: operationId: createProject security: [ { cookieAuth: [] } ] requestBody: required: true content: application/json: schema: { $ref: '#/components/schemas/CreateProjectRequest' } responses: '201': description: Created content: application/json: schema: type: object properties: id: { type: string } components: securitySchemes: cookieAuth: type: apiKey in: cookie name: excalidraw_session schemas: HealthResponse: type: object required: [status] properties: status: { type: string, enum: [ok, unhealthy] } User: type: object required: [id, name, username, email] properties: id: { type: string } name: { type: string } username: { type: string } email: { type: string } avatar_url: { type: string, nullable: true } locale: { type: string } timezone: { type: string } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } Session: type: object required: [id, user_id, token, expires_at] properties: id: { type: string } user_id: { type: string } token: { type: string } expires_at: { type: string, format: date-time } created_at: { type: string, format: date-time } Team: type: object required: [id, name, slug, created_by] properties: id: { type: string } name: { type: string } slug: { type: string } description: { type: string, nullable: true } avatar_url: { type: string, nullable: true } created_by: { type: string } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } TeamMember: type: object required: [id, team_id, user_id, role] properties: id: { type: string } team_id: { type: string } user_id: { type: string } role: { type: string, enum: [owner, admin, editor, viewer] } created_at: { type: string, format: date-time } TeamInvite: type: object required: [id, team_id, email, role, invited_by] properties: id: { type: string } team_id: { type: string } email: { type: string } role: { type: string } invited_by: { type: string } expires_at: { type: string, format: date-time } created_at: { type: string, format: date-time } Drawing: type: object required: [id, team_id, title, owner_user_id, visibility] properties: id: { type: string } team_id: { type: string } folder_id: { type: string, nullable: true } project_id: { type: string, nullable: true } slug: { type: string, nullable: true } title: { type: string } description: { type: string, nullable: true } owner_user_id: { type: string } latest_revision_id: { type: string, nullable: true } visibility: { type: string, enum: [private, team, public] } is_archived: { type: boolean } thumbnail_asset_id: { type: string, nullable: true } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } deleted_at: { type: string, format: date-time, nullable: true } owner: { $ref: '#/components/schemas/User' } folder: { $ref: '#/components/schemas/Folder' } project: { $ref: '#/components/schemas/Project' } DrawingRevision: type: object required: [id, drawing_id, change_summary, created_at, created_by] properties: id: { type: string } drawing_id: { type: string } change_summary: { type: string } created_at: { type: string, format: date-time } created_by: { type: string } snapshot: { type: string } Project: type: object required: [id, team_id, name, slug, created_by] properties: id: { type: string } team_id: { type: string } name: { type: string } slug: { type: string } description: { type: string, nullable: true } created_by: { type: string } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } Folder: type: object required: [id, team_id, name, slug, path_cache, visibility, created_by] properties: id: { type: string } team_id: { type: string } project_id: { type: string, nullable: true } parent_folder_id: { type: string, nullable: true } name: { type: string } slug: { type: string } path_cache: { type: string } visibility: { type: string } created_by: { type: string } created_at: { type: string, format: date-time } updated_at: { type: string, format: date-time } Activity: type: object required: [id, team_id, user_id, action, resource_type, resource_id, created_at] properties: id: { type: string } team_id: { type: string } user_id: { type: string } action: { type: string } resource_type: { type: string } resource_id: { type: string } metadata: { type: object } created_at: { type: string, format: date-time } Template: type: object required: [id, name, scope] properties: id: { type: string } name: { type: string } description: { type: string, nullable: true } scope: { type: string, enum: [user, team, global] } team_id: { type: string, nullable: true } category: { type: string, nullable: true } tags: { type: array, items: { type: string } } preview_asset_id: { type: string, nullable: true } created_by: { type: string } created_at: { type: string, format: date-time } PermissionGrant: type: object required: [id, resource_type, resource_id, user_id, permission, granted_by] properties: id: { type: string } resource_type: { type: string } resource_id: { type: string } user_id: { type: string } permission: { type: string, enum: [read, write, admin] } granted_by: { type: string } expires_at: { type: string, format: date-time, nullable: true } created_at: { type: string, format: date-time } ShareLink: type: object required: [id, resource_type, resource_id, access_level, created_by] properties: id: { type: string } resource_type: { type: string } resource_id: { type: string } access_level: { type: string, enum: [view, comment, edit] } password_hash: { type: string, nullable: true } expires_at: { type: string, format: date-time, nullable: true } created_by: { type: string } created_at: { type: string, format: date-time } Asset: type: object required: [id, drawing_id, type, name] properties: id: { type: string } drawing_id: { type: string } type: { type: string } name: { type: string } url: { type: string, nullable: true } file_path: { type: string, nullable: true } created_by: { type: string } created_at: { type: string, format: date-time } Embed: type: object required: [id, drawing_id, url, type] properties: id: { type: string } drawing_id: { type: string } url: { type: string } type: { type: string } title: { type: string, nullable: true } thumbnail_url: { type: string, nullable: true } created_by: { type: string } created_at: { type: string, format: date-time } LinkReference: type: object required: [id, source_type, source_id, target_type, target_id] properties: id: { type: string } source_type: { type: string } source_id: { type: string } target_type: { type: string } target_id: { type: string } relation: { type: string } created_at: { type: string, format: date-time } SharedResource: type: object required: [drawing, share_link] properties: drawing: { $ref: '#/components/schemas/Drawing' } share_link: { $ref: '#/components/schemas/ShareLink' } WorkspaceStats: type: object required: [total_teams, total_drawings, total_members, recent_activity] properties: total_teams: { type: integer } total_drawings: { type: integer } total_members: { type: integer } recent_activity: { type: array, items: { $ref: '#/components/schemas/Activity' } } CreateTeamRequest: type: object required: [name] properties: name: { type: string } description: { type: string, nullable: true } avatar_url: { type: string, nullable: true } CreateDrawingRequest: type: object required: [team_id, title, elements] properties: team_id: { type: string } folder_id: { type: string, nullable: true } project_id: { type: string, nullable: true } title: { type: string } description: { type: string, nullable: true } visibility: { type: string, enum: [private, team, public], default: team } elements: { type: string } UpdateDrawingRequest: type: object properties: title: { type: string } description: { type: string, nullable: true } folder_id: { type: string, nullable: true } project_id: { type: string, nullable: true } visibility: { type: string, enum: [private, team, public] } is_archived: { type: boolean } elements: { type: string } CreateFolderRequest: type: object required: [team_id, name] properties: team_id: { type: string } project_id: { type: string, nullable: true } parent_folder_id: { type: string, nullable: true } name: { type: string } visibility: { type: string } CreateProjectRequest: type: object required: [team_id, name] properties: team_id: { type: string } name: { type: string } description: { type: string, nullable: true } CreateInviteRequest: type: object required: [email, role] properties: email: { type: string, format: email } role: { type: string, enum: [admin, editor, viewer] } CreatePermissionGrantRequest: type: object required: [user_id, permission] properties: user_id: { type: string } permission: { type: string, enum: [read, write, admin] } expires_at: { type: string, format: date-time, nullable: true } CreateShareLinkRequest: type: object required: [access_level] properties: access_level: { type: string, enum: [view, comment, edit] } password: { type: string, nullable: true } expires_at: { type: string, format: date-time, nullable: true } CreateAssetRequest: type: object required: [name, type] properties: name: { type: string } type: { type: string } url: { type: string, nullable: true } file_path: { type: string, nullable: true } CreateEmbedRequest: type: object required: [url, type] properties: url: { type: string } type: { type: string } title: { type: string, nullable: true } thumbnail_url: { type: string, nullable: true } CreateLinkRequest: type: object required: [target_type, target_id] properties: target_type: { type: string } target_id: { type: string } relation: { type: string }