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

# Unified API Conventions

> Base URL, headers, response envelope, pagination, raw responses, provider parameters and errors: the rules every Unified API endpoint follows.

## Base URL and headers

```text theme={null}
https://unify.bundleup.io/v1
```

| Header | Required | Value |
| - | - | - |
| `Authorization` | Yes | `Bearer YOUR_API_KEY` |
| `BU-Connection-Id` | Yes | The connection to act on |
| `BU-Max-Retries` | No | `0` to `5`. See [Retries](/docs/guides/proxy/retries) |

The Unified API needs the `BU-Connection-Id` header. Unlike the Proxy and MCP, it doesn’t accept the connection ID appended to the API key.

## Response envelope

Successful responses are JSON with a `data` field. List endpoints also have `metadata`:

```json theme={null}
{
  "data": [ { "id": "C024BE91L", "name": "general" } ],
  "metadata": { "next": "dGVhbS0y" }
}
```

Single-object endpoints (`/v1/me`, `/v1/tasks/tasks/{id}`) return `data` as an object and no `metadata`.

### Null vs missing

Every field in the schema is always present. When a provider doesn’t have a value, the field is `null` rather than missing, so your code can rely on the shape.

### Dates

Timestamps are ISO 8601 strings. All-day calendar events use a `YYYY-MM-DD` date instead. See [Calendar](/docs/guides/unified/calendar).

### IDs

IDs are the provider’s own, passed through. Most are strings. A few are numbers where the provider uses numbers (GitHub and GitLab repository IDs, for example). Treat IDs as opaque values.

## Pagination

List endpoints are cursor-paginated:

| Parameter | Description |
| - | - |
| `limit` | Items per page, `1` to `100`. Default `100`. Some providers cap pages lower. |
| `after` | The cursor from the previous page’s `metadata.next`. Omit it for the first page. |

`metadata.next` is `null` on the last page. Cursors are opaque: don’t build or modify them.

<CodeGroup>
  ```bash cURL theme={null}
  # First page
  curl "https://unify.bundleup.io/v1/tasks/tasks?limit=50" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "BU-Connection-Id: conn_abc123"

  # Next page
  curl "https://unify.bundleup.io/v1/tasks/tasks?limit=50&after=Mg==" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "BU-Connection-Id: conn_abc123"
  ```

  ```typescript JavaScript theme={null}
  const unify = client.unify('conn_abc123');
  const tasks = [];
  let after = null;

  do {
    const page = await unify.tasks.tasks({ limit: 50, after });
    tasks.push(...page.data);
    after = page.metadata.next;
  } while (after);
  ```

  ```python Python theme={null}
  unify = client.unify("conn_abc123")
  tasks, after = [], None

  while True:
      page = unify.tasks.tasks({"limit": 50, "after": after})
      tasks.extend(page["data"])
      after = page["metadata"]["next"]
      if not after:
          break
  ```
</CodeGroup>

Each page is one request toward the connection’s [rate limit](/docs/guides/proxy/rate-limits).

## Include the raw response

Add `include_raw=true` to any endpoint to receive the provider’s original response in a `_raw` field, next to the normalized data:

```bash theme={null}
curl "https://unify.bundleup.io/v1/me?include_raw=true" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "BU-Connection-Id: conn_abc123"
```

```json theme={null}
{
  "data": { "id": "583231", "name": "The Octocat", "email": "octocat@github.com", "avatar_url": "https://..." },
  "_raw": { "login": "octocat", "id": 583231, "company": "@github", "...": "..." }
}
```

Use it when you need one or two provider-specific fields without switching that call to the Proxy. `_raw` is the provider’s format, so code that reads it is provider-specific.

## Provider parameters

List endpoints accept a `params` query parameter: a JSON object whose keys are added to the provider request’s query string. Use it to pass provider-specific filters the Unified API doesn’t model:

```bash theme={null}
# Ask GitHub for open pull requests only (GitHub's "state" parameter)
curl -G "https://unify.bundleup.io/v1/git/repos/octocat%2Fhello-world/pulls" \
  --data-urlencode 'params={"state":"open"}' \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "BU-Connection-Id: conn_abc123"
```

The keys and values are the provider’s, so they only make sense for that provider. Invalid JSON is ignored.

## Errors

The Unified API returns errors in this envelope:

```json theme={null}
{
  "success": false,
  "errors": [{ "code": 1000, "message": "Not Authorized" }]
}
```

| Status | When |
| - | - |
| `400` | A required header or parameter is missing or invalid, for example no API key, or a calendar window that isn’t ISO 8601. |
| `401` | The API key is unknown. Code `1000`. |
| `404` | The connection doesn’t exist or isn’t in this workspace, or the connection’s provider doesn’t support this endpoint. |
| `429` | The connection is over its [rate limit](/docs/guides/proxy/rate-limits). Code `1001`. |
| `500` | Something went wrong in BundleUp. |

**Provider errors are passed through unchanged.** If the provider answers with an error (a `403` for a missing scope, a `404` for a repository that doesn’t exist), you get the provider’s status, headers and body, not the envelope above. Check `success` or the content type before assuming which kind you have.

More detail in the [errors reference](/docs/api-reference/errors).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.