V2 Webhooks
Verify workflow notifications and retrieve their public resources.
V2 webhooks notify your HTTPS receiver when a discovery, import or monitor run reaches a confirmed final outcome. They contain public IDs you can read through the V2 API and signatures you can verify before processing the event.
Choose the format
Destinations default to legacy. For a monitor, set config.format to v2 and configure config.webhook_url when creating or updating it. The URL must resolve only to public addresses. Linkt checks it again at delivery and follows no redirect. A 3xx response is not a success and is not retried. It is treated like a 4xx response. A DNS lookup failure is retryable on legacy delivery. A private address or a disallowed scheme is not. V2 delivery does not retry a lookup failure, a private address, or a disallowed scheme.
For an existing discovery or import destination, set webhook_format: "v2" in its saved configuration. The destination stays on the configuration that started the run. Use a public run ID from that configuration to provision its secret.
An independent monitor can receive V2 notifications without a source campaign. Legacy notifications remain suppressed when no campaign exists.
Provision the verification secret
Call rotate_webhook_secret with an organization API key whose effective access includes webhook.write. The key and its owner both need that level. This operation is available through HTTP only. See Permissions.
curl --fail-with-body --request POST "$LINKT_API_URL/v2/webhook-secrets" \
--header "x-api-key: $LINKT_API_KEY" \
--header "Content-Type: application/json" \
--data '{"monitor_id":"0190a4e2-0000-7000-8000-000000000009"}'To select a discovery or import destination, send {"run_id":"<public run ID>"} instead. Send exactly one selector. The destination must already use the V2 format and have a webhook URL.
The response contains secret and previous_valid_until. Save the secret in your receiver's secret store immediately: it is shown once and cannot be retrieved later. It authenticates webhook signatures and grants no API access.
Call the same operation to rotate. Linkt sends signatures for both secrets for 24 hours, allowing your receiver to switch to the new secret. Another rotation returns 409 until that overlap ends. Changing the destination URL requires a new credential. A 503 response means signing is unavailable; retry after the service is restored.
Event contract
Download the JSON schema.
The sample event_id is illustrative. It is a version 5 UUID, not a time-ordered UUIDv7. Linkt sets event_id to UUID version 5 of the public run ID and the event type. One terminal outcome of one run keeps that id across retries and across a later delivery of the same outcome. Each attempt uses a new timestamp and signature. Resource IDs in data stay UUIDv7.
data.campaign appears when the run has an owned source campaign. data.monitor appears for monitors. Resource arrays contain only IDs from the run's created or updated records. Merged accounts and people use the survivor ID. Deleted identities keep their ID without expansion; a later read may return 404. Sender names, emails and private research content are omitted.
Retrieve the run through GET /v2/runs/{id}, monitors through /v2/monitors/{id}, campaigns through /v2/campaigns/{id}, accounts through /v2/accounts/{id}, people through /v2/people/{id}, and signals through /v2/signals/{id}. Use your organization's API key for those reads. A notification grants no additional access.
Final event types are run.succeeded, run.failed, run.canceled and run.skipped. Cancellation emits run.canceled after execution confirms a stop, never at the cancellation request. A crashed run emits run.failed. A run that completes early because of cutoff evidence also emits run.failed; its recorded data.run.status remains succeeded, matching the run API. occurred_at is the confirmed workflow end time, or the reaper's confirmation time when it forces cancellation after a stalled worker.
Verify before processing
Linkt sends X-Linkt-Event-Id, X-Linkt-Timestamp and X-Linkt-Signature. The signature is HMAC-SHA256 over the timestamp, a period and the exact raw request body. Keep the raw bytes; parsing and serializing JSON changes them.
Allow at most five minutes between the request timestamp and your receiver's clock. Verify a signature before parsing or processing. Then require the body's event_id to equal X-Linkt-Event-Id. Keep clocks synchronized and compare signatures in constant time.
Store processed event IDs durably with your business update, using a unique constraint or an equivalent transaction. Return success for an already processed event. One hook invocation sends one event. A later hook, or the reaper after the worker, can POST the same event_id again. Dedupe on event_id. Linkt preserves the event ID and body across its five retries, but computes a fresh timestamp and signature on each attempt. Retries wait 2, 4, 8, 16 and 32 seconds; there is no historical replay service.
Migrate a receiver
Keep accepting your existing legacy payload while preparing V2 verification and ID-based reads. Select v2, provision a secret, and confirm a signed event with a controlled receiver. Runs already executing on older workers can still send legacy payloads. When V2 is disabled, every destination uses the legacy payload.
A delivery failure does not change the run's research outcome. Linkt saves the run, publishes the event, and finishes completion work before the first V2 delivery. Use the run API when you need its current status. The Webhook URL reference describes the supported legacy format.