Official Node.js SDK for Copera.ai - The ultimate platform for workflow automation and data management.
📖 Full Documentation: For complete guides, tutorials, and API reference, visit developers.copera.ai
- ✨ Full TypeScript Support - Complete type definitions for all API endpoints
- 🔄 Modern ESM & CJS - Works with both ES modules and CommonJS
- 🏭 Factory Pattern - Clean dependency injection architecture
- 🧪 Sandbox Mode - Test against development environment
- 🎯 Type-Safe - Full IntelliSense support in your IDE
- 📦 Zero Dependencies - Uses native Fetch API
- ✅ Fully Tested - Comprehensive test coverage
# npm
npm install @copera.ai/sdk
# yarn
yarn add @copera.ai/sdk
# pnpm
pnpm add @copera.ai/sdk- Node.js 18 or higher
- An API key from Copera.ai
import { CoperaAI } from '@copera.ai/sdk';
// Initialize the SDK
const copera = CoperaAI({
apiKey: 'your-api-key-here'
});
// List all boards
const boards = await copera.board.listBoards();
console.log(boards);
// Send a message to a channel
await copera.channel.sendMessage({
channelId: 'channel-id',
message: 'Hello from Copera.ai SDK!'
});
// Browse drive files
const tree = await copera.drive.getDriveTree({ depth: 3 });
console.log(tree.root);import { CoperaAI } from '@copera.ai/sdk';
const copera = CoperaAI({
apiKey: process.env.COPERA_API_KEY
});Use sandbox mode to test against the development environment:
const copera = CoperaAI({
apiKey: process.env.COPERA_API_KEY,
sandbox: true // Use development environment
});List all boards available in your workspace.
const boards = await copera.board.listBoards();Returns: Promise<Board[]>
Get details of a specific board.
const board = await copera.board.getBoardDetails({
boardId: 'board-id'
});Parameters:
boardId(string, required) - The board ID
Returns: Promise<Board>
List all tables in a specific board.
const tables = await copera.board.listBoardTables({
boardId: 'board-id'
});Parameters:
boardId(string, required) - The board ID
Returns: Promise<Table[]>
Get details of a specific table.
const table = await copera.board.getBoardTable({
boardId: 'board-id',
tableId: 'table-id'
});Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table ID
Returns: Promise<Table>
List all rows in a table.
const rows = await copera.board.listTableRows({
boardId: 'board-id',
tableId: 'table-id'
});Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table ID
Returns: Promise<Row[]>
Get a specific row from a table.
const row = await copera.board.getTableRow({
boardId: 'board-id',
tableId: 'table-id',
rowId: 'row-id'
});Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table IDrowId(string, required) - The row ID
Returns: Promise<Row>
Create a new row in a table.
const newRow = await copera.board.createTableRow({
boardId: 'board-id',
tableId: 'table-id',
description: 'Optional description',
columns: [
{ columnId: 'column-1', value: 'Value 1' },
{ columnId: 'column-2', value: 'Value 2' }
]
});Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table IDdescription(string, optional) - Row descriptioncolumns(ColumnValue[], required) - Array of column values
Returns: Promise<Row>
Authenticate a table row by matching an identifier column and verifying a password column. Useful for building custom authentication systems using board tables.
const row = await copera.board.authenticateTableRow({
boardId: 'board-id',
tableId: 'table-id',
identifierColumnId: 'email-column-id',
identifierColumnValue: 'user@example.com',
passwordColumnId: 'password-column-id',
passwordColumnValue: 'user-password'
});
if ('error' in row) {
// Handle authentication error (400: not found, 401: invalid password)
console.error('Auth failed:', row.error);
} else {
// Authentication successful
console.log('Authenticated user:', row);
}Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table IDidentifierColumnId(string, required) - The column ID to use as identifier (e.g., email, username)identifierColumnValue(string, required) - The value to match in the identifier columnpasswordColumnId(string, required) - The password column IDpasswordColumnValue(string, required) - The plaintext password to verify
Returns: Promise<Row> - The authenticated row (password columns are masked with ********)
Errors:
400 Bad Request- No row found with the provided identifier401 Unauthorized- Invalid password
List comments on a specific row. Supports cursor-based pagination and visibility filtering.
const comments = await copera.board.listRowComments({
boardId: 'board-id',
tableId: 'table-id',
rowId: 'row-id',
visibility: 'all', // optional: "all" | "internal" | "external"
after: 'cursor-id', // optional: forward pagination cursor
before: 'cursor-id' // optional: backward pagination cursor
});Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table IDrowId(string, required) - The row IDvisibility(string, optional) - Filter by visibility:"all","internal", or"external". Defaults to"all"after(string, optional) - Comment ID cursor for forward paginationbefore(string, optional) - Comment ID cursor for backward pagination
Returns: Promise<RowCommentPagination>
Create a new comment on a specific row. Supports HTML content.
const comment = await copera.board.createRowComment({
boardId: 'board-id',
tableId: 'table-id',
rowId: 'row-id',
content: '<p>This task needs review</p>',
visibility: 'internal' // optional: "internal" | "external"
});Parameters:
boardId(string, required) - The board IDtableId(string, required) - The table IDrowId(string, required) - The row IDcontent(string, required) - Comment text content (HTML supported)visibility(string, optional) - Comment visibility:"internal"or"external". Defaults to"internal"
Returns: Promise<RowComment>
Create a new document.
const doc = await copera.doc.createDoc({
title: 'My Document',
parent: 'parent-doc-id', // optional
content: '# Hello World' // optional, markdown content
});Parameters:
title(string, required) - Document titleparent(string, optional) - Parent document ID for nestingcontent(string, optional) - Initial markdown content (processed asynchronously)
Returns: Promise<Doc>
Get metadata of a specific document.
const doc = await copera.doc.getDocDetails({
docId: 'doc-id'
});Parameters:
docId(string, required) - The document ID
Returns: Promise<Doc>
Get the markdown content of a document.
const { content } = await copera.doc.getDocContent({
docId: 'doc-id'
});Parameters:
docId(string, required) - The document ID
Returns: Promise<DocContent>
Update a document's metadata (title, icon, cover).
const doc = await copera.doc.updateDoc({
docId: 'doc-id',
title: 'Updated Title',
icon: { type: 'emoji', value: '📄' },
cover: { type: 'url', value: 'https://example.com/cover.jpg' }
});Parameters:
docId(string, required) - The document IDtitle(string, optional) - New document titleicon(DocIcon, optional) - Icon withtypeandvaluecover(DocCover, optional) - Cover withtypeandvalue
Returns: Promise<Doc>
Update a document's markdown content. Updates are processed asynchronously.
const result = await copera.doc.updateDocContent({
docId: 'doc-id',
operation: 'replace', // "replace" | "append" | "prepend"
content: '# New Content'
});Parameters:
docId(string, required) - The document IDoperation(string, required) -"replace","append", or"prepend"content(string, required) - Markdown content to apply
Returns: Promise<DocContentUpdateResult>
Delete a document (soft-delete). Only the document owner can delete.
const result = await copera.doc.deleteDoc({
docId: 'doc-id'
});Parameters:
docId(string, required) - The document ID
Returns: Promise<DocDeleteResult>
Search documents by query. Returns highlighted matches in title and content.
const results = await copera.doc.searchDocs({
q: 'meeting notes',
sortBy: 'updatedAt', // optional: "createdAt" | "updatedAt"
sortOrder: 'desc', // optional: "asc" | "desc"
limit: 20 // optional: 1-50
});Parameters:
q(string, required) - Search querysortBy(string, optional) - Sort field:"createdAt"or"updatedAt". Defaults to"updatedAt"sortOrder(string, optional) - Sort direction:"asc"or"desc". Defaults to"desc"limit(number, optional) - Max results 1-50. Defaults to 20
Returns: Promise<DocSearchResult>
Get the hierarchical document tree.
const tree = await copera.doc.getDocTree({
parentId: 'doc-id', // optional: start from specific doc
depth: 3 // optional: max nesting depth 1-10
});Parameters:
parentId(string, optional) - Start tree from this document (omit for root-level)depth(number, optional) - Max nesting depth 1-10. Defaults to 3
Returns: Promise<DocTreeResult>
Send a message to a channel.
await copera.channel.sendMessage({
channelId: 'channel-id',
message: 'Your message here',
name: 'Optional sender name' // optional
});Parameters:
channelId(string, required) - The channel IDmessage(string, required) - Message content (1-10000 characters)name(string, optional) - Name of the sender
Returns: Promise<void>
Get the hierarchical drive tree of files and folders.
const tree = await copera.drive.getDriveTree({
parentId: 'folder-id', // optional: start from specific folder
depth: 3 // optional: max nesting depth 1-10
});Parameters:
parentId(string, optional) - Start tree from this folder (omit for root-level)depth(number, optional) - Max nesting depth 1-10. Defaults to 3
Returns: Promise<DriveTreeResult>
Search files and folders by name.
const results = await copera.drive.searchDrive({
q: 'quarterly report',
sortBy: 'updatedAt', // optional: "name" | "createdAt" | "updatedAt"
sortOrder: 'desc', // optional: "asc" | "desc"
limit: 20 // optional
});Parameters:
q(string, required) - Search querysortBy(string, optional) - Sort fieldsortOrder(string, optional) - Sort direction:"asc"or"desc"limit(number, optional) - Max results. Defaults to 20
Returns: Promise<DriveSearchResult>
Get metadata for a file or folder.
const file = await copera.drive.getFile({
fileId: 'file-id'
});Parameters:
fileId(string, required) - The file or folder ID
Returns: Promise<DriveItem>
Get a signed download URL for a file (valid for 3 hours).
const { url } = await copera.drive.downloadFile({
fileId: 'file-id'
});
// Use the signed URL to download the fileParameters:
fileId(string, required) - The file ID (folders not supported)
Returns: Promise<DriveDownloadResult>
Create a new folder.
const folder = await copera.drive.createFolder({
name: 'Reports',
parentId: 'parent-folder-id' // optional
});Parameters:
name(string, required) - Folder nameparentId(string, optional) - Parent folder ID (omit for root-level)
Returns: Promise<DriveItem>
Start a multipart file upload. Returns an upload ID and file key for subsequent steps.
const { uploadId, fileKey } = await copera.drive.startUpload({
fileName: 'report.pdf',
fileSize: 1048576,
mimeType: 'application/pdf',
parentId: 'folder-id' // optional
});Parameters:
fileName(string, required) - File namefileSize(number, required) - Total file size in bytesmimeType(string, required) - MIME type (e.g.,"application/pdf")parentId(string, optional) - Parent folder ID
Returns: Promise<DriveUploadStartResult>
Get presigned S3 URLs for uploading file parts.
const { parts } = await copera.drive.getPresignedUrls({
uploadId: 'upload-id',
fileKey: 'file-key',
parts: 3
});
// Upload each chunk via PUT to the corresponding signedUrlParameters:
uploadId(string, required) - Upload ID fromstartUploadfileKey(string, required) - File key fromstartUploadparts(number, required) - Number of file chunks
Returns: Promise<DriveUploadPresignedUrlsResult>
Finalize a multipart upload after all parts have been uploaded to S3.
const file = await copera.drive.finalizeUpload({
uploadId: 'upload-id',
fileKey: 'file-key',
parts: [
{ partNumber: 1, eTag: 'etag-from-s3' },
{ partNumber: 2, eTag: 'etag-from-s3' }
]
});Parameters:
uploadId(string, required) - Upload ID fromstartUploadfileKey(string, required) - File key fromstartUploadparts(DriveUploadPart[], required) - Array of{ partNumber, eTag }from S3 responses
Returns: Promise<DriveItem>
The SDK exports all TypeScript types for your convenience:
import {
Board,
Table,
Row,
Column,
ColumnValue,
RowComment,
RowCommentPagination,
CommentAuthor,
PageInfo,
Doc,
DocContent,
DocIcon,
DocCover,
DocContentUpdateResult,
DocDeleteResult,
DocSearchResult,
DocSearchHit,
DocTreeResult,
DocTreeNode,
ListRowCommentsParams,
CreateRowCommentParams,
CreateDocParams,
UpdateDocParams,
UpdateDocContentParams,
SearchDocsParams,
GetDocTreeParams,
DriveItem,
DriveTreeNode,
DriveTreeResult,
DriveSearchResult,
DriveDownloadResult,
DriveUploadStartResult,
DrivePresignedUrlPart,
DriveUploadPresignedUrlsResult,
DriveUploadPart,
GetDriveTreeParams,
SearchDriveParams,
CreateFolderParams,
StartUploadParams,
GetPresignedUrlsParams,
FinalizeUploadParams,
SendMessageParams,
AuthenticateTableRowParams,
CoperaAIError
} from '@copera.ai/sdk';interface Board {
_id: string;
name: string;
description?: string;
createdAt: string;
updatedAt: string;
}
interface Table {
_id: string;
name: string;
board: string;
columns: Column[];
createdAt: string;
updatedAt: string;
}
interface Row {
_id: string;
rowId: string;
owner: string;
table: string;
board: string;
columns: ColumnValue[];
createdAt: string;
updatedAt: string;
}
interface Column {
columnId: string;
label: string;
type: string;
order?: number;
}
interface ColumnValue {
columnId: string;
value: unknown;
}
interface RowComment {
_id: string;
content: string | null;
contentType: string;
visibility: "internal" | "external";
author: CommentAuthor;
createdAt: string;
updatedAt: string;
}
interface CommentAuthor {
_id: string;
name: string | null;
picture: string | null;
email: string | null;
}
interface PageInfo {
endCursor: string | null;
startCursor: string | null;
hasNextPage: boolean;
hasPreviousPage: boolean;
}
interface RowCommentPagination {
items: RowComment[];
pageInfo: PageInfo;
}
interface Doc {
_id: string;
title: string;
owner: string;
createdAt: string;
updatedAt: string;
icon?: DocIcon;
cover?: DocCover;
starred: boolean;
parent?: string;
}
interface DocIcon {
type: string;
value: string;
}
interface DocCover {
type: string;
value: string;
}
interface DocContent {
content: string;
}
interface DocContentUpdateResult {
success: boolean;
message: string;
}
interface DocDeleteResult {
success: boolean;
}
interface DocSearchResult {
hits: DocSearchHit[];
totalHits: number;
query: string;
}
interface DocSearchHit {
_id: string;
title: string;
parents: DocSearchParent[];
highlight: DocSearchHighlight;
createdAt: string;
updatedAt: string;
}
interface DocTreeResult {
root: DocTreeNode[];
totalDocs: number;
truncated: boolean;
nextParentIds: string[];
}
interface DocTreeNode extends Doc {
hasChildren: boolean;
children: DocTreeNode[];
}
interface DriveItem {
id: string;
name: string;
type: "file" | "folder";
mimeType?: string;
fileSize?: number;
parentId?: string;
owner?: string;
createdAt: string;
updatedAt: string;
}
interface DriveTreeNode {
id: string;
name: string;
type: "file" | "folder";
hasChildren: boolean;
children: DriveTreeNode[];
}
interface DriveTreeResult {
root: DriveTreeNode[];
totalItems: number;
truncated: boolean;
nextParentIds: string[];
}
interface DriveSearchResult {
hits: DriveItem[];
totalHits: number;
query: string;
}
interface DriveDownloadResult {
url: string;
}
interface DriveUploadStartResult {
uploadId: string;
fileKey: string;
}
interface DrivePresignedUrlPart {
signedUrl: string;
PartNumber: number;
}
interface DriveUploadPresignedUrlsResult {
parts: DrivePresignedUrlPart[];
}
interface DriveUploadPart {
partNumber: number;
eTag: string;
}The SDK returns errors in a consistent format:
try {
const boards = await copera.board.listBoards();
if ('error' in boards) {
console.error('API Error:', boards.error);
return;
}
// Success - process boards
console.log(boards);
} catch (error) {
console.error('Network Error:', error);
}import { CoperaAI } from '@copera.ai/sdk';
const copera = CoperaAI({ apiKey: 'your-api-key' });const { CoperaAI } = require('@copera.ai/sdk');
const copera = CoperaAI({ apiKey: 'your-api-key' });// tsconfig.json
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler"
}
}import { CoperaAI } from '@copera.ai/sdk';
const copera = CoperaAI({ apiKey: process.env.COPERA_API_KEY });
async function manageBoardData() {
// 1. Get all boards
const boards = await copera.board.listBoards();
const firstBoard = boards[0];
// 2. Get board details
const board = await copera.board.getBoardDetails({
boardId: firstBoard._id
});
// 3. Get all tables in the board
const tables = await copera.board.listBoardTables({
boardId: board._id
});
// 4. Get rows from first table
const rows = await copera.board.listTableRows({
boardId: board._id,
tableId: tables[0]._id
});
// 5. Create a new row
const newRow = await copera.board.createTableRow({
boardId: board._id,
tableId: tables[0]._id,
columns: [
{ columnId: 'col-1', value: 'New Value' }
]
});
console.log('Created row:', newRow);
}
manageBoardData().catch(console.error);import { CoperaAI } from '@copera.ai/sdk';
const copera = CoperaAI({ apiKey: process.env.COPERA_API_KEY });
async function manageDocuments() {
// 1. Create a document
const doc = await copera.doc.createDoc({
title: 'Project Notes',
content: '# Project Notes\n\nInitial content here.'
});
// 2. Update the document title and icon
await copera.doc.updateDoc({
docId: doc._id,
title: 'Project Notes - Q1 2026',
icon: { type: 'emoji', value: '📋' }
});
// 3. Append content
await copera.doc.updateDocContent({
docId: doc._id,
operation: 'append',
content: '\n## New Section\n\nAppended via SDK.'
});
// 4. Search documents
const results = await copera.doc.searchDocs({
q: 'Project Notes',
limit: 10
});
console.log('Found:', results.totalHits, 'documents');
// 5. Browse document tree
const tree = await copera.doc.getDocTree({ depth: 3 });
console.log('Root documents:', tree.root.length);
}
manageDocuments().catch(console.error);import { CoperaAI } from '@copera.ai/sdk';
const copera = CoperaAI({ apiKey: process.env.COPERA_API_KEY });
async function manageDrive() {
// 1. Create a folder
const folder = await copera.drive.createFolder({
name: 'Project Files'
});
// 2. Upload a file (multipart)
const { uploadId, fileKey } = await copera.drive.startUpload({
fileName: 'report.pdf',
fileSize: 5242880, // 5 MB
mimeType: 'application/pdf',
parentId: folder.id
});
// 3. Get presigned URLs for upload chunks
const { parts: presignedParts } = await copera.drive.getPresignedUrls({
uploadId,
fileKey,
parts: 1
});
// 4. Upload chunks to S3 (using fetch or any HTTP client)
const response = await fetch(presignedParts[0].signedUrl, {
method: 'PUT',
body: fileBuffer
});
const eTag = response.headers.get('etag')!;
// 5. Finalize the upload
const file = await copera.drive.finalizeUpload({
uploadId,
fileKey,
parts: [{ partNumber: 1, eTag }]
});
// 6. Browse the drive tree
const tree = await copera.drive.getDriveTree({ depth: 3 });
console.log('Drive items:', tree.totalItems);
// 7. Search for files
const results = await copera.drive.searchDrive({ q: 'report' });
console.log('Found:', results.totalHits, 'files');
// 8. Get a download link
const { url } = await copera.drive.downloadFile({ fileId: file.id });
console.log('Download URL:', url);
}
manageDrive().catch(console.error);import { CoperaAI } from '@copera.ai/sdk';
const copera = CoperaAI({ apiKey: process.env.COPERA_API_KEY });
async function sendNotification() {
await copera.channel.sendMessage({
channelId: 'your-channel-id',
message: 'Task completed successfully!',
name: 'Automation Bot'
});
}
sendNotification().catch(console.error);// .env
COPERA_API_KEY=your-api-key-here
COPERA_CHANNEL_ID=your-channel-id
// app.ts
import { CoperaAI } from '@copera.ai/sdk';
import 'dotenv/config';
const copera = CoperaAI({
apiKey: process.env.COPERA_API_KEY!
});
await copera.channel.sendMessage({
channelId: process.env.COPERA_CHANNEL_ID!,
message: 'Hello from automation!'
});Never hardcode API keys in your source code:
// ❌ Bad
const copera = CoperaAI({ apiKey: 'sk_live_abc123...' });
// ✅ Good
const copera = CoperaAI({ apiKey: process.env.COPERA_API_KEY });Always handle errors appropriately:
try {
const boards = await copera.board.listBoards();
if ('error' in boards) {
// Handle API error
logger.error('API Error:', boards.error);
return;
}
// Process successful response
} catch (error) {
// Handle network/unexpected errors
logger.error('Unexpected error:', error);
}Take advantage of full type safety:
import { CoperaAI, type Board, type Row } from '@copera.ai/sdk';
const copera = CoperaAI({ apiKey: process.env.COPERA_API_KEY });
// TypeScript will provide full autocomplete and type checking
const boards: Board[] = await copera.board.listBoards();Use different API keys for different environments:
const copera = CoperaAI({
apiKey: process.env.COPERA_API_KEY,
sandbox: process.env.NODE_ENV === 'development'
});We welcome contributions! Please see our Contributing Guide for details.
If you discover a security vulnerability, please email security@copera.ai. Do not create public issues for security vulnerabilities.
See our Security Policy for more details.
This project is licensed under the MIT License - see the LICENSE file for details.
- 📧 Email: support@copera.ai
- 🐛 Issues: GitHub Issues
- 📖 Documentation: developers.copera.ai
Made with ❤️ by the Copera.ai team