Setting one up
1
Add the endpoint
Integrations → Webhooks → New endpoint. Give it a name and a publicly reachable
https:// URL.2
Choose your events
Tick the events you want. For each one you can also tick which fields the payload
should carry — see choosing fields.
3
Save the signing secret
It’s shown once, on creation, and starts with
whsec_. You need it to
verify signatures. Store it in your secret manager before
closing the dialog.4
Send a test
Use Send test on the endpoint’s page. It goes through the real delivery path —
same signing, same logging — so a test that arrives proves the setup works.
Events
Editing a lead’s stage fires both
lead.updated and lead.stage.changed. Subscribe to
the one you actually mean — taking both means handling each move twice.lead.won and lead.lost follow the won/lost flags on your pipeline stages, not the stage
name. If you renamed “Won” to “Closed — signed”, it still fires.The payload
Every delivery has the same envelope. Onlydata differs by event:
source is worth using if your workflow writes back into Linkiasoft: it lets you tell a
change your own automation made from one a person made, which is how you avoid a loop.
Choosing which fields to send
Each event has a set of fields, and you pick which ones we include. Untick anything the receiving system doesn’t need — particularlytext, customer_phone and email, which
are the fields you’re least likely to want sitting in a third-party tool’s logs.
Leaving all fields ticked is not the same as listing them out. All-ticked means “send
whatever this event carries”, so a field we add later is included automatically. If you
untick even one, you get exactly the set you chose and nothing new.
Verifying the signature
Every request carries these headers:v1 is an HMAC-SHA256, keyed with your signing secret, over the string
`${t}.${rawBody}` — the timestamp, a literal dot, then the raw request body.
timingSafeEqual, compare_digest,
hash_equals — not ==.
The timestamp is inside the signed string on purpose. Signing the body alone would leave
every delivery you receive replayable by anyone who captured one, forever. Checking
t
against a tolerance is the half that makes it useful, so don’t skip it.Retries
Answer2xx and we consider the delivery done. Anything else:
Retries back off: 30 seconds, 2 minutes, 10 minutes, 1 hour, 6 hours — five attempts
across roughly eight hours, then we stop. Every attempt appears in the log with its own
X-Linkia-Attempt number, and all attempts at one delivery share the same envelope id.
Twenty consecutive failures disables the endpoint. We stop calling it, the page shows
why, and events stop being queued for it. This exists so an abandoned test URL doesn’t
generate failing deliveries forever. Fix the endpoint and press Enable again — that
clears the streak. Nothing is replayed automatically; use the log to re-send what matters.
The delivery log
Each endpoint keeps its most recent 300 attempts, with the exact body we sent, the first 2KB of your response, the status code, and how long it took. Open one to see the request and response side by side, or press the retry icon to send it again. Lifetime totals — how many requests we’ve ever made and how many failed — are counted separately and are not affected by the 300-row limit.The log is a debugging tool, not an archive. On a busy endpoint 300 attempts can be under
an hour. If you need durable history, record deliveries on your side keyed by envelope
id.Custom headers
If your endpoint sits behind a proxy that wants its own header, add it under the endpoint’s settings. Headers we set ourselves —Content-Type, User-Agent and every
X-Linkia-* header — can’t be overridden, since a custom header that could replace the
signature would make deliveries trivially forgeable.
Rotating the secret
Rotate secret on the endpoint page issues a new one and shows it once.Things to watch in production
A slow endpoint costs you events, not us. We wait 10 seconds. If your handler does real work — calling other APIs, writing to a slow database — answer2xx immediately and process
in the background. A handler that answers in 11 seconds looks identical to one that is down.
Don’t return 4xx for your own problems. A 422 because your validation is too strict
is terminal on our side: we won’t retry, and the event is gone. Return 5xx for anything
you’d want another attempt at.
Test deliveries look like real ones. webhook.test arrives through the same path with a
valid signature. Handle or ignore the event name; don’t assume every delivery is a real
domain event.
