---
name: bamf-integration
description: Integrate BAMF into your product — LinkedIn/X post drafting, scheduling, and analytics for your users, attributed per workspace. Covers MCP, REST, auth, scopes, and a worked example.
---

# Integrating BAMF into your product

You are a coding agent. A human pointed you at this file because they want **BAMF** reachable from
their own product. Read the whole file before writing code — the attribution section changes how you
structure the calling code, so writing the plumbing first means rewriting it.

**What BAMF exposes:** a REST API and an MCP server over the same account — LinkedIn/X post drafting
and generation, scheduling, engagement boost, ideas, media, knowledge, and analytics, scoped to a
BAMF creator space. You (the integrator) hold one API key per BAMF workspace; your own users are the
customers of your product, not of BAMF directly.

---

## 1. Pick an integration method

| Method | Use when | Shape |
|---|---|---|
| **REST** `https://api.bamf.ai/v1` | your backend already makes HTTP calls — the default for a product | you call the resource, BAMF acts inside the creator space the key belongs to |
| **MCP** `https://mcp.bamf.ai` | your agent runtime speaks MCP and you want tool discovery | JSON-RPC over streamable HTTP, same bearer auth |

REST resources you will reach for most: `/v1/creator-spaces`, `/v1/posts`, `/v1/posts/:id/schedule`,
`/v1/ideas`, `/v1/analytics/summary`, `/v1/media`, `/v1/knowledge`, `/v1/boost/*`, `/v1/jobs`. Full
schema: https://bamf.ai/openapi.json.

MCP tools mirror the REST surface one-to-one (dotted names, e.g. `bamf.create_post_draft`,
`bamf.schedule_post`, `bamf.get_analytics_summary`) — see the connect page for tool discovery and
https://bamf.ai/docs/mcp/tools for the full list.

## 2. Auth

Every call — REST or MCP — carries:

```
Authorization: Bearer <bamf_api_key>
```

A key is created at https://bamf.ai/settings/api-keys and is scoped to one workspace and one or
more creator spaces inside it. There is no separate app-level credential: the key you hold **is** the
tenant boundary. Never put a raw key in a prompt an LLM fills in — set it once at your own call site,
the same place you already set your own auth headers.

```ts
// the ONE place your product talks to BAMF
async function bamfCall(path: string, init: RequestInit = {}) {
  const r = await fetch(`https://api.bamf.ai/v1${path}`, {
    ...init,
    headers: {
      ...init.headers,
      Authorization: `Bearer ${process.env.BAMF_API_KEY}`,
      'Content-Type': 'application/json',
    },
  });
  if (!r.ok) throw new Error(`BAMF ${path} failed: ${r.status}`);
  return r.json();
}
```

## 3. Scopes

Keys carry explicit scopes, checked per route — a key without `posts:schedule` cannot schedule even
if it can draft. Common scopes: `creator_spaces:read`, `posts:write`, `posts:schedule`,
`posts:publish`, `posts:boost`, `posts:delete`, `ideas:write`, `ideas:delete`, `members:write`,
`analytics:sync`, `ai:generate`. Request the narrowest set your integration needs; full list at
https://bamf.ai/docs/scopes.

## 4. Per-customer usage attribution

BAMF's tenant boundary is the **creator space**, not a request header. If your product serves several
of your own customers against one BAMF workspace, attribute usage on your side, honestly:

- **Preferred — one BAMF workspace per customer.** Mint one API key per customer; every call you make
  on their behalf already carries the right key, so attribution is exact and BAMF-side.
- **Shared workspace, several creator spaces.** Select the matching creator space per call
  (`GET /v1/creator-spaces`, then pass its id) and log that id against your own customer record. BAMF
  does not accept a customer-id header today, so this join lives in your own database, keyed by
  `creator_space_id` and BAMF's `request_id` (every response carries one; log it).
- Every mutating call should send an `Idempotency-Key` you generate — a retry replays the stored
  result instead of double-drafting or double-scheduling.

Do not claim a per-call billing header exists — it does not. State this to your own users as: "usage
is metered per BAMF API key/workspace; your product's own database joins that usage to your
customers."

## 5. Rate limits

Enforced per API key, per minute, by request category: `read` 600, `write` 120, `delete` 30,
`generate` 30, `publish` 10. A `429` with `error: "rate_limited"` means back off and retry with
backoff — check `Retry-After` if present. Scheduling and publishing share the tightest bucket
(`publish`), so batch those calls rather than firing them in a loop.

## 6. Worked example — draft, schedule, read analytics

```bash
# 1. Find the creator space this key can act in
curl https://api.bamf.ai/v1/creator-spaces -H "Authorization: Bearer $BAMF_API_KEY"

# 2. Create a post draft
curl -X POST https://api.bamf.ai/v1/posts \
  -H "Authorization: Bearer $BAMF_API_KEY" -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"creator_id":"<creator_id>","content":"Draft text here","post_type":"text"}'

# 3. Schedule it
curl -X POST https://api.bamf.ai/v1/posts/<post_id>/schedule \
  -H "Authorization: Bearer $BAMF_API_KEY" -H "Content-Type: application/json" \
  -d '{"scheduled_at":"2026-10-01T14:00:00Z"}'

# 4. Read analytics once it has run
curl "https://api.bamf.ai/v1/analytics/summary?creator_id=<creator_id>" \
  -H "Authorization: Bearer $BAMF_API_KEY"
```

The same three calls over MCP: `bamf.list_creator_spaces` → `bamf.create_post_draft` →
`bamf.schedule_post` → `bamf.get_analytics_summary`.

---

## Before you ship

- [ ] One function wraps every BAMF call and sets `Authorization` from your own request context.
- [ ] Mutating calls send `Idempotency-Key`.
- [ ] Your usage log keys off BAMF's `request_id` and the `creator_space_id`/workspace the key
      belongs to — not an invented customer header.
- [ ] You handle `429 rate_limited` with backoff instead of surfacing it raw.
- [ ] Scopes requested match what your integration actually does — nothing broader.
- [ ] You never publish or schedule without your own product's explicit user approval step.

## Call a registered tool

A workspace's team vault registers upstream APIs (Your vault → Skills & tools). Call one with your
BAMF developer key; the server injects the tool's stored secret, so the upstream key never reaches
your machine or your agent's context. A developer key can only `call` — listing, adding or
removing secrets and tools takes a signed-in teammate — and only when the key carries the
`tools:call` scope.

```bash
curl -X POST https://ubetjzaarcuwiafglxtq.supabase.co/functions/v1/workspace-secrets \
  -H "Authorization: Bearer $BAMF_API_KEY" -H "Content-Type: application/json" \
  -d '{"action":"call","workspace_id":"<workspace_id>","tool_id":"<tool_id>","path":"/v1/people?page=1","method":"GET"}'
```

Body: `action` (`"call"`), `workspace_id`, `tool_id`, `path` (relative to the tool's base URL,
query string included), `method` (`GET`/`POST`/`PUT`/`PATCH`/`DELETE`, default `GET`), optional
`body` (JSON or a string, up to 200 KB) and `headers` (only `accept` and `content-type`). The key's
owner must be a member of `workspace_id`. Returns `{ status, headers, body, truncated }` with the
secret scrubbed from the body. The call goes only to the host the secret is bound to, over https,
never follows redirects, times out at 15 s and caps the response at 1 MB. Refusals carry a fixed
`reason`, e.g. `host_not_allowed`, `private_address`, `redirect_blocked`, `developer_key_call_only`,
`scope_required`.
