---
name: flow-capi
title: Flow Customer API (CAPI)
version: 2026.05
description: Operational context for any LLM building against the Flow Customer API. Bundles auth, conventions, safety contract, and pointers to the live OpenAPI spec, best-practices guide, changelog, and announcements feed.
homepage: https://developer.joinflow.com/capi
spec: https://developer.joinflow.com/api/spec/capi
license: proprietary
---

# Flow Customer API (CAPI) — agent skill

> Agent-agnostic Markdown. Drop into Claude Skills, a custom GPT, a
> local model, or any LLM that reads text. The YAML frontmatter is
> consumed by Anthropic tooling — every other agent should ignore it.

## 1. Identity

This is the **Flow Customer API (CAPI)** — a REST/JSON API owned
by a single Flow customer. Use it when you are:

- Automating one organisation's own Flow workflows.
- Embedding Flow functionality (calling, SMS, presence, queues,
  call history, recordings) in an internal tool or product.
- Provisioning users, credentials, queue memberships, profiles, and
  PBX resources for that one customer.

## 2. Base URL

Canonical:

```
https://admin.joinflow.com/api/capi
```

## 3. Authentication

Bearer JWT, sent on every request:

```
Authorization: Bearer <jwt>
```

The token is **user-supplied**. Minted by the end user from:

```
Flow web or desktop app → My Account → Username and Password → Manage tokens
```

The token is shown **once** at creation. It carries the minting user's
permissions, remains valid across password changes, and is revoked
from the same Manage tokens screen.

**Tooling quirk.** The CAPI spec is Swagger 2.0 and declares
`BEARER_TOKEN` as `type: apiKey` (header `Authorization`) — not as
`type: http` with scheme `bearer`. Many generators read this
literally and will not auto-prefix `Bearer ` for you. **Always
include the literal `Bearer ` prefix yourself.** Sending the raw
JWT, or any other auth scheme, will fail authentication.

## 4. Conventions

- **Transport.** REST over HTTPS. JSON request and response bodies.
  UTF-8 everywhere.
- **Versioning.** Paths are versioned in the URL (`/v1/...`). Pin the
  version your client targets and watch the changelog (see §5).
- **Pagination and filtering.** Most list endpoints accept query
  parameters — commonly `limit`, `offset`, date ranges (`fromDate`,
  `toDate`), and resource filters (`invoice-place`, `callType`, etc.).
  Walk pages sequentially; do not fan-out paginate.
- **Phone numbers.** Use E.164 (`+46701234567`) in request bodies.
  Example payloads in the spec use Nordic numbers.
- **Errors.** Most error responses today are `400` (client problem,
  do not retry) or `5xx` (server problem, retry with exponential
  backoff + jitter). More granular codes are rolling out — dispatch
  on status code, not response body parsing.
- **Retries.** Exponential backoff + jitter for `5xx`. Honour
  `Retry-After` literally on `429`. Do not retry `4xx`. Be careful
  retrying writes — a timed-out request may still have been applied.

## 5. Live references (fetch these — do not assume from memory)

| What | URL |
| --- | --- |
| OpenAPI spec (Swagger 2.0, current) | https://developer.joinflow.com/api/spec/capi |
| Interactive reference (Scalar UI) | https://developer.joinflow.com/capi/reference |
| Best-practices, single-fetch for agents | https://developer.joinflow.com/llms-full.txt |
| Spec changelog (machine-generated diff) | https://developer.joinflow.com/capi/changelog |
| Announcements (machine-readable JSON) | https://developer.joinflow.com/announcements.json |
| Identify-yourself guidance (User-Agent) | https://developer.joinflow.com/best-practices/identify |

At session start, fetch `/announcements.json` and skim
`/capi/changelog` for breaking changes or deprecations announced
since this skill's `version:` date.

## 6. Safety / agent contract

**Tokens.** User-supplied. Never store, never log, never paste a JWT
verbatim into chat output, summaries, or error messages. If the user
shares one, use it for the immediate request and do not echo it back.

**Reads vs. writes.** `GET` requests are autonomous-safe. `POST`,
`PUT`, `PATCH`, `DELETE` mutate customer data — they dial real
phones, send real SMS, change billing-relevant configuration.
**Require explicit user confirmation before any mutating request**,
with a preview of method, URL, and body. Wait for "yes".

**Rate limits.** Honour any `Retry-After` literally. Exponential
backoff with jitter on `5xx`. Cap at 3–5 attempts; surface the
failure rather than looping.

**Identification.** Set a descriptive `User-Agent` on every request
including a contact URL or email, e.g.
`AcmeCRM/2.3 (+https://acme.example/contact)`. See
`https://developer.joinflow.com/best-practices/identify`. Generic or
missing UAs may be throttled as unidentified traffic.

## 7. Example workflows

Two HTTP recipes. cURL only — translate to the target language on
demand. Bodies are illustrative; consult the live spec for required
fields.

Notation below: `BASE=https://admin.joinflow.com/api/capi`,
`H="Authorization: Bearer <jwt>"`,
`UA="User-Agent: AcmeApp/1.0 (+https://acme.example/contact)"`.
Every cURL should set both headers — they're elided after step 1 for
readability.

### 7.1 Onboard a new employee

Provision a user, hand them credentials, plug them into the support
queue, trigger the welcome email.

**Step 1 — create the user.** Persist the returned user / extension
identifier; every later step needs it.

```bash
curl -X POST "$BASE/v1/extensions/users" \
  -H "$H" -H "$UA" -H 'Content-Type: application/json' \
  -d '{"firstName":"Alva","lastName":"Andersson","email":"alva@acme.example"}'
```

**Step 2 — confirm the user and capture the extension.**

```bash
curl "$BASE/v1/extensions/users/{user}" -H "$H" -H "$UA"
```

**Step 3 — provision a credential** (deskphone / softphone).

```bash
curl -X POST "$BASE/v1/extensions/users/{user}/credentials" \
  -H "$H" -H "$UA" -H 'Content-Type: application/json' \
  -d '{"label":"Desk phone"}'
```

**Step 4 — add the user to the support queue.** The path parameter
is the queue's own extension number.

```bash
curl -X POST "$BASE/v1/extensions/pbx/queues/{extension}/members" \
  -H "$H" -H "$UA" -H 'Content-Type: application/json' \
  -d '{"user":"{user}"}'
```

**Step 5 — trigger the onboarding email.**

```bash
curl -X POST "$BASE/v1/extensions/users/user-access/{user}/onboarding-mail" \
  -H "$H" -H "$UA"
```

**Recovery.** If a later step fails after step 1 succeeded, you have
an orphaned user — `GET /v1/extensions/users/{user}` to confirm, then
either complete the missing steps or
`DELETE /v1/extensions/users/{user}`.

### 7.2 Daily call-history pull

Read-only report for the previous business day. No mutations — no
user confirmation needed.

**Step 1 — list the team.** Filter by invoice place (or another
attribute that scopes "the team" in your account). Walk pages
sequentially via `offset`.

```bash
curl "$BASE/v1/extensions/users?invoice-place={place}&limit=100&offset=0" \
  -H "$H" -H "$UA"
```

**Step 2 — per user, fetch the call history for the window.** If
your token is scoped to a single user and you only want that user's
history, prefer `GET /v1/extensions/users/me/calls/history` instead.

```bash
curl "$BASE/v1/extensions/{extension}/calls?fromDate=2026-05-18&toDate=2026-05-18" \
  -H "$H" -H "$UA"
```

**Step 3 — optionally fetch recordings referenced in the history.**
Each recording entry has a `recordedCall` identifier; response is
the audio bytestream.

```bash
curl "$BASE/v1/extensions/users/calls/recordings/{recordedCall}" \
  -H "$H" -H "$UA" --output recording.mp3
```

**Step 4 — enrich with license / product context if needed.**

```bash
curl "$BASE/v1/extensions/users/{user}/licenses" -H "$H" -H "$UA"
curl "$BASE/v1/products"                          -H "$H" -H "$UA"
```

**Cadence.** Daily report → poll once per day. See polling guidance
in `/llms-full.txt`.

## 8. What you can't do here

Do not pretend these endpoints exist:

- **Real-time call-event streaming.** CAPI is request/response REST.
  No WebSocket / SSE / webhook surface for live call events. Poll
  `calls/history` or design around the absence.
- **SMS sender / channel configuration.** Sending an SMS via
  `POST /v1/extensions/users/me/sms` is supported; configuring the
  outbound sender or channel is not exposed here.
- **A single "who am I" endpoint.** Legacy v1's `GET /extensions/me`
  is not directly replicated. Per-user actions live under
  `/v1/extensions/users/me/…`, but no single object names the
  caller's own extension. It's bound to the JWT at issuance —
  discover it out of band.
- **Basic Authentication.** CAPI is Bearer JWT only.

## 9. Versioning

This skill file is **2026.05** (calendar-versioned, see frontmatter).

Consider it stale when either:

- `https://developer.joinflow.com/capi/changelog` shows a major
  upstream API change after this date (renamed paths, new required
  fields, breaking auth or pagination changes).
- `https://developer.joinflow.com/announcements.json` carries a
  release or breaking-change entry tagged for CAPI dated after this
  skill's version.

Fetch both at session start and reconcile against this file. The
references in §5 are deliberately the source of truth — operational
guidance here (auth shape, safety contract, conventions) changes
rarely; endpoint surface and best-practices wording change often.
