---
title: "Start a LinkedIn Campaign from Any Webhook (Custom Triggers)"
description: "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."
url: "https://prosyo.com/docs/integrations/custom-triggers/"
image: "https://prosyo.com/wp-content/uploads/2026/10/Prosyo-wordmark-white-vector.svg"
language: "es"
---

# Start a LinkedIn Campaign from Any Webhook (Custom Triggers)

1. [Prosyo Docs](https://prosyo.com/es/docs/)
2. [Integrations](https://prosyo.com/es/docs/integrations/)
3. Custom triggers: start a campaign from any webhook

Last updated November 6, 2026 8 min read

**On this page**

- How it works
- Before you start
- Option 1: n8n (recommended for full control)
- Option 2: Zapier
- Option 3: Make
- Option 4: your own code (a trigger URL you host)
- Ready-made trigger recipes
- Chain triggers with Prosyo events
- Rules and limits
- Troubleshooting

A **custom trigger** starts Prosyo outreach when something happens in another app. For example:

- someone fills in your **website form** → they're added to a list and enrolled in a LinkedIn sequence,
- a **trial signup** in Stripe → the founder sends a personal LinkedIn touch,
- a **Calendly no-show** → a gentle follow-up sequence starts,
- a **HubSpot deal** moves to closed-lost → a revival campaign starts in 90 days.

Webhooks already let Prosyo *send* events out. See [Signed webhooks](https://prosyo.com/es/docs/integrations/webhooks/). Custom triggers work the other way: **any app → Prosyo**.

---

## How it works#

Every custom trigger follows the same three steps:

Text

```
Your app ──webhook──▶ Trigger URL (n8n / Zapier / Make / your code)
                          │
                          ├─ 1. POST /api/v1/ext/leads      → add or update the person on a list
                          ├─ 2. POST /api/v1/ext/campaigns  → enroll them in a campaign
                          └─ 3. (optional) POST /api/v1/ext/enrich or /bulk
```

| Step | Endpoint | Result |
| --- | --- | --- |
| 1. Add the lead | `POST /api/v1/ext/leads` | Returns `prospectIds` for new **and** existing people |
| 2. Enroll | `POST /api/v1/ext/campaigns` | Adds them to a campaign. A running campaign starts sending inside working hours. |
| 3. Optional | `POST /api/v1/ext/enrich` | Research the person (5 credits each) or write a first-touch line (2 credits each) |
| 3. Optional | `POST /api/v1/ext/bulk` | Tag, change stage, or add to another list |

Everything still follows your campaign's **daily limits, working hours, delay between actions and stop-on-reply**. A trigger can't make Prosyo send faster than your settings allow.

## Before you start#

1. **Create the campaign** you want people to enter and **launch** it. If it's still a draft, people are enrolled but nothing sends until you launch.
2. **Get the campaign ID.** Call `GET /api/v1/ext/campaigns` (below), or open the campaign in Prosyo and copy the ID from the address bar: `app.prosyo.com/campaigns/<campaign-id>`.
3. **Generate an API token** in **Integrations → Chrome extension & API → Generate API token**. Copy it now, because it's shown once. Only owners and admins can generate one. If your workspace already has a token and you saved it, use it. Otherwise click **Rotate token**: it replaces every token, so paste the new one into your Chrome extension and every other tool that used the old one.

Important

Treat the token like a password. Store it in your automation tool's credentials, not in plain text, and never put it in website code that visitors can see.

---

## Option 1: n8n (recommended for full control)#

**Workflow:** Webhook → HTTP Request (add lead) → HTTP Request (enroll)

1. **Webhook** node
   - HTTP Method: `POST`
   - Path: `prosyo-trigger`
   - Respond: Immediately
   - Copy the **Production URL**. This is your custom trigger URL. Paste it into your form tool, CRM or app as the webhook destination.
2. **HTTP Request: add lead**
   - Method `POST`, URL `https://app.prosyo.com/api/v1/ext/leads`
   - Authentication: Header Auth → `Authorization: Bearer YOUR_TOKEN`
   - Body (JSON):
     JSON
     ```json
{
  "listName": "Website form",
  "leads": [{
    "fullName": "{{ $json.body.name }}",
    "email": "{{ $json.body.email }}",
    "companyName": "{{ $json.body.company }}",
    "linkedinUrl": "{{ $json.body.linkedin }}"
  }]
}
```
3. **HTTP Request: enroll**
   - Method `POST`, URL `https://app.prosyo.com/api/v1/ext/campaigns`
   - Same Header Auth
   - Body (JSON):
     JSON
     ```json
{
  "campaignId": "YOUR_CAMPAIGN_ID",
  "prospectIds": {{ JSON.stringify($json.data.prospectIds) }}
}
```
4. **Activate** the workflow.

Add an **If** node between steps 2 and 3 to enroll only good-fit leads, for example when the company size is over 20 or the email domain isn't Gmail.

## Option 2: Zapier#

1. **Trigger:** your app (Typeform, Webflow, HubSpot, Stripe, Calendly, or anything else), or **Webhooks by Zapier → Catch Hook** for a generic trigger URL.
2. **Action: Webhooks by Zapier → Custom Request** (add lead)
   - Method `POST`, URL `https://app.prosyo.com/api/v1/ext/leads`
   - Headers: `Authorization: Bearer YOUR_TOKEN`, `Content-Type: application/json`
   - Data: the lead JSON above, with fields inserted from the trigger
3. **Action: Webhooks by Zapier → Custom Request** (enroll)
   - Method `POST`, URL `https://app.prosyo.com/api/v1/ext/campaigns`
   - Same headers
   - Data:
     JSON
     ```json
{ "campaignId": "YOUR_CAMPAIGN_ID", "prospectIds": ["{{data__prospectIds}}"] }
```
     Insert **Data Prospect Ids** from step 2. If Zapier returns it as a comma-separated list, add a **Formatter → Utilities → Line-item to text** step, or use a **Code by Zapier** step to build the JSON array.

## Option 3: Make#

1. **Webhooks → Custom webhook** as the trigger. Copy the URL. That's your trigger URL.
2. **HTTP → Make a request** to `/api/v1/ext/leads` (Raw JSON body, `Authorization: Bearer` header).
3. **HTTP → Make a request** to `/api/v1/ext/campaigns` with `campaignId` and `prospectIds` mapped from step 2. Turn on **Parse response** in step 2 so you can map `data.prospectIds`.

## Option 4: your own code (a trigger URL you host)#

Deploy this small function to Vercel, Netlify, Cloudflare Workers or any Node server. Its URL becomes your custom trigger. Set `PROSYO_TOKEN`, `PROSYO_CAMPAIGN_ID` and `TRIGGER_SECRET` as environment variables.

JavaScript

```js
// POST /api/prosyo-trigger?key=TRIGGER_SECRET
export default async function handler(req, res) {
  if (req.method !== "POST" || req.query.key !== process.env.TRIGGER_SECRET) {
    return res.status(401).json({ ok: false });
  }
  const { name, email, company, linkedin, list = "Custom trigger" } = req.body || {};
  const headers = {
    Authorization: `Bearer ${process.env.PROSYO_TOKEN}`,
    "Content-Type": "application/json",
  };
  const lead = { fullName: name, companyName: company, linkedinUrl: linkedin };
  if (email) lead.email = email; // never send an empty email string

  const added = await fetch("https://app.prosyo.com/api/v1/ext/leads", {
    method: "POST",
    headers,
    body: JSON.stringify({ listName: list, leads: [lead] }),
  }).then((r) => r.json());
  if (!added.ok) return res.status(502).json(added);

  const enrolled = await fetch("https://app.prosyo.com/api/v1/ext/campaigns", {
    method: "POST",
    headers,
    body: JSON.stringify({
      campaignId: process.env.PROSYO_CAMPAIGN_ID,
      prospectIds: added.data.prospectIds,
    }),
  }).then((r) => r.json());

  return res.status(200).json({ ok: true, added: added.data, enrolled: enrolled.data });
}
```

**cURL test:**

Bash

```bash
curl -X POST "https://your-app.vercel.app/api/prosyo-trigger?key=YOUR_TRIGGER_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"name":"Alex Rivera","email":"alex@acme.com","company":"Acme","linkedin":"https://www.linkedin.com/in/alexrivera"}'
```

**Python (Flask):**

Python

```python
import os, requests
from flask import Flask, request, jsonify

app = Flask(__name__)
API = "https://app.prosyo.com/api/v1/ext"
H = {"Authorization": f"Bearer {os.environ['PROSYO_TOKEN']}"}

@app.post("/prosyo-trigger")
def trigger():
    if request.args.get("key") != os.environ["TRIGGER_SECRET"]:
        return jsonify(ok=False), 401
    b = request.get_json(force=True)
    lead = {k: v for k, v in {
        "fullName": b.get("name"), "email": b.get("email"),
        "companyName": b.get("company"), "linkedinUrl": b.get("linkedin"),
    }.items() if v}
    added = requests.post(f"{API}/leads", headers=H,
                          json={"listName": "Custom trigger", "leads": [lead]}).json()
    ids = added["data"]["prospectIds"]
    enrolled = requests.post(f"{API}/campaigns", headers=H,
                             json={"campaignId": os.environ["PROSYO_CAMPAIGN_ID"], "prospectIds": ids}).json()
    return jsonify(ok=True, added=added["data"], enrolled=enrolled["data"])
```

---

## Ready-made trigger recipes#

| Trigger (your app) | List | Campaign to enroll in | Extra step |
| --- | --- | --- | --- |
| **Website demo form** (Webflow, WordPress, HubSpot Forms, Typeform) | Inbound – demo form | "Inbound: fast LinkedIn touch": visit → connect → message within 1 day | Enrich (`personalized_line`) |
| **Gated content download** (ebook, report) | Content – [asset name] | Soft 3-touch: connect → "hope the guide helps" → offer a call | Tag `content` |
| **Webinar registration** (Zoom, Livestorm, Luma) | Event – [name] | [Webinar playbook](https://prosyo.com/es/docs/playbooks/webinar-event-promotion/) | Tag `registered` |
| **Free trial signup** (Stripe, Paddle, your app) | Trials – this month | Founder welcome on LinkedIn | Stage `interested` |
| **Calendly / Cal.com no-show** | No-shows | 2-touch reschedule sequence | Stage `meeting` → `interested` |
| **HubSpot deal closed-lost** | Closed-lost | [Revival playbook](https://prosyo.com/es/docs/playbooks/closed-lost-revival/) (delay 60–90 days) | Tag `closed-lost` |
| **Job change** (Clay, Common Room, UserGems) | Job changers | [Job-change playbook](https://prosyo.com/es/docs/playbooks/job-change-outreach/) | Enrich |
| **New hire posted** (hiring-signal tool) | Hiring signals | [Hiring-signal playbook](https://prosyo.com/es/docs/playbooks/hiring-signal-outreach/) | — |
| **Slack slash command** (`/prosyo add <linkedin url>`) | Team referrals | Your main ABM campaign | — |
| **Google Sheets new row** | Sheet – [name] | Any campaign | — |

## Chain triggers with Prosyo events#

Combine custom triggers with [outgoing webhooks](https://prosyo.com/es/docs/integrations/webhooks/) to build loops:

- **Reply received** → an n8n Switch → if the reply is positive, `POST /bulk` with `action: "stage"` and `stage: "interested"`, then create a CRM deal.
- **Stage changed to `not_interested`** → `POST /bulk` with `action: "add_to_list"` to a *Nurture – 6 months* list → a scheduled n8n job enrolls that list in a nurture campaign later.
- **Prospect created** from a form → wait 10 minutes → `POST /enrich` → enroll once enrichment is done.

Tip

Not ready to start outreach yet? Send the event to Intent instead with `POST /signals` (for example a `signup` or `form_submit`). Intent adds it to the person's score, and your Intent playbooks can take it from there. See the [API reference](https://prosyo.com/es/docs/integrations/api-reference/#post-signals).

## Rules and limits#

| Rule | Value |
| --- | --- |
| Leads per `/leads` request | Up to 100 |
| Prospects per `/campaigns` enroll request | Up to 100 |
| Rate limit, `/leads` | 60 requests / minute / user |
| Rate limit, `/campaigns` enroll | 30 requests / minute / user |
| Each lead needs | A name, a LinkedIn URL or an email |
| Empty values | Leave the field out or send `null`. Don't send `""` for `email`. |
| Import quota | New people count toward monthly imports. People matched by their LinkedIn profile are updated and don't count. A lead with no LinkedIn URL is always added as a new person, even if its email is already in Prosyo, and is then saved without that email. |
| Duplicates | Leads are matched to people already in Prosyo by their LinkedIn profile, so send `linkedinUrl` whenever you have it. The same person is never enrolled twice in one campaign. |
| Role | A token acts as the owner or admin who generated it, with their current role. If that person's role is lowered to Viewer, the token can only read. |

## Troubleshooting#

| Response | Meaning | Fix |
| --- | --- | --- |
| `401` | Token missing, wrong, rotated or revoked | Generate a new token |
| `404` "That list is not in this workspace" | Wrong `listId` | Use `listName`, or get IDs from `GET /boards` |
| `422` "No usable leads" | Every lead was empty | Send at least a name, email or LinkedIn URL |
| `422` validation error on `email` | Empty or invalid email | Leave `email` out when you don't have one |
| `429` | Rate limit | Batch leads (up to 100) or add a short delay |
| Enrolled but nothing sends | Campaign is a draft or paused, or it's outside working hours | Launch or resume the campaign, and check working hours |
| `skipped` > 0 when enrolling | The person is already in that campaign, is marked Do not contact, or isn't visible to the token's user | Expected. Prosyo protects people from double outreach. |
| `403` with `plan_limit` | Everyone in the request was new, and the monthly import limit is reached | People already in Prosyo still update. Wait for next month or upgrade. If only some people fit, the request succeeds and `quotaBlocked` shows how many weren't added. |

See the full [API reference](https://prosyo.com/es/docs/integrations/api-reference/).

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

[API reference Prosyo REST API reference: authentication, add leads, enroll in campaigns, enrichment, bulk actions, prospects, inbox, Intent signals, limits and errors.](https://prosyo.com/es/docs/integrations/api-reference/) [n8n Trigger n8n workflows from Prosyo events, verify webhook signatures, and add leads and enroll them in LinkedIn campaigns from n8n.](https://prosyo.com/es/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/es/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/es/docs/integrations/make/)
