@contio/partner-sdk
v1.5.0
Published
Official SDK for Contio MeetingOS Partner API
Maintainers
Readme
Contio Partner SDK
Official TypeScript/JavaScript SDK for integrating with the Contio Partner API.
📚 Full Documentation: docs.contio.ai/partner-api
Installation
npm install @contio/partner-sdkRequirements
- Node.js 22.0.0 or higher (LTS)
- TypeScript 4.5.0 or higher (for TypeScript users)
Quick Start
OAuth User Operations
import { ContioPartnerSDK } from '@contio/partner-sdk';
const { oauth, user } = ContioPartnerSDK.forUser({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'https://your-app.com/callback'
});
// Generate authorization URL
const authUrl = oauth.getAuthorizationUrl('csrf-state');
// After user authorizes, exchange code for tokens
const tokens = await oauth.exchangeCodeForToken(authorizationCode);
// Access user data
const meetings = await user.getMeetings();
const actionItems = await user.getActionItems();Shared Workspace Provisioning
Provision new users into existing shared workspaces instead of creating individual workspaces:
import { ContioPartnerSDK } from '@contio/partner-sdk';
const { oauth } = ContioPartnerSDK.forUser({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'https://your-app.com/callback'
});
// Provision a new user into a shared workspace
const result = await oauth.initiatePartnerAuth(
'[email protected]',
'New User',
{ workspace_id: 'existing-workspace-uuid' }
);
// Provision as a workspace admin
const adminResult = await oauth.initiatePartnerAuth(
'[email protected]',
'Admin User',
{ workspace_id: 'existing-workspace-uuid', is_admin: true }
);
// Check a user's workspace after provisioning
const profile = await user.getUserProfile();
console.log(`Workspace: ${profile.workspace_name} (${profile.workspace_role})`);API Key Admin Operations
import { ContioPartnerSDK } from '@contio/partner-sdk';
const { admin } = ContioPartnerSDK.forAdmin({
apiKey: 'your-api-key'
});
const workflows = await admin.getWorkflows();
const stats = await admin.getStats();Webhook Management
const disabledApp = await admin.updateWebhookStatus({
enabled: false
});
const filteredApp = await admin.setWebhookFilter({
type: 'include',
events: ['meeting.created', 'meeting.completed']
});
const unfilteredApp = await admin.removeWebhookFilter();
console.log(disabledApp.webhook_enabled);
console.log(filteredApp.webhook_filter);
console.log(unfilteredApp.webhook_filter); // nullKey Features
Calendar Events
Access user calendar events and retrieve detailed event information:
// List calendar events in a date range
const events = await user.getCalendarEvents({
start: '2026-01-01T00:00:00Z',
end: '2026-01-31T23:59:59Z'
});
// Get detailed information about a specific calendar event
const event = await user.getCalendarEvent('cal-event-123');
console.log(`Event: ${event.title}`);
console.log(`Organizer: ${event.organizer?.email}`);
// Create a meeting from a calendar event
const result = await user.createMeetingFromCalendarEvent('cal-event-123');
console.log(`Created: ${result.created}, Meeting ID: ${result.meeting?.id}`);Webhook Events
The SDK supports all Partner API webhook events with full TypeScript typing:
Meeting Events:
meeting.created- New meeting createdmeeting.updated- Meeting properties changed (title, scheduled time, etc.)meeting.completed- Meeting ended with notes/summary available
Calendar Events:
calendar_event.created- Calendar event synced from external calendarcalendar_event.updated- Calendar event updated in external calendarcalendar_event.deleted- Calendar event cancelled/deleted
Agenda Items:
agenda_item.created- Agenda item created in meeting or backlogagenda_item.updated- Agenda item title, status, or properties changedagenda_item.deleted- Agenda item removed
Action Items:
action_item.created- New action item createdaction_item.updated- Action item status or details changedaction_item.completed- Action item marked as completed
Other Events:
workflow.assignment.created- Action item assigned to your workflowparticipant.added- Participant(s) added to meetingparticipant.removed- Participant removed from meetinguser.connection.revoked- User revoked connection to your app
Webhook Verification
import { verifyWebhookSignature, parseWebhook } from '@contio/partner-sdk';
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-contio-signature'] as string;
try {
const event = parseWebhook(req.body, signature, process.env.WEBHOOK_SECRET!);
// Handle event...
res.status(200).json({ received: true });
} catch (error) {
res.status(401).json({ error: 'Invalid signature' });
}
});Error Handling
All API errors are thrown as ContioAPIError instances with structured information:
import { ContioAPIError } from '@contio/partner-sdk';
try {
const meeting = await user.getMeeting('invalid-id');
} catch (error) {
if (error instanceof ContioAPIError) {
console.error('Code:', error.code); // e.g., 'not_found', 'unauthorized'
console.error('Message:', error.message); // Human-readable description
console.error('Status:', error.statusCode); // HTTP status code (404, 401, etc.)
console.error('Response:', error.response); // Full error response body
}
}Common Error Codes
| Code | Status | Description |
|------|--------|-------------|
| unauthorized | 401 | Invalid or expired access token |
| forbidden | 403 | Insufficient permissions for this resource |
| not_found | 404 | Resource does not exist |
| validation_error | 400 | Invalid request parameters |
| rate_limited | 429 | Too many requests; retry after backoff |
| workspace_not_found | 400 | Specified workspace_id does not exist |
| workspace_not_authorized | 403 | Workspace not owned by a user from the same partner |
| workspace_conflict | 409 | User already belongs to a different workspace |
| internal_error | 500 | Server error; retry with exponential backoff |
The SDK automatically retries transient errors (5xx) with exponential backoff.
Documentation
For complete documentation, visit:
- Quick Start - Get up and running
- Authentication - OAuth 2.0 and API keys
- API Reference - Complete method reference
- OAuth Flow - Detailed OAuth implementation
- Webhook Events - Real-time notifications
- Credential Management - Secure credential rotation
- Troubleshooting - Common issues and solutions
Token Format
Contio uses opaque tokens for improved security. Access tokens (cto_at_v1_...) and refresh tokens (cto_rt_v1_...) contain no readable claims.
| Token | Format | Validity |
|-------|--------|----------|
| Authorization code | Opaque | 5 minutes |
| Access token | cto_at_v1_... | 24 hours |
| Refresh token | cto_rt_v1_... | 30 days |
| ID token | JWT | 24 hours |
Important: Do not attempt to decode or parse access/refresh tokens. Use introspection to get token metadata:
const tokenInfo = await oauth.introspectToken(accessToken);
console.log('Scopes:', tokenInfo.scope);
console.log('Expires:', new Date(tokenInfo.exp * 1000));Security Best Practices
Token Storage
- Never store tokens in client-side code (localStorage, cookies accessible to JS)
- Use secure, server-side storage (encrypted database, secure session store)
- Refresh tokens are sensitive — treat them like passwords
- Set appropriate token lifetimes — access tokens expire in 24 hours by default
// Example: Storing tokens securely (server-side)
async function storeTokens(userId: string, tokens: AuthTokens) {
await db.userTokens.upsert({
where: { userId },
data: {
accessToken: encrypt(tokens.accessToken),
refreshToken: encrypt(tokens.refreshToken),
expiresAt: tokens.expiresAt,
},
});
}Webhook Security
- Always verify signatures using
parseWebhook()orverifyWebhookSignature() - Use
express.raw()to preserve the raw request body for signature verification - Implement idempotency — webhooks may be delivered more than once
- Respond quickly (< 5 seconds) to avoid timeout retries
// Track processed webhook IDs to handle retries
const processedEvents = new Set<string>();
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const event = parseWebhook(req.body, signature, secret);
if (processedEvents.has(event.event_id)) {
return res.status(200).json({ received: true, duplicate: true });
}
processedEvents.add(event.event_id);
// Process event...
});Development
Building from Source
git clone https://github.com/Contio-AI/partner-sdk.git
cd partner-sdk
npm install
npm run buildRunning Tests
npm test # Run all tests
npm test -- --coverage # Run with coverage report
npm test -- oauth.test.ts # Run specific test fileScripts
| Script | Description |
|--------|-------------|
| npm run build | Compile TypeScript to JavaScript |
| npm test | Run the test suite |
| npm run lint | Run ESLint on source files |
| npm run docs | Generate TypeDoc documentation |
| npm run gen-types | Regenerate types from OpenAPI spec |
Support
For support, contact [email protected]
License
This SDK is licensed under the MIT License. See the LICENSE file for details.
Note: This license applies to the SDK source code only. Use of the Contio Partner API is governed by your Partner Agreement with Contio, Inc.
