- Prosyo Docs
- Integrations
- Signed webhooks
On this page
Looking for the opposite direction, to start Prosyo outreach when something happens in another app? See Custom triggers.
Prosyo can POST a JSON event to any HTTPS URL when something happens. Use this to connect n8n, Zapier, Make, a data warehouse or your own backend.
Events#
| Event | Fires when |
|---|---|
prospect.created | A prospect is added by import, LinkedIn search, the Chrome extension or by hand |
prospect.stage_changed | A prospect moves stage in the table, board, side panel or Inbox |
prospect.enriched | AI research or a first-touch line is saved |
conversation.reply_received | A LinkedIn or email reply stops the sequence |
campaign.launched | A campaign starts sending |
campaign.paused | A running campaign is paused |
account.needs_reconnect | A LinkedIn or email account needs reconnecting |
Set up#
- Create an endpoint that accepts POST with a JSON body. It must be HTTPS and publicly reachable. HTTP, private and internal addresses are blocked.
- In Prosyo, go to Integrations → Custom webhook.
- Paste the URL into HTTPS endpoint.
- Click Connect.
- Copy the Signing secret. It's shown once. You can rotate it later.
- Choose the Events to send.
- Under Field mapping, choose which prospect fields appear in
data. - Click Test connection to send a sample.
Payload#
{
"id": "evt_01HQEXAMPLE",
"type": "conversation.reply_received",
"created_at": "2026-08-17T12:00:00.000Z",
"workspace_id": "ws_…",
"data": {
"prospect_id": "pr_…",
"campaign_id": "cp_…",
"event": "conversation.reply_received",
"email": "alex@acme.com",
"full_name": "Alex Rivera",
"title": "Head of Sales",
"company": "Acme",
"linkedin_url": "https://www.linkedin.com/in/alexrivera",
"stage": "replied",
"personalized_message": "…"
}
}The fields inside data follow your field mapping. Integrations → Custom webhook always shows a sample payload that matches the live format.
Headers#
| Header | Value |
|---|---|
Content-Type | application/json |
X-Prosyo-Signature | Hex HMAC-SHA256 of the raw body, using your signing secret |
X-Prosyo-Event | The event type, for example conversation.reply_received |
X-Prosyo-Delivery-Id | Unique ID for this delivery. Use it to ignore duplicates. |
User-Agent | Prosyo-Webhooks/1.0 |
Verify the signature#
Compute HMAC-SHA256 of the raw request body with your signing secret, as lowercase hex, and compare it to X-Prosyo-Signature. Reject the request if they don't match.
Node.js
import crypto from "node:crypto";
function isValidProsyoRequest(rawBody, signatureHeader, secret) {
const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader || "");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Python
import hmac, hashlib
def is_valid_prosyo_request(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header or "")PHP
$expected = hash_hmac('sha256', file_get_contents('php://input'), $secret);
$valid = hash_equals($expected, $_SERVER['HTTP_X_PROSYO_SIGNATURE'] ?? '');Always sign the raw body. Parsing the JSON and re-encoding it changes the bytes, and the signature won't match.
Responses and retries#
- Return any 2xx status within about 10 seconds to mark the delivery successful.
- Failed deliveries are retried with backoff.
- Recent deliveries on the integration page shows each delivery and the last error.
- Use
X-Prosyo-Delivery-Idto skip duplicates, because a retry can deliver the same event twice.
Rotate the secret#
Rotate the signing secret from Integrations → Custom webhook. Update the secret in your workflow straight away. Deliveries signed with the new secret fail verification until you do.
