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

# API quickstart

> Your first calls to the B2B Signals API with curl: add a person, list people, add people to a campaign and read the results.

This page takes you from an API key to a person in a running campaign, with curl. Every call here is in the [API reference](#the-api-reference), with all its fields.

## Set up

You need an API key. "API keys come with a paid plan (Software or Custom)." Only the workspace owner or an admin can make one. See [API keys](https://docs.b2bsignals.ai/developers/api-keys.md).

Find your base URL in **Settings** › **API & MCP**, under **REST API**. Then set two variables in your terminal:

```bash theme={null}
export B2B_API_BASE="<your base URL>"
export B2B_API_KEY="<your key>"
```

Every request sends the key in the `X-API-Key` header. A key works only in its own workspace, with the rights of the workspace owner. Keep it private.

## Names in the API

The API uses a few older names. They mean the same as the words in the app:

| In the app | In the API |
| - | - |
| Person | `lead`, as in `/leads/` and `lead_id` |
| Campaign | `workflow`, as in `/workflows/` and `workflow_id` |
| A person's place in a campaign | `workflow-run`, as in `/workflow-runs/` and `run_id` |
| List | `lead-list` |
| Sender | `connected-account` |
| Do not contact | `suppressions` for people, `company-blacklist` for companies |

## 1. Add a person

```bash theme={null}
curl -X POST "$B2B_API_BASE/intake/people" \
  -H "X-API-Key: $B2B_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "linkedin_url": "https://www.linkedin.com/in/jordan-example",
    "name": "Jordan Example",
    "company_name": "Example Logistics",
    "reason": "Asked for a demo on our website"
  }'
```

The answer is `201` with the person's id in `lead_id`, and `action` set to `created`, or `updated` if they were already in your workspace. Nothing is sent to anyone.

Add `?test=true` to check a row without saving it. For up to 100 people at once, people without a LinkedIn URL, and every answer you can get, see [Zapier, Make, n8n and your own code](https://docs.b2bsignals.ai/integrations/api-intake.md).

## 2. List people

```bash theme={null}
curl -i "$B2B_API_BASE/leads/?limit=20&skip=0&search=example" \
  -H "X-API-Key: $B2B_API_KEY"
```

The body is a list of people. The `X-Total-Count` header holds how many match in total. To get the next page, add `limit` to `skip`: `skip=20`, then `skip=40`.

To find one person by their LinkedIn URL:

```bash theme={null}
curl "$B2B_API_BASE/leads/by-linkedin?url=https://www.linkedin.com/in/jordan-example" \
  -H "X-API-Key: $B2B_API_KEY"
```

## 3. Add people to a campaign

Find the campaign's id first:

```bash theme={null}
curl "$B2B_API_BASE/workflows/" -H "X-API-Key: $B2B_API_KEY"
```

Then check what adding people would do. This changes nothing:

```bash theme={null}
curl -X POST "$B2B_API_BASE/workflows/$CAMPAIGN_ID/enroll/preview" \
  -H "X-API-Key: $B2B_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lead_ids": ["6f0c2d3e-0000-4000-8000-000000000001"] }'
```

`would_add` is how many would start. `skipped` lists who wouldn't, with the reason. `start` says when the first ones would start.

Now add them, up to 200 per call:

```bash theme={null}
curl -X POST "$B2B_API_BASE/workflows/$CAMPAIGN_ID/enroll" \
  -H "X-API-Key: $B2B_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lead_ids": ["6f0c2d3e-0000-4000-8000-000000000001"] }'
```

```json theme={null}
{ "enrolled": 1, "skipped_suppressed": 0, "skipped_active": 0, "skipped_already_connected": 0, "errors": 0 }
```

The real answer has a count for every reason a person can be skipped. Do not contact always applies. Send `"force": true` to add someone who is already running in another campaign.

Adding people doesn't send anything by itself. The campaign's schedule, [sender limits](https://docs.b2bsignals.ai/campaigns/senders-and-limits.md) and [approvals](https://docs.b2bsignals.ai/campaigns/approvals.md) decide when each message goes. If the campaign is a draft or paused, nothing goes until it runs. You can start it with `POST /workflows/{workflow_id}/activate`, unless its approval setting is one only a signed-in person can start.

To add a whole list, use `POST /lead-lists/{list_id}/enroll` with the campaign's `workflow_id`.

## 4. Read the results

The campaign's numbers:

```bash theme={null}
curl "$B2B_API_BASE/workflows/$CAMPAIGN_ID/analytics/summary?days=30" \
  -H "X-API-Key: $B2B_API_KEY"
```

`totals` counts people by where they are. `kpis` holds invites sent and accepted, messages, replies and their rates.

Each person and what happened to them:

```bash theme={null}
curl "$B2B_API_BASE/workflows/$CAMPAIGN_ID/analytics/leads?limit=50&skip=0&has_reply=true" \
  -H "X-API-Key: $B2B_API_KEY"
```

The answer has `items` and `total`. Each item says where the person is (`where_label`), what's next (`next_label`), and whether they `replied`.

One person, step by step, in plain words:

```bash theme={null}
curl "$B2B_API_BASE/workflow-runs/$RUN_ID/activity" -H "X-API-Key: $B2B_API_KEY"
```

To get told the moment someone replies instead of asking, use [Webhooks](https://docs.b2bsignals.ai/developers/webhooks.md).

## Pagination

| Endpoints | How to page | Total |
| - | - | - |
| `GET /leads/` | `skip` and `limit`, up to 500 | `X-Total-Count` header |
| `GET /suppressions/` | `skip` and `limit`, up to 500 | `X-Total-Count` header |
| `GET /lead-lists/`, `GET /workflow-runs/pending-approvals` | `skip` and `limit`, up to 200 | `X-Total-Count` header |
| `GET /lead-lists/{list_id}/leads`, `GET /conversations`, `GET /workflows/{workflow_id}/analytics/leads` | `skip` and `limit`, up to 200 | `total` in the body |
| `GET /workflows/`, `GET /workflow-runs/`, `GET /webhooks/` | `skip` and `limit`, up to 200 | Keep going until a page has fewer rows than `limit` |
| `GET /conversations/{conversation_id}/messages` | `limit` and `before` | |

`limit` is 50 when you leave it out.

## Errors

Errors come back as JSON with a `detail` field. It is a sentence, or an object with a `code` and a `message`:

```json theme={null}
{ "detail": "Invalid API key" }
```

```json theme={null}
{ "detail": { "code": "no_person_found", "message": "No person could be identified. Send a LinkedIn profile URL, or a full name and a company. Rows with only a company are skipped." } }
```

| Status | What it means | What to do |
| - | - | - |
| `401` | "Missing authentication credentials" or "Invalid API key" | Check the `X-API-Key` header. A new key replaces the old one |
| `403` | The key can't do this, or the plan doesn't include it | Read `detail`. See below |
| `404` | Nothing with that id in your workspace | Check the id |
| `409` | The action clashes with the current state, such as deleting a running campaign | Read `detail`, fix the state, try again |
| `422` | A field is missing or wrong. `detail` lists each problem, with its place in `loc` | Fix the field |
| `429` | A limit was reached | Wait. If there's a `Retry-After` header, wait that many seconds |
| `500` | Something went wrong on our side | Try again later |

Some actions answer `403` to a key on purpose:

* Approving a message answers `403` unless the workspace owner turned on **Let AI tools approve and send messages** in **Settings** › **API & MCP**. Skipping a message always works.
* Starting a campaign answers `403` when its approval setting lets messages go without a person checking each one.
* Moving a campaign from HeyReach, and some settings, need a person signed in to the app. See [Developers overview](https://docs.b2bsignals.ai/developers/overview.md#permissions).
* A plan without outreach answers "Outreach automation is not enabled for this account."

## Rate limits

There's no general limit on requests per key today. Some actions have their own:

| Action | Limit |
| - | - |
| Adding people without a LinkedIn URL | 150 lookups per workspace per UTC day, and 20 per request |

Past a limit you get `429`. Your [senders' limits](https://docs.b2bsignals.ai/campaigns/senders-and-limits.md) always apply to messages, however a person was added.

## The API reference

The **API reference** in this tab lists every endpoint your key can use, grouped as in the app: Add people, People, Notes and tasks, Lists, Campaigns, Campaign people, Approvals, Inbox, Do not contact, Senders, Webhooks, Integrations, Reports, Watchlists and Search. Each page shows the fields, the answers, and a request you can try.

> [card] [API keys](https://docs.b2bsignals.ai/developers/api-keys.md)
>
> Make, replace and turn off your key.

> [card] [Add people from your tools](https://docs.b2bsignals.ai/integrations/api-intake.md)
>
> Every intake option, with examples.

> [card] [Webhooks](https://docs.b2bsignals.ai/developers/webhooks.md)
>
> Get events as they happen.

> [card] [Developers overview](https://docs.b2bsignals.ai/developers/overview.md)
>
> Permissions, limits and errors.


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