Managing Webhooks
Register and manage the webhook that receives workflow and account events. For general Admin API information, see the Admin overview. A client may hold one active webhook at a time.
Everything on this page can also be done in the platform dashboard – registering the endpoint, switching payload mode, choosing events, signing, custom headers, and inspecting recent deliveries. Use these endpoints when you want it in code; the two reach the same webhook.
In the Node SDK these endpoints are methods of admin.webhooks on the admin client, named after the endpoint: webhook.setPayloadMode is admin.webhooks.setPayloadMode(). Each method returns the content of result unwrapped: the webhook (TWebhookSubscription) from set, the other set… methods, deleteHeader, revealSecret and rotateSecret; an array from get, eventTypes and deliveries (TWebhookDelivery); and nothing from delete, replayDelivery and sendTest. A failed call throws LinkedApiError – see error handling.
In the Python SDK these endpoints are methods of admin.webhooks on the admin client, in snake_case: webhook.setPayloadMode is admin.webhooks.set_payload_mode(). A method that takes parameters takes them as a params model exported from linkedapi, such as SetWebhookPayloadModeParams. Each method returns the content of result unwrapped: a WebhookSubscription from set, the other set_… methods, delete_header, reveal_secret and rotate_secret; a list from get, event_types and deliveries (WebhookDelivery); and None from delete, replay_delivery and send_test. Fields are snake_case, such as payload_mode and header_names. A failed call raises LinkedApiError – see error handling.
In the CLI these endpoints are the linkedin admin webhook commands, in kebab-case: webhook.setPayloadMode is set-payload-mode, and webhook.replayDelivery is replay. The webhook id and the other required values are positional arguments. The signing secret and header values never appear on screen or on the command line unless you ask – see secrets in the CLI and header values in the CLI.
webhook.set
Register the webhook. Enforces a maximum of one active webhook per client. There is no endpoint that changes the destination url – for that, delete the webhook and set a new one. Payload mode, the event selection, signing and custom headers are all changeable in place.
curl -X POST https://api.linkedapi.io/admin/webhook.set \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{
"url": "https://example.com/hooks/linkedapi",
"payloadMode": "fat"
}'In the CLI this is linkedin admin webhook set:
linkedin admin webhook set <url> [flags]| Flag | Type | Description |
|---|---|---|
--payload-mode | string | fat (default) or thin |
--signing | boolean | Sign every delivery. The returned secret is shown as [REDACTED] |
--events | string | Comma-separated event types and <namespace>.* wildcards. Omitted means every event |
--headers-stdin | boolean | Read the custom headers from stdin, as a JSON object of names and values |
cat headers.json | linkedin admin webhook set https://example.com/hooks \
--signing --events "workflow.*,inbox.messageReceived" --headers-stdinBody:
url– HTTPS endpoint that will receive deliveries. Required.payloadMode–fat(default) orthin. See payload modes. Optional.signingEnabled–trueto sign every delivery. Defaults tofalse. Whentrue, the response includessecret. See signature verification. Optional.headers– custom headers sent with every delivery, as a name-to-value object. See custom delivery headers. Optional.events– the events to deliver: exact event types and<namespace>.*wildcards, such as["workflow.completed", "inbox.*"]. Omitted ornullmeans every event. See event selection. Optional.
Response:
{
"success": true,
"result": {
"webhook": {
"id": "whs-...",
"url": "https://example.com/hooks/linkedapi",
"payloadMode": "fat",
"signingEnabled": false,
"headerNames": [],
"events": null,
"isActive": true,
"createdAt": "2026-06-25T12:00:00.000Z"
}
}
}id– webhook identifier, used by the other endpoints.url– the registered destination.payloadMode– current payload mode.signingEnabled– whether deliveries are signed.headerNames– names of the configured custom headers, never their values.events– the stored event selection, de-duplicated and sorted, ornullfor every event.isActive– whether the webhook is active.createdAt– ISO 8601 timestamp.secret– the signing secret. Present only on awebhook.setthat enabled signing, onwebhook.setSigningwhen enabling, onwebhook.revealSecretand onwebhook.rotateSecret. Every other response omits it.
A
urlthat is a bare IP address in a private or internal range is rejected. Deliveries are only ever sent to public addresses.
webhook.get
List the active webhook for this client. Returns an array with at most one entry. Never returns the signing secret – use webhook.revealSecret for that.
curl -X POST https://api.linkedapi.io/admin/webhook.get \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here"Response:
{
"success": true,
"result": {
"webhooks": [
{
"id": "whs-...",
"url": "https://example.com/hooks/linkedapi",
"payloadMode": "fat",
"signingEnabled": false,
"headerNames": [],
"events": null,
"isActive": true,
"createdAt": "2026-06-25T12:00:00.000Z"
}
]
}
}webhook.setPayloadMode
Switch the payload mode between fat and thin.
curl -X POST https://api.linkedapi.io/admin/webhook.setPayloadMode \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-...", "payloadMode": "thin" }'Body:
id– webhook identifier. Required.payloadMode–fatorthin. Required.
webhook.setEvents
Replace the event selection in place. null goes back to every event. Saving the selection the webhook already has, even reordered or with duplicates, changes nothing. Returns the updated webhook.
curl -X POST https://api.linkedapi.io/admin/webhook.setEvents \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-...", "events": ["workflow.completed", "inbox.*"] }'events is typed as Array<TWebhookEventSelector> | null, so TypeScript flags an unknown event type or wildcard at compile time. An entry that Linked API rejects throws LinkedApiError with type invalidRequestPayload. The same applies to events in set.
The params model checks each entry against the WebhookEventSelector values as you build it and raises pydantic.ValidationError for an unknown one, before any request is sent. An entry that Linked API rejects raises LinkedApiError with type invalidRequestPayload. The same applies to events in SetWebhookParams.
In the CLI this is linkedin admin webhook set-events. It takes the selection as one comma-separated argument, or --all for every event (null) – exactly one of the two:
linkedin admin webhook set-events <id> <events>
linkedin admin webhook set-events <id> --allAn empty entry, such as one left by a trailing comma, is refused with exit code 5 before anything is sent. set --events takes the same list.
Body:
id– webhook identifier. Required.events– exact event types and<namespace>.*wildcards, ornullfor every event. Required, so a request without it never clears a selection by accident.
An invalid entry is rejected with invalidRequestPayload naming it, and the stored selection stays as it was.
webhook.eventTypes
List the event types a selection can name, in the order Linked API defines them. webhook.test is not among them, because it is always delivered. It works before you register a webhook, so you can build a selection first.
curl -X POST https://api.linkedapi.io/admin/webhook.eventTypes \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here"Response:
{
"success": true,
"result": {
"eventTypes": [
"workflow.created",
"workflow.started",
"workflow.completed",
"account.active",
"account.reconnectionRequired",
"account.frozen",
"account.deleted",
"inbox.messageReceived",
"inbox.messageSent",
"network.connectionAccepted",
"network.connectionAdded",
"network.connectionRequestReceived"
]
}
}The wildcards are not listed: each namespace in this list, the part before the first ., can be selected as <namespace>.*.
webhook.setSigning
Turn signature verification on or off for this webhook. Returns the signing secret when enabling, so you can configure your verifier in the same step. No other endpoint changes the signing state.
curl -X POST https://api.linkedapi.io/admin/webhook.setSigning \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-...", "signingEnabled": true }'In the CLI this is linkedin admin webhook set-signing <id> with --on or --off, exactly one of the two. When enabling, the returned secret is shown as [REDACTED]; read it with reveal-secret.
Body:
id– webhook identifier. Required.signingEnabled–trueto sign deliveries,falseto stop. Required.
webhook.setHeader
Add or replace one custom header, leaving the others untouched. This is the endpoint to use when editing an existing configuration, because header values cannot be read back.
curl -X POST https://api.linkedapi.io/admin/webhook.setHeader \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-...", "name": "Authorization", "value": "Bearer your_endpoint_token" }'In the CLI this is linkedin admin webhook set-header. The value is never an argument: it comes from stdin or from an environment variable, exactly one of the two.
linkedin admin webhook set-header <id> <name> --value-stdin
linkedin admin webhook set-header <id> <name> --value-env <variable>| Flag | Type | Description |
|---|---|---|
--value-stdin | boolean | Read the value from stdin. One trailing newline is removed |
--value-env | string | Name of the environment variable that holds the value |
An empty value, or an environment variable that is not set, is refused with exit code 5 before anything is sent. See header values in the CLI.
Body:
id– webhook identifier. Required.name– header name. An existing header with the same name, ignoring case, is replaced. Required.value– header value. Required.
webhook.deleteHeader
Remove one custom header by name. The name is matched ignoring case.
curl -X POST https://api.linkedapi.io/admin/webhook.deleteHeader \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-...", "name": "Authorization" }'Body:
id– webhook identifier. Required.name– header name to remove. Required.
webhook.setHeaders
Replace all custom headers at once. {} or null removes every header. Use this only when you hold every value – it is the programmatic path; for editing one header, use webhook.setHeader.
curl -X POST https://api.linkedapi.io/admin/webhook.setHeaders \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-...", "headers": { "Authorization": "Bearer your_endpoint_token" } }'In the CLI this is linkedin admin webhook set-headers <id> with exactly one of two flags:
| Flag | Type | Description |
|---|---|---|
--stdin | boolean | Read the headers from stdin, as a JSON object of names and values |
--clear | boolean | Remove every header (sends null) |
An empty value in the JSON object is refused with exit code 5 before anything is sent; an empty object {} clears every header, like --clear.
Body:
id– webhook identifier. Required.headers– name-to-value object, ornullto clear. Required.
webhook.revealSecret
Return the current signing secret. Use it when you need the secret again after enabling signing – for example when configuring a second environment.
curl -X POST https://api.linkedapi.io/admin/webhook.revealSecret \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-..." }'In the CLI this is linkedin admin webhook reveal-secret <id>, and it needs exactly one of two flags that say where the secret goes:
| Flag | Type | Description |
|---|---|---|
--print-secret | boolean | Write only the secret and a newline to stdout, ignoring --json and --fields, so it fits $(...) |
--secret-file | string | Create a new file with mode 0600 holding the secret, and print its path instead of the secret. A path that already exists is refused |
Body:
id– webhook identifier. Required.
webhook.rotateSecret
Generate a new signing secret, store it and return it. This replaces a leaked secret without deleting the webhook, which would change its id.
curl -X POST https://api.linkedapi.io/admin/webhook.rotateSecret \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-..." }'In the CLI this is linkedin admin webhook rotate-secret <id>. It warns on stderr that the old secret stops working at once, and takes the same two flags as reveal-secret, both optional. With --secret-file, the file is created before the secret rotates, so a path that already exists stops the command before anything changes. Without either flag, the new secret is shown as [REDACTED]; read it with reveal-secret.
Body:
id– webhook identifier. Required.
Rotation takes effect immediately and there is no overlap period: the old secret stops signing the moment the new one is stored, so every delivery fails verification until your endpoint holds the new value. Rotate when you are ready to deploy it.
webhook.delete
Delete the webhook. This is a soft delete: the delivery history is preserved and any still-pending deliveries are dropped. Because only active webhooks count toward the one-per-client limit, you can register a fresh webhook afterwards.
curl -X POST https://api.linkedapi.io/admin/webhook.delete \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "id": "whs-..." }'Body:
id– webhook identifier. Required.
webhook.deliveries
Return the most recent deliveries (newest first) as a debug feed – useful for confirming an endpoint is receiving and acknowledging events.
curl -X POST https://api.linkedapi.io/admin/webhook.deliveries \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here"Response:
{
"success": true,
"result": {
"deliveries": [
{
"id": "whd-...",
"eventType": "workflow.completed",
"eventId": "workflow.completed:wf-...",
"status": "success",
"attempts": 1,
"responseStatusCode": 200,
"lastError": null,
"createdAt": "2026-06-25T12:00:00.000Z",
"updatedAt": "2026-06-25T12:00:01.000Z"
}
]
}
}id– delivery identifier, passed towebhook.replayDelivery.eventType– the eventtypethat was delivered.eventId– the envelopeidof the delivered event.status–pending,delivering,success, orfailed.attempts– delivery attempts made so far.responseStatusCode– HTTP status your endpoint returned on the last attempt, ornull.lastError– error text from the last failed attempt, ornull.createdAt/updatedAt– ISO 8601 timestamps.
webhook.replayDelivery
Re-arm an already-settled (success or failed) delivery for redelivery. The same eventId is reused, so consumer-side deduplication still applies – this is a true redelivery, not a new event.
curl -X POST https://api.linkedapi.io/admin/webhook.replayDelivery \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here" \
-d '{ "deliveryId": "whd-..." }'Body:
deliveryId– delivery identifier fromwebhook.deliveries. Required.
webhook.sendTest
Emit a synthetic webhook.test event to the active webhook through the normal delivery path. Its data carries a single message field. Use it to verify a freshly registered endpoint without waiting for a real event.
curl -X POST https://api.linkedapi.io/admin/webhook.sendTest \
-H "Content-Type: application/json" \
-H "linked-api-token: linked_your_token_here"