# How to use SourceLoop Outgoing Webhooks

Forward every new or updated SourceLoop lead to your backend, Slack, Zapier, or Make. Up to 5 endpoints, signed deliveries, per-event subscriptions.

Source: https://sourceloop.ai/help/outgoing-webhooks-overview/
Updated: 2026-05-29

---

SourceLoop's **Outgoing Webhooks** are the reverse direction of [Incoming Webhooks](/help/incoming-webhooks-overview/). Every time a lead is created or edited in the Contacts Hub, SourceLoop fires a POST to every endpoint you've subscribed to that event type. Use this to alert your team in Slack the moment a lead comes in, hand qualified leads to Zapier / Make for routing, sync custom fields to a homegrown CRM, or trigger any backend workflow you want.

## When to use an Outgoing Webhook

Outgoing Webhooks are right when:

- You want real-time alerts (Slack, Discord, Microsoft Teams, email tools that accept webhooks)
- You're routing leads through Zapier, Make, n8n, Pipedream, or any low-code workflow tool
- You're syncing leads into a CRM or backend that SourceLoop doesn't natively support
- You need to enrich leads server-side (Clearbit, Apollo, ZoomInfo) before pushing them to your team

They're **not** the right fit when:

- A native CRM integration covers your use case ([HubSpot](/help/connect-hubspot-to-sourceloop/) / [Salesforce](/help/connect-salesforce-to-sourceloop/) / [Pipedrive](/help/connect-pipedrive-to-sourceloop/) ship with deeper field mapping, status sync, and bidirectional update handling)
- You want to pull data on a schedule rather than receive pushes (use the Contacts Hub CSV export or the Conversion API instead)

## What gets sent

Each delivery is a JSON POST containing the **full lead record** at the moment of the event. The payload includes everything you'd see on the lead's row in the Contacts Hub:

| Field group | Examples |
|---|---|
| **Identity** | email, name, phone, company, country, city |
| **Conversion** | type (Web form / Meeting / Chat / Payment / Subscription / API / CRM Import / Custom), source provider (intercom-webhook / stripe / hubspot-form / etc.), occurred_at, received_at |
| **Lifecycle** | status (New / Contacted / In Progress / Converted / Lost), qualified (yes / no / not set), lead score |
| **Revenue** | expected_revenue, revenue (in workspace currency) |
| **First-touch attribution** | first_channel, first_source, first_medium, first_campaign, first_keyword, first_content, first_landing_page |
| **Last-touch attribution** | latest_channel, latest_source, latest_medium, latest_campaign, latest_keyword, latest_content, latest_landing_page |
| **Click IDs** | gclid, msclkid, fbclid, li_fat_id, gbraid, wbraid, epik, rdt_cid |
| **Technical** | browser, device_type, os |
| **Flags** | is_spam, is_duplicate |
| **Free text** | notes |
| **Custom data** | every field your form / API / CRM sent that doesn't map to the above |

And these HTTP headers on every delivery:

```
Content-Type: application/json
X-Webhook-Secret: <the secret you configured or SourceLoop generated>
X-Webhook-Event: created | updated
```

The `X-Webhook-Event` value tells your receiver which subscription matched (`created` for New Lead, `updated` for Updated Lead).

## Before you start

You'll need:

- A **SourceLoop workspace** with at least one captured lead to test against ([free trial](https://app.sourceloop.ai/sign-up))
- **Admin** or **Owner** role in SourceLoop
- An HTTPS endpoint that accepts POST requests (Slack incoming webhook, Zapier "Catch Webhook" URL, your own server, etc.)

## Step 1: Open the Outgoing Webhooks page

1. Sign in to [SourceLoop](https://app.sourceloop.ai/).
2. Open **Setup -> Outgoing Webhooks** in the left sidebar.

You'll see a form to add a new webhook and (if any exist) a table listing the ones already configured.

## Step 2: Add the endpoint

Fill the form:

1. **Webhook URL** — the URL on your receiving server. For Slack, paste an incoming-webhook URL from your workspace's app config. For Zapier / Make, paste the trigger's "Catch Webhook" URL. For your own backend, any HTTPS endpoint that returns 2xx works.
2. **Events** — tick **New Lead**, **Updated Lead**, or both depending on what your downstream needs.
3. **Secret** (optional) — leave blank to have SourceLoop auto-generate one, or paste a string you've chosen. Whatever value lands here is what arrives on the `X-Webhook-Secret` header for every delivery.
4. Click **Add Webhook**.

The new endpoint shows up in the table below with its URL, subscribed events, status (Active by default), and a copy button for the secret.

> **Workspace cap is 5 endpoints**
> If you need to fan out further, point one SourceLoop endpoint at a router (Zapier multi-step Zap, Make scenario, n8n, your own dispatcher) and let it spread the event to as many downstream destinations as you need. This also lets you transform the payload before forwarding.

## Step 3: Verify the receiver works

The cleanest way to test: trigger a real lead.

1. Open your site in an **incognito window**.
2. Submit a form, book a meeting, or fire whatever triggers a SourceLoop conversion.
3. Within seconds, your endpoint should receive the POST.

If you'd rather test without a real lead, edit any existing lead in the Contacts Hub (change status, add a note) and watch for the **Updated Lead** webhook to fire.

To inspect the actual headers and body your endpoint receives, point your URL temporarily at a service like [webhook.site](https://webhook.site) or [requestbin.com](https://requestbin.com), trigger a lead, and read the captured request. Once you know the shape, switch the URL to your real endpoint.

## Step 4: Sign-verify on your receiver

The `X-Webhook-Secret` header carries the exact secret you set (or that SourceLoop generated). On your receiving server, compare the incoming header to the expected value and reject anything that doesn't match.

Node.js / Express:

```js
app.post("/sourceloop-webhook", express.json(), (req, res) => {
  const got = req.header("X-Webhook-Secret");
  if (got !== process.env.SOURCELOOP_WEBHOOK_SECRET) {
    return res.status(401).end();
  }
  const event = req.header("X-Webhook-Event"); // "created" or "updated"
  const lead = req.body;
  // ...your logic here
  res.status(200).end();
});
```

Python / Flask:

```python
@app.post("/sourceloop-webhook")
def sourceloop_webhook():
    got = request.headers.get("X-Webhook-Secret", "")
    if got != os.environ["SOURCELOOP_WEBHOOK_SECRET"]:
        return "", 401
    event = request.headers.get("X-Webhook-Event")  # "created" or "updated"
    lead = request.json
    # ...your logic here
    return "", 200
```

> **Always check the secret**
> Webhook URLs aren't secret on their own. Any backend that knows your URL can POST to it and look like SourceLoop. The X-Webhook-Secret header is what proves the request actually originated from your SourceLoop workspace.

## Common downstream patterns

### Slack alert on every new lead

Slack's [incoming-webhook URL](https://api.slack.com/messaging/webhooks) accepts a POST body, but it expects Slack's own format (`{text: "..."}`), not SourceLoop's lead JSON. Two options:

- **Wrap the lead in a Zap / Make scenario.** Point SourceLoop at a Catch Webhook trigger, transform the body into a Slack message, post it. Cleanest.
- **Use a small middleware function.** Cloudflare Worker, Vercel Edge Function, or AWS Lambda that accepts SourceLoop's payload and forwards a Slack-shaped message.

### Zapier / Make routing

Zapier's "Webhooks by Zapier -> Catch Hook" trigger accepts SourceLoop's payload as-is. Map the lead fields into the next Zap step (add to Google Sheets, create a task in Asana, send to Mailchimp, whatever).

### Server-side enrichment

POST the SourceLoop payload into your backend, look up the company via Clearbit / Apollo / ZoomInfo, then forward the enriched lead onward to your CRM. Use **Updated Lead** as the next-stage trigger so your CRM sees the enriched record.

### Custom CRM sync

If your CRM doesn't have a native SourceLoop integration, build a thin shim: receive the POST, map the lead fields to your CRM's contact model, call your CRM's API to upsert. Use the `X-Webhook-Event` header to decide whether to create (`created`) or update (`updated`).

## Monitor and debug deliveries

Setup -> Outgoing Webhooks shows a **Recent Webhook Logs** table beneath the webhook list, every delivery with:

| Column | What it shows |
|---|---|
| **URL** | Which endpoint received the request |
| **Event** | New Lead or Updated Lead |
| **Response code** | HTTP status your endpoint returned |
| **Response body** | First few hundred characters of your endpoint's response, useful for reading error messages |
| **Error** | Network-level failure (DNS, TLS, timeout) if the request didn't reach your server at all |
| **Sent at** | Delivery timestamp |

The most common failures:

- **401 / 403 from your endpoint** — your server rejected the request. Usually a wrong secret check or missing auth header. Compare the `X-Webhook-Secret` against your expected value.
- **404** — wrong URL. Recopy from your downstream tool.
- **5xx** — your server errored. Check your application logs and fix the handler; SourceLoop currently doesn't auto-retry, so the missed event won't be sent again unless you re-trigger it (e.g., re-save the lead).
- **Network error** — usually a TLS / DNS issue, an HTTPS-only endpoint receiving from an HTTP URL, or a downstream that's been offline too long.

## What's next

- **Receive leads into SourceLoop from any tool:** [How to use SourceLoop Incoming Webhooks](/help/incoming-webhooks-overview/) for the reverse direction.
- **Native CRM sync instead of webhooks:** [Connect HubSpot](/help/connect-hubspot-to-sourceloop/), [Connect Salesforce](/help/connect-salesforce-to-sourceloop/), or [Connect Pipedrive](/help/connect-pipedrive-to-sourceloop/) cover the bidirectional field-mapping case.
- **Browse leads sent by webhooks in the UI:** [Contacts (Leads) table](/help/contacts-leads-table/) walks through the same data your webhook receives.

## Frequently Asked Questions

### How quickly are outgoing webhooks delivered?

Webhooks fire within seconds of the triggering event. New lead webhooks fire the moment SourceLoop creates the lead in the Contacts Hub; updated lead webhooks fire whenever an editable field on the lead changes (status, qualified, lead score, expected revenue, revenue, notes, or a custom field).

### What's the difference between New Lead and Updated Lead?

New Lead fires once per contact, on the initial conversion (the form submit, meeting booking, chat handle, payment, or CRM import that created the row). Updated Lead fires every time an editable field changes afterwards, manually in the UI, via API, via CRM sync, or by a teammate's edit. Subscribe to one or both depending on what your downstream system needs.

### How do I verify a webhook is really from SourceLoop?

Every delivery carries an X-Webhook-Secret header containing the secret you set (or that SourceLoop auto-generated) when you created the endpoint. Compare the header to the expected secret on your receiving server. The header is sent as a literal value, not an HMAC; if you need an HMAC-signed delivery for SOC 2 / PCI compliance, contact hello@sourceloop.ai.

### Will I get the same webhook twice if a field is edited multiple times?

Yes, one Updated Lead webhook fires per save. There's no client-side dedup. If you don't want to react to repeat edits, key your downstream logic on the lead's id and the changed fields (the full lead payload is sent each time, so you can diff).

### How many endpoints can I configure?

Up to five active webhook endpoints per workspace. Each can subscribe to New Lead, Updated Lead, or both. If you need more, fan out from a single SourceLoop webhook into a router like Zapier or your own backend.

### What happens if my endpoint is down or returns an error?

SourceLoop attempts one delivery per event and logs the response in the Recent Webhook Logs table at the bottom of Setup -> Outgoing Webhooks. There's currently no automatic retry on 5xx; build idempotent receivers so that re-sending a missed event later (manually re-saving the lead, or contacting support to replay) is safe.

### Can I limit which leads get sent (e.g., only paid leads, only qualified ones)?

Subscribing to events is all-or-nothing per endpoint today. To filter, accept every delivery on your side and ignore the ones that don't match. Common filter fields, lead type (web form / meeting / chat / payment), qualified flag, lead score, status, or any custom field, are all on the payload.

### Where do I see what's been sent?

Setup -> Outgoing Webhooks shows a Recent Webhook Logs table with the URL, event, response code, response body, error message, and timestamp for every delivery attempt. Use it to confirm a delivery happened and to diagnose 4xx / 5xx responses from your endpoint.
