---
title: "Prosyo API Reference: Leads, Campaigns, Enrich, Bulk"
description: "Prosyo REST API reference: authentication, add leads, enroll in campaigns, enrichment, bulk actions, prospects, inbox, Intent signals, limits and errors."
url: "https://prosyo.com/docs/integrations/api-reference/"
image: "https://prosyo.com/wp-content/uploads/2026/10/Prosyo-wordmark-white-vector.svg"
language: "nl"
---

# Prosyo API Reference: Leads, Campaigns, Enrich, Bulk

Last updated November 6, 2026 6 min read

**On this page**

- Basics
- Response envelope
- GET /session
- POST /leads
- GET /campaigns
- POST /campaigns
- POST /enrich
- POST /bulk
- GET /prospects
- GET /boards
- GET /inbox
- GET /intent
- POST /signals
- Outgoing events
- Good practice

The Prosyo API lets your own tools add leads, enroll prospects in campaigns, enrich, organize, read your inbox and send buying signals to Intent. The [Chrome extension](https://prosyo.com/nl/docs/integrations/chrome-extension/) uses the same API.

## Basics#

| | |
| --- | --- |
| **Base URL** | `https://app.prosyo.com/api/v1/ext` |
| **Auth** | `Authorization: Bearer <token>` on every request. Being signed in to Prosyo in the browser isn't enough. |
| **Get a token** | **Integrations → Chrome extension & API → Generate API token** (owners and admins) |
| **Format** | JSON in, JSON out |
| **Plans** | All plans. Your plan's limits apply. |

Tokens act as the user who created them, with that user's role and workspace, and they stop working if that user leaves the workspace. Prosyo shows a token once. When your workspace already has one, the button reads **Rotate token**: it replaces every token in the workspace, so paste the new one into every tool and Chrome extension install. **Revoke all tokens** disconnects every install.

### Response envelope#

Success:

JSON

```json
{ "ok": true, "data": { … } }
```

Error:

JSON

```json
{ "ok": false, "error": { "code": "validation_error", "message": "…", "fieldErrors": { "email": ["Invalid email"] }, "correlationId": "a1b2c3d4" } }
```

Include the `correlationId` when you contact support about a failed request. Check `error.code` rather than the message text, because messages can change.

| Status | Meaning |
| --- | --- |
| `200` | Success |
| `401` | Missing or invalid token (`unauthorized`) |
| `402` | Not enough credits (`insufficient_credits`) |
| `403` | Your role doesn't allow this (`forbidden`), your plan isn't active (`plan_inactive`), or the monthly import limit is reached (`plan_limit`) |
| `404` | Not found in this workspace |
| `422` | Validation failed |
| `429` | Rate limited (`rate_limited`). Wait a minute. |
| `503` | AI isn't available right now (`ai_unavailable`). Try again later. |

---

## GET /session#

Check the token, and read the workspace, plan and remaining import quota.

Bash

```bash
curl https://app.prosyo.com/api/v1/ext/session \
  -H "Authorization: Bearer $PROSYO_TOKEN"
```

**Response `data`** includes `user` (`email`, `displayName` and `role`), `workspace` (`id` and `name`), `plan` (`code` and `name`), `quota` (`monthlyImports`, `usedThisMonth` and `remaining`) and `appUrl`. In a client workspace, the import quota is shared with your agency.

---

## POST /leads#

Add or update prospects, optionally on a list. **Role:** Member or higher. **Limit:** 60 requests per minute.

**Body**

| Field | Type | Notes |
| --- | --- | --- |
| `leads` | array, 1–100 | Required |
| `listName` | string | Adds the leads to this list, creating it if needed |
| `listId` | uuid | Or add them to an existing list by ID. Leave out both to save without a list. |
| `sourceType` | string | `extension` (default), `extension_profile`, `extension_search`, `extension_comments` |

**Lead fields:** `fullName`, `firstName`, `lastName`, `title`, `companyName`, `linkedinUrl`, `website`, `email`, `phone`, `locationName`. Each lead needs a name, LinkedIn URL or email. Leave empty fields out rather than sending `""`.

Bash

```bash
curl -X POST https://app.prosyo.com/api/v1/ext/leads \
  -H "Authorization: Bearer $PROSYO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "listName": "API test",
    "leads": [
      { "firstName": "Alex", "lastName": "Rivera", "title": "Head of Sales",
        "companyName": "Acme", "email": "alex@acme.com",
        "linkedinUrl": "https://www.linkedin.com/in/alexrivera" }
    ]
  }'
```

**Response `data`**

JSON

```json
{
  "importJobId": "…",
  "received": 1,
  "processed": 1,
  "inserted": 1,
  "skipped": 0,
  "failed": 0,
  "quotaBlocked": 0,
  "listId": "…",
  "prospectIds": ["…"],
  "quota": { "monthlyImports": 5000, "remaining": 4999 }
}
```

`prospectIds` includes both new and already-existing people, so you can pass it straight to `/campaigns` or `/enrich`. Leads are matched to people already in Prosyo by their LinkedIn profile only, so send `linkedinUrl` whenever you have it. A lead sent without a LinkedIn URL is not matched by email: it's added as a new person and counts toward your import limit, and if someone in Prosyo already has that email, the new person is saved without it.

`inserted` counts new people, the only ones that use import quota. `skipped` counts people who were already in Prosyo (they're updated) plus those not added because the monthly import limit was reached, which also appear in `quotaBlocked`. If every person in the request is new and the limit is already reached, the request fails with `403` and `plan_limit`.

---

## GET /campaigns#

List up to 50 draft, running and paused campaigns. **Role:** Viewer or higher.

JSON

```json
{ "campaigns": [ { "id": "…", "name": "UK founders", "status": "running", "enrolled": 142 } ] }
```

## POST /campaigns#

Enroll prospects in a campaign. **Role:** Member or higher. **Limit:** 30 requests per minute.

| Field | Type | Notes |
| --- | --- | --- |
| `campaignId` | uuid | Required |
| `prospectIds` | uuid[], 1–100 | Required |

Bash

```bash
curl -X POST https://app.prosyo.com/api/v1/ext/campaigns \
  -H "Authorization: Bearer $PROSYO_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "campaignId": "CAMPAIGN_ID", "prospectIds": ["PROSPECT_ID"] }'
```

**Response `data`:** `{ "enrolled": 1, "skipped": 0, "campaignName": "UK founders" }`

People already in the campaign, marked Do not contact, or not visible to the token's user are **skipped**, not double-enrolled. A running campaign starts them inside its working hours, at its usual pace. In a draft or paused campaign they wait until you launch or resume it.

---

## POST /enrich#

Queue AI research or a first-touch line. **Role:** Member or higher. Uses **5 credits per prospect** for `enrich_only` and **2 credits per prospect** for `personalized_line`, and failures are refunded.

| Field | Type | Notes |
| --- | --- | --- |
| `prospectIds` | uuid[], 1–100 | Required |
| `mode` | `enrich_only` \| `personalized_line` | Default `enrich_only` |
| `useWorkspaceInstructions` | boolean | Use your workspace AI instructions |
| `customPrompt` | string, max 2,000 characters | Extra guidance for the line |

**Response `data`:** `{ "jobId": "…", "total": 1, "cost": 5 }`. If your balance can't cover the cost, nothing is queued and you get `402` with `insufficient_credits`.

**Check progress** with `GET /enrich?jobId=…`. It returns the job's `status`, `total`, `completed`, `found`, `not_found`, `failed` and `credit_cost`.

---

## POST /bulk#

Organize up to 500 prospects at once. **Role:** Member or higher.

| Field | Type | Notes |
| --- | --- | --- |
| `action` | `tag`, `untag`, `stage`, `add_to_list`, `remove_from_list`, `suppress`, `unsuppress`, `delete` | Required |
| `prospectIds` | uuid[], 1–500 | Required |
| `tagId` / `tagName` | | For `tag` / `untag` |
| `listId` / `listName` | | For `add_to_list` / `remove_from_list` |
| `stage` | `new`, `contacted`, `replied`, `interested`, `meeting`, `customer`, `not_interested` | For `stage` |

`suppress` marks people **Do not contact**. `delete` can't be undone.

JSON

```json
{ "action": "stage", "prospectIds": ["…"], "stage": "interested" }
```

---

## GET /prospects#

Search your prospects. **Role:** Viewer or higher.

| Query param | Example |
| --- | --- |
| `q` | `q=acme` |
| `stage` | `stage=replied` |
| `listId` | `listId=…` |
| `tagId` | `tagId=…` |
| `campaignId` | `campaignId=…` |
| `hasEmail` | `hasEmail=true` |
| `hasLinkedin` | `hasLinkedin=true` |
| `enrichmentStatus` | `enrichmentStatus=enriched` |
| `createdFrom` / `createdTo` | ISO dates |
| `page` / `pageSize` | Pagination. `pageSize` is 10–100, default 25. |
| `returnIds` | `returnIds=true` returns only IDs |

## GET /boards#

Returns your lists, tags and pipeline stages, with the IDs you need for `/bulk` and `/leads`. **Role:** Viewer or higher.

## GET /inbox#

Returns the latest conversations visible to the token's user. **Role:** Viewer or higher.

---

## GET /intent#

Read a LinkedIn profile's Intent priority, the same badge the Chrome extension shows. **Role:** Viewer or higher.

Bash

```bash
curl "https://app.prosyo.com/api/v1/ext/intent?linkedinUrl=https://www.linkedin.com/in/alexrivera" \
  -H "Authorization: Bearer $PROSYO_TOKEN"
```

**Response `data`:** `{ "lead": null }` when the person isn't in Intent. Otherwise `lead` has `tier`, `tierLabel` (**High-intent ICP**, **Warm** or **Warming up**), `intentScore`, `fitScore`, `topSignal`, `locked` (`true` until the person is unlocked in Intent) and `url` (where to open them in Prosyo).

## POST /signals#

Send buying signals from your own systems to Intent, such as a product sign-up or a form. **Role:** Member or higher.

| Field | Type | Notes |
| --- | --- | --- |
| `signals` | array, 1–50 | Required |
| `signals[].kind` | string | `profile_view`, `post_comment`, `post_reaction`, `invite_received`, `inbound_message`, `custom_signal`, `signup`, `form_submit` |
| `signals[].person` | object | Required: `linkedinUrl` (a `linkedin.com/in/` profile) or `email`. Optional: `name`, `title`, `company`. |
| `signals[].label` | string, max 120 characters | The signal's name, for `custom_signal` |
| `signals[].detail` | string, max 1,000 characters | Extra context |
| `signals[].url` | URL | Where it happened |
| `signals[].occurredAt` | ISO date | When it happened. Default: now. |
| `signals[].strength` | 1–60 | How strong a `custom_signal` is. Default 30. |
| `signals[].id` | string, max 120 characters | Your own ID, so the same signal isn't counted twice |

JSON

```json
{ "signals": [ { "kind": "signup", "person": { "email": "alex@acme.com", "name": "Alex Rivera", "company": "Acme" } } ] }
```

**Response `data`:** `{ "accepted": 1, "ignored": 0, "paused": false }`.

- If you send both `email` and `linkedinUrl`, Prosyo uses the email. A signal sent by email needs someone already in your prospects with that email, except `signup`, `form_submit` and `custom_signal`, which can add a new person to Intent.
- When this month's Intent people are used up and you have fewer than 10 credits, nothing is stored and the response has `"paused": true` with a `message`. It starts again when you add credits or next month. If your plan isn't active, the request fails with `403` and `plan_inactive`.

---

## Outgoing events#

To receive events from Prosyo (replies, stage changes and so on), use [signed webhooks](https://prosyo.com/nl/docs/integrations/webhooks/).

## Good practice#

- Keep tokens in a secrets manager or your automation tool's credentials.
- Batch requests (up to 100 leads) instead of one request per person.
- Retry `429` responses after a short wait. Don't retry a `plan_limit` or `insufficient_credits` error until the quota resets or credits are added.
- Generate tokens from a long-term owner or admin account, so a teammate leaving doesn't break your integrations.

See [Custom triggers](https://prosyo.com/nl/docs/integrations/custom-triggers/) for end-to-end recipes.

Connect it in Prosyo

Open Integrations in the app to set this up in a few minutes.

[Connect in Prosyo](https://app.prosyo.com/integrations)

## Related guides

[Custom triggers Turn any form, CRM, Stripe or Calendly event into LinkedIn outreach: add the lead and enroll them in a Prosyo campaign with n8n, Zapier, Make or code.](https://prosyo.com/nl/docs/integrations/custom-triggers/) [n8n Trigger n8n workflows from Prosyo events, verify webhook signatures, and add leads and enroll them in LinkedIn campaigns from n8n.](https://prosyo.com/nl/docs/integrations/n8n/) [Zapier Use the Prosyo Zapier app: start Zaps on new prospects, stage changes and replies, and create prospects, update stages or add people to lists from other apps.](https://prosyo.com/nl/docs/integrations/zapier/) [Make Trigger Make scenarios from Prosyo events with a custom webhook, and add leads to Prosyo campaigns with an HTTP request.](https://prosyo.com/nl/docs/integrations/make/)
