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

# Authentication

> Authenticate every request with a Bearer API key.

Every iBubbl API request must include a Bearer API key in the `Authorization` header. Requests without a valid key return `401 unauthorized`.

## Header format

```http theme={null}
Authorization: Bearer ib_pk_<64 hex chars>
```

Example:

```http theme={null}
Authorization: Bearer ib_pk_a3f2c8d9e0f1b4d52c8a1f4e23b9d4f7ca8e51d2c3b4a5e6f7a8b9c0d1e2f3a4
```

The token always starts with the literal prefix `ib_pk_` followed by 64 lowercase hexadecimal characters. The full token is 70 characters.

## Generating a key

1. Open your dashboard at [ibubbl-dashboard.vercel.app](https://ibubbl-dashboard.vercel.app).
2. Navigate to **Settings → API Access**.
3. Click **Create key** and give it a descriptive name (e.g., "production-backend", "zapier-integration").
4. Copy the key from the modal that appears. **This is the only time the full secret is shown.** If you lose it, revoke the key and create a new one.

The dashboard displays the key's prefix (first 14 characters, e.g. `ib_pk_a3f2c8d9`) on the list view so you can identify which key is which without revealing the full secret.

## Make an authenticated request

Keep the key in the `IBUBBL_API_KEY` environment variable; every example in these docs reads it from there.

```bash theme={null}
export IBUBBL_API_KEY="ib_pk_..."
```

Listing your webhook subscriptions is a safe first call: it only reads, and sends nothing. A `200` with a list (`[]` if you have none) means the key works.

```bash cURL theme={null}
curl https://api.ibubbl.com/v1/webhooks \
  -H "Authorization: Bearer $IBUBBL_API_KEY"
```

```typescript TypeScript theme={null}
const res = await fetch("https://api.ibubbl.com/v1/webhooks", {
  headers: { Authorization: `Bearer ${process.env.IBUBBL_API_KEY}` },
  signal: AbortSignal.timeout(10_000),
});

const data = await res.json();
if (!res.ok) throw new Error(`${res.status} ${data.detail?.error}`);
console.log(`key works: ${data.length} subscription(s)`);
```

```python Python theme={null}
import os

import requests

API_KEY = os.environ["IBUBBL_API_KEY"]

res = requests.get(
    "https://api.ibubbl.com/v1/webhooks",
    headers={"Authorization": f"Bearer {API_KEY}"},
    timeout=10,
)
data = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {data['detail']['error']}")
print(f"key works: {len(data)} subscription(s)")
```

To explore the API in Postman or Insomnia instead, import the OpenAPI file: see [OpenAPI, Postman and Insomnia](https://docs.ibubbl.com/openapi.md).

## Revocation and rotation

To revoke a key, click **Revoke** next to it in the dashboard. Revocation is immediate — subsequent requests with that key return `401 unauthorized`.

**Recommended rotation cadence:**

* Rotate any key whose holder has changed (employee offboarding, vendor change).
* Rotate at least annually for long-lived integrations.
* Rotate immediately if a key may have been exposed (committed to a repo, posted in chat, etc.).

To rotate without downtime:

1. Create a new key.
2. Deploy the new key to your integration.
3. Verify traffic is flowing using the new key (check the dashboard's "last used" timestamp on each key).
4. Revoke the old key.

## Security best practices

> [!WARNING]
>
> **Never commit API keys to source control.** Use environment variables, a secrets manager, or your platform's secret-injection system. If a key is ever exposed, revoke it in the dashboard right away and issue a new one.

* **Server-side only.** Never embed an iBubbl API key in a mobile app, browser bundle, or any client-controlled environment. The key has full account access.
* **One key per integration.** Issue a separate key per service or environment so you can revoke one without breaking the others.
* **Least-privilege isn't a thing yet.** API keys carry full account access; scoped keys are on the roadmap.

## Authentication errors

A `401 unauthorized` response means one of:

* Missing `Authorization` header.
* Header doesn't start with `Bearer ` (note the space).
* Token doesn't start with `ib_pk_`.
* Token has been revoked, or never existed.

Response shape:

```json theme={null}
{
  "detail": {
    "error": "unauthorized",
    "message": "API key required (use Authorization: Bearer ib_pk_…)"
  }
}
```

## Suspended accounts

A valid key on a suspended or cancelled account returns `403 forbidden`:

```json theme={null}
{
  "detail": {
    "error": "forbidden",
    "message": "Account is suspended"
  }
}
```

(`"Account is cancelled"` for a cancelled account.) Retrying won't help; email [support@ibubbl.com](mailto:support@ibubbl.com).

See the [Error Reference](https://docs.ibubbl.com/errors.md) for the full code catalog.


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