> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bundleup.io/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript

## Installation

Install the SDK using your preferred package manager:

**npm:**

```bash theme={null}
npm install @bundleup/sdk
```

**yarn:**

```bash theme={null}
yarn add @bundleup/sdk
```

**pnpm:**

```bash theme={null}
pnpm add @bundleup/sdk
```

## Requirements

* **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

### Node.js Compatibility

For Node.js versions below 18, you'll need to install a fetch polyfill:

```bash theme={null}
npm install node-fetch
```

Then import it before using the SDK:

```javascript theme={null}
import fetch from 'node-fetch';

globalThis.fetch = fetch;
```

## Features

* 🚀 **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

## Quick Start

Get started with BundleUp in just a few lines of code:

```javascript theme={null}
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);
```

## Authentication

The BundleUp SDK uses API keys for authentication. You can obtain your API key from the [BundleUp Dashboard](https://app.bundleup.io).

### Getting Your API Key

1. Sign in to your [BundleUp Dashboard](https://app.bundleup.io)
2. Navigate to **API Keys**
3. Click **Create API Key**
4. Copy your API key and store it securely

### Initializing the SDK

```javascript theme={null}
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);
```

### Security Best Practices

* ✅ **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:`**

```bash theme={null}
BUNDLEUP_API_KEY=bu_live_1234567890abcdefghijklmnopqrstuvwxyz
```

**Loading environment variables:**

```javascript theme={null}
// For Node.js projects
import { BundleUp } from '@bundleup/sdk';
import 'dotenv/config';

const client = new BundleUp(process.env.BUNDLEUP_API_KEY);
```

## Core Concepts

### Platform API

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.

### Proxy API

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.

### 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.

## API Reference

### Connections

Manage your integration connections.

#### List Connections

Retrieve a list of all connections in your account.

```javascript theme={null}
const connections = await client.connections.list();
```

**With query parameters:**

```javascript theme={null}
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 ID
* `integration_identifier` (string): Filter by integration identifier (e.g., 'slack', 'github')
* `external_id` (string): Filter by external user/account ID
* `limit` (number): Maximum number of results (default: 50, max: 100)
* `offset` (number): Number of results to skip for pagination

**Response:**

```typescript theme={null}
[
  {
    id: 'conn_123abc',
    externalId: 'user_456',
    integrationId: 'int_slack',
    createdAt: '2024-01-15T10:30:00Z',
    updatedAt: '2024-01-20T14:22:00Z',
    expiresAt: '2024-04-20T14:22:00Z',
  },
  // ... more connections
];
```

#### Retrieve a Connection

Get details of a specific connection by ID.

```javascript theme={null}
const connection = await client.connections.retrieve('conn_123abc');
```

**Response:**

```typescript theme={null}
{
  id: 'conn_123abc',
  externalId: 'user_456',
  integrationId: 'int_slack',
  createdAt: '2024-01-15T10:30:00Z',
  updatedAt: '2024-01-20T14:22:00Z',
  expiresAt: '2024-04-20T14:22:00Z'
}
```

#### Delete a Connection

Remove a connection from your account.

```javascript theme={null}
await client.connections.del('conn_123abc');
```

**Note:** Deleting a connection will revoke access to the integration and cannot be undone.

### Integrations

Discover and work with available integrations.

#### List Integrations

Get a list of all available integrations.

```javascript theme={null}
const integrations = await client.integrations.list();
```

**With query parameters:**

```javascript theme={null}
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 results
* `offset` (number): Number of results to skip for pagination

**Response:**

```typescript theme={null}
[
  {
    id: 'int_slack',
    identifier: 'slack',
    name: 'Slack',
    category: 'chat',
    createdAt: '2023-01-01T00:00:00Z',
    updatedAt: '2024-01-15T10:00:00Z',
  },
  // ... more integrations
];
```

#### Retrieve an Integration

Get details of a specific integration.

```javascript theme={null}
const integration = await client.integrations.retrieve('int_slack');
```

**Response:**

```typescript theme={null}
{
  id: 'int_slack',
  identifier: 'slack',
  name: 'Slack',
  category: 'chat',
  createdAt: '2023-01-01T00:00:00Z',
  updatedAt: '2024-01-15T10:00:00Z'
}
```

### Webhooks

Manage webhook subscriptions for real-time event notifications.

#### List Webhooks

Get all registered webhooks.

```javascript theme={null}
const webhooks = await client.webhooks.list();
```

**With pagination:**

```javascript theme={null}
const webhooks = await client.webhooks.list({
  limit: 50,
  offset: 0,
});
```

**Response:**

```typescript theme={null}
[
  {
    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',
  },
];
```

#### Create a Webhook

Register a new webhook endpoint.

```javascript theme={null}
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 established
* `connection.deleted` - Triggered when a connection is removed
* `connection.updated` - Triggered when a connection is modified

**Request Body:**

```typescript theme={null}
{
  name: string;           // Friendly name for the webhook
  url: string;            // Your webhook endpoint URL
  events: {               // Events to subscribe to
    [eventName: string]: boolean;
  };
}
```

**Response:**

```typescript theme={null}
{
  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'
}
```

#### Retrieve a Webhook

Get details of a specific webhook.

```javascript theme={null}
const webhook = await client.webhooks.retrieve('webhook_123');
```

#### Update a Webhook

Modify an existing webhook.

```javascript theme={null}
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,
  },
});
```

#### Delete a Webhook

Remove a webhook subscription.

```javascript theme={null}
await client.webhooks.del('webhook_123');
```

#### Webhook Payload Example

When an event occurs, BundleUp sends a POST request to your webhook URL with the following payload:

```json theme={null}
{
  "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",
    "created_at": "2024-01-15T10:30:00Z"
  }
}
```

#### Webhook Security

To verify webhook signatures:

```javascript theme={null}
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');
});
```

### Proxy API

Make direct HTTP requests to integration APIs through BundleUp.

#### Creating a Proxy Instance

```javascript theme={null}
const proxy = client.proxy('conn_123abc');
```

#### GET Request

```javascript theme={null}
const response = await proxy.get('/api/users');
const data = await response.json();
console.log(data);
```

**With custom headers:**

```javascript theme={null}
const response = await proxy.get('/api/users', {
  'X-Custom-Header': 'value',
  Accept: 'application/json',
});
```

#### POST Request

```javascript theme={null}
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:**

```javascript theme={null}
const response = await proxy.post(
  '/api/users',
  JSON.stringify({ name: 'John Doe' }),
  {
    'Content-Type': 'application/json',
    'X-API-Version': '2.0',
  },
);
```

#### PUT Request

```javascript theme={null}
const response = await proxy.put(
  '/api/users/123',
  JSON.stringify({
    name: 'Jane Doe',
    email: 'jane@example.com',
  }),
);

const updatedUser = await response.json();
```

#### PATCH Request

```javascript theme={null}
const response = await proxy.patch(
  '/api/users/123',
  JSON.stringify({
    email: 'newemail@example.com',
  }),
);

const partiallyUpdated = await response.json();
```

#### DELETE Request

```javascript theme={null}
const response = await proxy.delete('/api/users/123');

if (response.ok) {
  console.log('User deleted successfully');
}
```

#### Working with Different Content Types

**Sending form data:**

```javascript theme={null}
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:**

```javascript theme={null}
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());
```

#### Handling Binary Data

```javascript theme={null}
const response = await proxy.get('/api/files/download/123');
const buffer = await response.arrayBuffer();
fs.writeFileSync('downloaded-file.pdf', Buffer.from(buffer));
```

### Unify API

Access unified, normalized data across different integrations with a consistent interface.

#### Creating a Unify Instance

```javascript theme={null}
const unify = client.unify('conn_123abc');
```

#### Chat API

The Chat API provides a unified interface for chat platforms like Slack, Discord, and Microsoft Teams.

##### List Channels

Retrieve a list of channels from the connected chat platform.

```javascript theme={null}
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 response
* `include_raw` (boolean, optional): Include raw API response from the integration (default: false)

**Response:**

```typescript theme={null}
{
  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:**

```javascript theme={null}
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`);
```

#### Git API

The Git API provides a unified interface for version control platforms like GitHub, GitLab, and Bitbucket.

##### List Repositories

```javascript theme={null}
const result = await unify.git.repos({
  limit: 50,
  after: null,
  include_raw: false,
});

console.log('Repositories:', result.data);
```

**Response:**

```typescript theme={null}
{
  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'
  }
}
```

##### List Pull Requests

```javascript theme={null}
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 return
* `after` (string, optional): Pagination cursor
* `include_raw` (boolean, optional): Include raw API response

**Response:**

```typescript theme={null}
{
  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
  }
}
```

##### List Tags

```javascript theme={null}
const result = await unify.git.tags('organization/repo-name', {
  limit: 50,
});

console.log('Tags:', result.data);
```

**Response:**

```typescript theme={null}
{
  data: [
    {
      name: 'v1.0.0',
      commit_sha: 'abc123def456'
    },
    {
      name: 'v0.9.0',
      commit_sha: 'def456ghi789'
    }
  ],
  metadata: {
    next: null
  }
}
```

##### List Releases

```javascript theme={null}
const result = await unify.git.releases('organization/repo-name', {
  limit: 10,
});

console.log('Releases:', result.data);
```

**Response:**

```typescript theme={null}
{
  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
  }
}
```

#### Project Management API

The PM API provides a unified interface for project management platforms like Jira, Linear, and Asana.

##### List Issues

```javascript theme={null}
const result = await unify.pm.issues({
  limit: 100,
  after: null,
  include_raw: false,
});

console.log('Issues:', result.data);
```

**Response:**

```typescript theme={null}
{
  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:**

```javascript theme={null}
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),
);
```

## Error Handling

The SDK throws standard JavaScript errors with descriptive messages. Always wrap SDK calls in try-catch blocks for proper error handling.

```javascript theme={null}
try {
  const connections = await client.connections.list();
} catch (error) {
  console.error('Failed to fetch connections:', error.message);
}
```
