Official JavaScript/TypeScript SDK for the BundleUp API. Connect to 100+ integrations with a single, unified API. Build once, integrate everywhere.
- Installation
- Requirements
- Features
- Examples
- Quick Start
- Authentication
- Core Concepts
- API Reference
- Error Handling
- Development
- Contributing
- License
Install the SDK using your preferred package manager:
npm:
npm install @bundleup/sdkyarn:
yarn add @bundleup/sdkpnpm:
pnpm add @bundleup/sdk- Node.js: 16.0.0 or higher
- TypeScript: 5.0+ (for TypeScript projects)
- Fetch API: Native fetch support (Node.js 18+) or polyfill for older versions
For Node.js versions below 18, you'll need to install a fetch polyfill:
npm install node-fetchThen import it before using the SDK:
import fetch from 'node-fetch';
globalThis.fetch = fetch;- 🚀 TypeScript First - Built with TypeScript, includes comprehensive type definitions
- 📦 Modern JavaScript - ESM and CommonJS support for maximum compatibility
- ⚡ Promise-based API - Async/await support using native fetch
- 🔌 100+ Integrations - Connect to Slack, GitHub, Jira, Linear, and many more
- 🎯 Unified API - Consistent interface across all integrations via Unify API
- 🔑 Proxy API - Direct access to underlying integration APIs
- 🪶 Lightweight - Zero dependencies beyond native fetch API
- 🛡️ Error Handling - Comprehensive error messages and validation
- 📚 Well Documented - Extensive documentation and examples
Runnable examples are available in the examples/ directory:
examples/basic_usage.js- Client setup, connections, integrations, and webhooksexamples/proxy_api.js- Proxy API GET request with a connectionexamples/unify_api.js- Unify Chat, Git, and PM endpoint usageexamples/README.md- Setup and execution instructions
Get started with BundleUp in just a few lines of code:
import { BundleUp } from '@bundleup/sdk';
// Initialize the client
const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
// List all active connections
const connections = await client.connections.list();
console.log(`You have ${connections.length} active connections`);
// Use the Proxy API to make requests to integrated services
const proxy = client.proxy('conn_123');
const response = await proxy.get('/api/users');
const users = await response.json();
console.log('Users:', users);
// Use the Unify API for standardized data across integrations
const unify = client.unify('conn_456');
const channels = await unify.chat.channels({ limit: 10 });
console.log('Chat channels:', channels.data);The BundleUp SDK uses API keys for authentication. You can obtain your API key from the BundleUp Dashboard.
- Sign in to your BundleUp Dashboard
- Navigate to API Keys
- Click Create API Key
- Copy your API key and store it securely
import { BundleUp } from '@bundleup/sdk';
// Initialize with API key
const client = new BundleUp('your_api_key_here');
// Or use environment variable (recommended)
const client = new BundleUp(process.env.BUNDLEUP_API_KEY);- ✅ DO store API keys in environment variables
- ✅ DO use a secrets management service in production
- ✅ DO rotate API keys regularly
- ❌ DON'T commit API keys to version control
- ❌ DON'T hardcode API keys in your source code
- ❌ DON'T share API keys in public channels
Example .env file:
BUNDLEUP_API_KEY=bu_live_1234567890abcdefghijklmnopqrstuvwxyzLoading environment variables:
import 'dotenv/config'; // For Node.js projects
import { BundleUp } from '@bundleup/sdk';
const client = new BundleUp(process.env.BUNDLEUP_API_KEY);The Platform API provides access to core BundleUp features like managing connections and integrations. Use this API to list, retrieve, and delete connections, as well as discover available integrations.
The Proxy API allows you to make direct HTTP requests to the underlying integration's API through BundleUp. This is useful when you need access to integration-specific features not covered by the Unify API.
The Unify API provides a standardized, normalized interface across different integrations. For example, you can fetch chat channels from Slack, Discord, or Microsoft Teams using the same API call.
Manage your integration connections.
Retrieve a list of all connections in your account.
const connections = await client.connections.list();With query parameters:
const connections = await client.connections.list({
integration_id: 'int_slack',
limit: 50,
offset: 0,
external_id: 'user_123',
});Query Parameters:
integration_id(string): Filter by integration IDintegration_identifier(string): Filter by integration identifier (e.g., 'slack', 'github')external_id(string): Filter by external user/account IDlimit(number): Maximum number of results (default: 50, max: 100)offset(number): Number of results to skip for pagination
Response:
[
{
id: 'conn_123abc',
externalId: 'user_456',
integrationId: 'int_slack',
isValid: true,
createdAt: '2024-01-15T10:30:00Z',
updatedAt: '2024-01-20T14:22:00Z',
refreshedAt: '2024-01-20T14:22:00Z',
expiresAt: '2024-04-20T14:22:00Z',
},
// ... more connections
];Get details of a specific connection by ID.
const connection = await client.connections.retrieve('conn_123abc');Response:
{
id: 'conn_123abc',
externalId: 'user_456',
integrationId: 'int_slack',
isValid: true,
createdAt: '2024-01-15T10:30:00Z',
updatedAt: '2024-01-20T14:22:00Z',
refreshedAt: '2024-01-20T14:22:00Z',
expiresAt: '2024-04-20T14:22:00Z'
}Remove a connection from your account.
await client.connections.del('conn_123abc');Note: Deleting a connection will revoke access to the integration and cannot be undone.
Discover and work with available integrations.
Get a list of all available integrations.
const integrations = await client.integrations.list();With query parameters:
const integrations = await client.integrations.list({
status: 'active',
limit: 100,
offset: 0,
});Query Parameters:
status(string): Filter by status ('active', 'inactive', 'beta')limit(number): Maximum number of resultsoffset(number): Number of results to skip for pagination
Response:
[
{
id: 'int_slack',
identifier: 'slack',
name: 'Slack',
category: 'chat',
createdAt: '2023-01-01T00:00:00Z',
updatedAt: '2024-01-15T10:00:00Z',
},
// ... more integrations
];Get details of a specific integration.
const integration = await client.integrations.retrieve('int_slack');Response:
{
id: 'int_slack',
identifier: 'slack',
name: 'Slack',
category: 'chat',
createdAt: '2023-01-01T00:00:00Z',
updatedAt: '2024-01-15T10:00:00Z'
}Manage webhook subscriptions for real-time event notifications.
Get all registered webhooks.
const webhooks = await client.webhooks.list();With pagination:
const webhooks = await client.webhooks.list({
limit: 50,
offset: 0,
});Response:
[
{
id: 'webhook_123',
name: 'My Webhook',
url: 'https://example.com/webhook',
events: {
'connection.created': true,
'connection.deleted': true,
},
createdAt: '2024-01-15T10:30:00Z',
updatedAt: '2024-01-20T14:22:00Z',
lastTriggeredAt: '2024-01-20T14:22:00Z',
},
];Register a new webhook endpoint.
const webhook = await client.webhooks.create({
name: 'Connection Events Webhook',
url: 'https://example.com/webhook',
events: {
'connection.created': true,
'connection.deleted': true,
'connection.updated': true,
},
});Webhook Events:
connection.created- Triggered when a new connection is establishedconnection.deleted- Triggered when a connection is removedconnection.updated- Triggered when a connection is modified
Request Body:
{
name: string; // Friendly name for the webhook
url: string; // Your webhook endpoint URL
events: { // Events to subscribe to
[eventName: string]: boolean;
};
}Response:
{
id: 'webhook_123',
name: 'Connection Events Webhook',
url: 'https://example.com/webhook',
events: {
'connection.created': true,
'connection.deleted': true,
'connection.updated': true
},
createdAt: '2024-01-15T10:30:00Z',
updatedAt: '2024-01-15T10:30:00Z'
}Get details of a specific webhook.
const webhook = await client.webhooks.retrieve('webhook_123');Modify an existing webhook.
const updated = await client.webhooks.update('webhook_123', {
name: 'Updated Webhook Name',
url: 'https://example.com/new-webhook',
events: {
'connection.created': true,
'connection.deleted': false,
},
});Remove a webhook subscription.
await client.webhooks.del('webhook_123');When an event occurs, BundleUp sends a POST request to your webhook URL with the following payload:
{
"id": "evt_1234567890",
"type": "connection.created",
"created_at": "2024-01-15T10:30:00Z",
"data": {
"id": "conn_123abc",
"external_id": "user_456",
"integration_id": "int_slack",
"is_valid": true,
"created_at": "2024-01-15T10:30:00Z"
}
}To verify webhook signatures:
import crypto from 'crypto';
function verifyWebhookSignature(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(payload);
const digest = hmac.digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
// In your webhook handler
app.post('/webhook', (req, res) => {
const signature = req.headers['bundleup-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process the webhook
console.log('Webhook received:', req.body);
res.status(200).send('OK');
});Make direct HTTP requests to integration APIs through BundleUp.
const proxy = client.proxy('conn_123abc');const response = await proxy.get('/api/users');
const data = await response.json();
console.log(data);With custom headers:
const response = await proxy.get('/api/users', {
'X-Custom-Header': 'value',
Accept: 'application/json',
});const response = await proxy.post(
'/api/users',
JSON.stringify({
name: 'John Doe',
email: 'john@example.com',
role: 'developer',
}),
);
const newUser = await response.json();
console.log('Created user:', newUser);With custom headers:
const response = await proxy.post('/api/users', JSON.stringify({ name: 'John Doe' }), {
'Content-Type': 'application/json',
'X-API-Version': '2.0',
});const response = await proxy.put(
'/api/users/123',
JSON.stringify({
name: 'Jane Doe',
email: 'jane@example.com',
}),
);
const updatedUser = await response.json();const response = await proxy.patch(
'/api/users/123',
JSON.stringify({
email: 'newemail@example.com',
}),
);
const partiallyUpdated = await response.json();const response = await proxy.delete('/api/users/123');
if (response.ok) {
console.log('User deleted successfully');
}Sending form data:
const formData = new URLSearchParams();
formData.append('name', 'John Doe');
formData.append('email', 'john@example.com');
const response = await proxy.post('/api/users', formData.toString(), {
'Content-Type': 'application/x-www-form-urlencoded',
});Uploading files:
import FormData from 'form-data';
import fs from 'fs';
const form = new FormData();
form.append('file', fs.createReadStream('document.pdf'));
form.append('title', 'My Document');
const response = await proxy.post('/api/documents', form, form.getHeaders());const response = await proxy.get('/api/files/download/123');
const buffer = await response.arrayBuffer();
fs.writeFileSync('downloaded-file.pdf', Buffer.from(buffer));Access unified, normalized data across different integrations with a consistent interface.
const unify = client.unify('conn_123abc');The Chat API provides a unified interface for chat platforms like Slack, Discord, and Microsoft Teams.
Retrieve a list of channels from the connected chat platform.
const result = await unify.chat.channels({
limit: 100,
after: null,
include_raw: false,
});
console.log('Channels:', result.data);
console.log('Next cursor:', result.metadata.next);Parameters:
limit(number, optional): Maximum number of channels to return (default: 100, max: 1000)after(string, optional): Pagination cursor from previous responseinclude_raw(boolean, optional): Include raw API response from the integration (default: false)
Response:
{
data: [
{
id: 'C1234567890',
name: 'general'
},
{
id: 'C0987654321',
name: 'engineering'
}
],
metadata: {
next: 'cursor_abc123' // Use this for pagination
},
_raw?: { // Only present if include_raw: true
// Original response from the integration API
}
}Pagination example:
let allChannels = [];
let cursor = null;
do {
const result = await unify.chat.channels({
limit: 100,
after: cursor,
});
allChannels = [...allChannels, ...result.data];
cursor = result.metadata.next;
} while (cursor);
console.log(`Fetched ${allChannels.length} total channels`);The Git API provides a unified interface for version control platforms like GitHub, GitLab, and Bitbucket.
const result = await unify.git.repos({
limit: 50,
after: null,
include_raw: false,
});
console.log('Repositories:', result.data);Response:
{
data: [
{
id: '123456',
name: 'my-awesome-project',
full_name: 'organization/my-awesome-project',
description: 'An awesome project',
url: 'https://github.com/organization/my-awesome-project',
created_at: '2023-01-15T10:30:00Z',
updated_at: '2024-01-20T14:22:00Z',
pushed_at: '2024-01-20T14:22:00Z'
}
],
metadata: {
next: 'cursor_xyz789'
}
}const result = await unify.git.pulls('organization/repo-name', {
limit: 20,
after: null,
include_raw: false,
});
console.log('Pull Requests:', result.data);Parameters:
repoName(string, required): Repository name in the format 'owner/repo'limit(number, optional): Maximum number of PRs to returnafter(string, optional): Pagination cursorinclude_raw(boolean, optional): Include raw API response
Response:
{
data: [
{
id: '12345',
number: 42,
title: 'Add new feature',
description: 'This PR adds an awesome new feature',
draft: false,
state: 'open',
url: 'https://github.com/org/repo/pull/42',
user: 'john-doe',
created_at: '2024-01-15T10:30:00Z',
updated_at: '2024-01-20T14:22:00Z',
merged_at: null
}
],
metadata: {
next: null
}
}const result = await unify.git.tags('organization/repo-name', {
limit: 50,
});
console.log('Tags:', result.data);Response:
{
data: [
{
name: 'v1.0.0',
commit_sha: 'abc123def456'
},
{
name: 'v0.9.0',
commit_sha: 'def456ghi789'
}
],
metadata: {
next: null
}
}const result = await unify.git.releases('organization/repo-name', {
limit: 10,
});
console.log('Releases:', result.data);Response:
{
data: [
{
id: '54321',
name: 'Version 1.0.0',
tag_name: 'v1.0.0',
description: 'Initial release with all the features',
prerelease: false,
url: 'https://github.com/org/repo/releases/tag/v1.0.0',
created_at: '2024-01-15T10:30:00Z',
released_at: '2024-01-15T10:30:00Z'
}
],
metadata: {
next: null
}
}The PM API provides a unified interface for project management platforms like Jira, Linear, and Asana.
const result = await unify.pm.issues({
limit: 100,
after: null,
include_raw: false,
});
console.log('Issues:', result.data);Response:
{
data: [
{
id: 'PROJ-123',
url: 'https://jira.example.com/browse/PROJ-123',
title: 'Fix login bug',
status: 'in_progress',
description: 'Users are unable to log in',
created_at: '2024-01-15T10:30:00Z',
updated_at: '2024-01-20T14:22:00Z'
}
],
metadata: {
next: 'cursor_def456'
}
}Filtering and sorting:
const openIssues = result.data.filter(issue => issue.status === 'open');
const sortedByDate = result.data.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));The SDK throws standard JavaScript errors with descriptive messages. Always wrap SDK calls in try-catch blocks for proper error handling.
try {
const connections = await client.connections.list();
} catch (error) {
console.error('Failed to fetch connections:', error.message);
}If you're still experiencing issues:
- Check the BundleUp Documentation
- Search GitHub Issues
- Contact support@bundleup.io
When reporting issues, please include:
- SDK version (
@bundleup/sdkversion from package.json) - Node.js version (
node --version) - Minimal code to reproduce the issue
- Full error message and stack trace
# Clone the repository
git clone https://github.com/bundleup/bundleup-sdk-js.git
cd bundleup-sdk-js/packages/sdk
# Install dependencies
npm install
# Build the package
npm run build
# Run tests
npm test
# Watch mode for development
npm run devsrc/
├── index.ts # Main entry point
├── proxy.ts # Proxy API implementation
├── unify.ts # Unify API implementation
├── utils.ts # Utility functions
├── resources/
│ ├── base.ts # Base resource class
│ ├── connection.ts # Connections API
│ ├── integration.ts # Integrations API
│ └── webhooks.ts # Webhooks API
├── unify/
│ ├── base.ts # Base Unify class
│ ├── chat.ts # Chat Unify API
│ ├── git.ts # Git Unify API
│ └── pm.ts # PM Unify API
└── __tests__/ # Test files
# Run all tests
npm test
# Run tests in watch mode
npm test -- --watch
# Run specific test file
npm test -- proxy.test.ts
# Run with coverage
npm test -- --coverage# Build for production
npm run build
# Clean build artifacts
npm run clean
# Build and watch for changes
npm run dev# Run ESLint
npm run lint
# Fix linting issues
npm run lint -- --fixWe welcome contributions to the BundleUp JavaScript SDK! Here's how you can help:
- Check if the bug has already been reported in GitHub Issues
- If not, create a new issue with:
- Clear title and description
- Steps to reproduce
- Expected vs actual behavior
- SDK version and environment details
- Open a new issue with the "feature request" label
- Describe the feature and its use case
- Explain why this feature would be useful
- Fork the repository
- Create a new branch:
git checkout -b feature/my-new-feature - Make your changes
- Write or update tests
- Ensure all tests pass:
npm test - Commit your changes:
git commit -am 'Add new feature' - Push to the branch:
git push origin feature/my-new-feature - Submit a pull request
- Follow the existing code style
- Add tests for new features
- Update documentation for API changes
- Keep commits focused and atomic
- Write clear commit messages
This package is available as open source under the terms of the ISC License.
Copyright (c) 2026 BundleUp
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Everyone interacting in the BundleUp project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
Made with ❤️ by the BundleUp team