Skip to content

Webhooks endpoints ​

These REST routes manage the endpoints that receive Link Manager Pro events. You can create and edit a webhook, send a test, read its recent deliveries and reveal or regenerate its signing secret.

A webhook is an HTTPS address that receives an HTTP request when a Link Manager Pro event happens. Sign-in and the response envelope are in the REST API overview.

All paths are relative to /wp-json/link-manager-pro/v1. Examples use https://example.com and USER:APPLICATION_PASSWORD. Inside WordPress, send the X-WP-Nonce header instead.

Every route here needs an administrator (manage_options). The request your receiver gets, its headers and signature are described in Events, webhooks and AutomatorWP. The screen is explained in Webhooks and AutomatorWP.

A validation failure is a 422 with message and an errors object that lists messages per field. A missing webhook is a 404 with code, message, data.status and status.

json
{
  "code": "not_found",
  "message": "Webhook not found.",
  "data": { "status": 404 },
  "status": 404
}

The webhook object ​

Every route that returns a webhook uses these fields. The signing secret is never in the list. The list and PUT return only a masked form of it.

FieldTypeWhat it holds
idintegerThe webhook ID.
namestringA label for you.
target_urlstringThe address that receives the request.
eventsarray of stringsThe events it receives: link_clicked, broken_link_found, link_created, broken_link_fixed.
statusstringactive or paused. A paused webhook receives nothing.
methodstringThe HTTP method: GET, POST, PUT, PATCH or DELETE.
ignore_signingstringyes sends requests without a signature. no signs them.
headersobjectCustom headers added to each request, as name and value strings. Empty as [] when there are none.
has_secretstringyes or no. A secret is always made on create, so this is yes unless the stored secret cannot be read.
secret_unreadablestringyes when a secret is stored but can't be decrypted, for example after the site's AUTH_KEY changed. Then has_secret is no and secret_mask is empty. Otherwise no.
secret_maskstringThe secret with every character but the last 4 replaced by •. Empty when there is no secret.

Signing. With ignore_signing set to no and a secret stored, each request carries an X-LMP-Signature header. Its value is sha256= and the hex HMAC-SHA256 of t=<timestamp>.<body>, made with the raw secret. Verify the signature has receiver code. Setting ignore_signing to yes keeps the secret but stops signing. With ignore_signing set to no and an unreadable secret, the delivery fails with secret_unreadable and is never sent unsigned. Regenerate the secret to fix it.

The secret. The plugin stores it encrypted. Create and regenerate return the raw value once in a secret field. GET /webhooks/{id}/secret reveals it at any time.

Failures. The plugin counts failed deliveries in a row for each webhook. After 15 in a row it sets status to paused. Failed deliveries with secret_unreadable count too. The count is not part of the object. Saving a new secret resets it to 0. To resume a paused webhook, send PUT /webhooks/{id} with status set to active. Resuming does not reset the count. Only a delivered request or a new secret does.

The delivery object ​

A delivery is one queued event for one webhook. The plugin sends it in the background, up to 5 tries per delivery.

FieldTypeWhat it holds
idintegerThe delivery ID.
webhook_idintegerThe webhook it belongs to.
eventstringThe event name, for example link_clicked.
statusstringpending, sending, success or failed.
attemptsintegerTries made so far.
response_codeinteger or nullThe HTTP status of the last try. 0 when the receiver gave no answer. null before the first try.
last_errorstring or nullWhy the last try failed, for example http_500 or secret_unreadable. null when there is no error.
created_atstring or nullWhen the event was queued, UTC Y-m-d H:i:s.
delivered_atstring or nullWhen the receiver accepted it, UTC Y-m-d H:i:s. null until then.

Status values.

StatusMeaning
pendingQueued, or waiting for the next try after a failed one.
sendingA background run has taken it and is sending it now.
successThe receiver answered with a status from 200 to 299.
failedThe fifth try failed, or the webhook was deleted.

A try that gets any other status, a timeout, or a blocked address fails. A failed try that is not the fifth puts the delivery back to pending with a wait of about 1 minute, then 5 minutes, then 30 minutes, then 2 hours. Details are in Events, webhooks and AutomatorWP.

Webhooks ​

List webhooks ​

GET /webhooks

Returns every webhook, oldest first. There is no paging.

Permission: manage_options (Permissions)

Query parameters: None.

Response: 200. data.data is an array of webhook objects.

bash
curl -u USER:APPLICATION_PASSWORD \
  https://example.com/wp-json/link-manager-pro/v1/webhooks
json
{
  "success": true,
  "data": {
    "data": [
      {
        "id": 31,
        "name": "Zapier clicks",
        "target_url": "https://hooks.example.com/catch/123",
        "events": ["link_clicked", "link_created"],
        "status": "active",
        "method": "POST",
        "ignore_signing": "no",
        "headers": { "X-Source": "lmp" },
        "has_secret": "yes",
        "secret_mask": "••••••••••••••••••••••••••••••••••••a1b2"
      }
    ]
  }
}

Create a webhook ​

POST /webhooks

Creates a webhook and returns it with its signing secret. A secret is generated when you send none.

Permission: manage_options

Body parameters:

NameTypeRequiredDefaultWhat it does
namestringYesnoneA label. 191 bytes or fewer.
target_urlstringYesnoneWhere requests go. A valid URL of 2048 bytes or fewer. It must use https and its host must resolve only to public addresses. Any port is allowed.
eventsarray of stringsYesnoneAt least one of link_clicked, broken_link_found, link_created, broken_link_fixed.
statusstringNoactiveactive or paused.
methodstringNoPOSTGET, POST, PUT, PATCH or DELETE. A GET request has no body.
ignore_signingboolean or stringNofalsetrue, 1, yes or on turns signing off. Anything else leaves it on.
secretstringNogeneratedThe signing secret, 255 bytes or fewer. Empty or missing generates a random 40 character one.
headersobjectNononeCustom headers as name and value pairs.

Headers. Names and values are cleaned as plain text. An entry with a non-string name or a value that is not a string, number or boolean is dropped. The code sets no limit on the number of headers. A header named Content-Type, X-LMP-Event, X-LMP-Delivery, X-LMP-Timestamp or X-LMP-Signature is stored but never sent, whatever its case.

Response: 200, with the message "Webhook created." data is the webhook object plus one field:

FieldTypeWhat it holds
secretstringThe raw signing secret.
bash
curl -X POST -u USER:APPLICATION_PASSWORD \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Zapier clicks",
    "target_url": "https://hooks.example.com/catch/123",
    "events": ["link_clicked", "link_created"],
    "headers": { "X-Source": "lmp" }
  }' \
  https://example.com/wp-json/link-manager-pro/v1/webhooks
json
{
  "success": true,
  "data": {
    "id": 31,
    "name": "Zapier clicks",
    "target_url": "https://hooks.example.com/catch/123",
    "events": ["link_clicked", "link_created"],
    "status": "active",
    "method": "POST",
    "ignore_signing": "no",
    "headers": { "X-Source": "lmp" },
    "has_secret": "yes",
    "secret_mask": "••••••••••••••••••••••••••••••••••••a1b2",
    "secret": "Qz7hT0w3Lk9mN2pR5vX8cB1dF4gJ6sUyaa1b2"
  },
  "message": "Webhook created."
}

Errors:

StatusWhen
422name, target_url or events is missing or empty.
422name is over 191 bytes, target_url is not a URL or is over 2048 bytes, or secret is over 255 bytes.
422An event is not one of the four. The error is keyed events.0, events.1 and so on.
422status or method is not an allowed value.
422target_url does not use https, does not resolve, or resolves to a private or reserved address. errors.target_url says "The webhook URL must use https and point to a public host."

Update a webhook ​

PUT /webhooks/{id}

Changes the fields you send and leaves the others as they are. It returns the updated webhook without the secret.

Permission: manage_options

Path parameters:

NameTypeWhat it is
idintegerThe webhook ID.

Body parameters: the same fields as Create a webhook, all optional, with the same limits and allowed values.

NameTypeRequiredDefaultWhat it does
namestringNounchangedA new label.
target_urlstringNounchangedA new address. It must pass the same https and public host check.
eventsarray of stringsNounchangedReplaces the list. A name that is not allowed is a 422, as on create.
statusstringNounchangedactive or paused.
methodstringNounchangedThe new HTTP method.
ignore_signingboolean or stringNounchangedSame values as on create. When the key is present, the value is applied.
secretstringNounchangedA new secret. An empty value is ignored, and so is a value equal to the stored secret. A new secret resets the failure count. Setting status to active on a paused webhook resets it too.
headersobjectNounchangedReplaces all custom headers. Send {} or null to remove them.

Response: 200, with the message "Webhook updated." data is the webhook object, without secret.

bash
curl -X PUT -u USER:APPLICATION_PASSWORD \
  -H "Content-Type: application/json" \
  -d '{ "status": "paused" }' \
  https://example.com/wp-json/link-manager-pro/v1/webhooks/31
json
{
  "success": true,
  "data": {
    "id": 31,
    "name": "Zapier clicks",
    "target_url": "https://hooks.example.com/catch/123",
    "events": ["link_clicked", "link_created"],
    "status": "paused",
    "method": "POST",
    "ignore_signing": "no",
    "headers": { "X-Source": "lmp" },
    "has_secret": "yes",
    "secret_mask": "••••••••••••••••••••••••••••••••••••a1b2"
  },
  "message": "Webhook updated."
}

Errors:

StatusWhen
404No webhook has that ID.
422A sent field breaks a limit or allowed value listed under Create a webhook.
422target_url fails the https and public host check. errors.target_url holds the message.

Delete a webhook ​

DELETE /webhooks/{id}

Removes a webhook and all of its stored deliveries.

Permission: manage_options

Path parameters:

NameTypeWhat it is
idintegerThe webhook ID.

Response: 200, with the message "Webhook deleted." data is null.

json
{
  "success": true,
  "data": null,
  "message": "Webhook deleted."
}

Errors:

StatusWhen
404No webhook has that ID.

List a webhook's deliveries ​

GET /webhooks/{id}/deliveries

Returns the 50 most recent deliveries of one webhook, newest first.

Permission: manage_options

Path parameters:

NameTypeWhat it is
idintegerThe webhook ID.

Query parameters: None. There is no paging, and no way to ask for more than 50.

Response: 200. data.data is an array of delivery objects. The queued payload is not included. The plugin keeps delivered rows for 7 days and failed rows for 30 days.

bash
curl -u USER:APPLICATION_PASSWORD \
  https://example.com/wp-json/link-manager-pro/v1/webhooks/31/deliveries
json
{
  "success": true,
  "data": {
    "data": [
      {
        "id": 904,
        "webhook_id": 31,
        "event": "link_clicked",
        "status": "success",
        "attempts": 1,
        "response_code": 200,
        "last_error": null,
        "created_at": "2026-10-23 09:10:02",
        "delivered_at": "2026-10-23 09:10:07"
      }
    ]
  }
}

Errors:

StatusWhen
404No webhook has that ID.

Reveal a webhook's secret ​

GET /webhooks/{id}/secret

Returns the raw signing secret of one webhook.

Permission: manage_options

Path parameters:

NameTypeWhat it is
idintegerThe webhook ID.

Response: 200. data holds:

FieldTypeWhat it holds
secretstringThe raw secret. Empty when none can be read.
json
{
  "success": true,
  "data": { "secret": "Qz7hT0w3Lk9mN2pR5vX8cB1dF4gJ6sUyaa1b2" }
}

Errors:

StatusWhen
404No webhook has that ID.

Regenerate a webhook's secret ​

POST /webhooks/{id}/regenerate-secret

Replaces the signing secret with a new random one and returns it. The old secret stops working at once, so update your receiver.

Permission: manage_options

Path parameters:

NameTypeWhat it is
idintegerThe webhook ID.

Body parameters: None.

The new secret is 40 letters and digits. The webhook's failure count is reset to 0.

Response: 200, with the message "Signing secret regenerated." data is the webhook object plus the raw secret, as in Create a webhook.

json
{
  "success": true,
  "data": {
    "id": 31,
    "name": "Zapier clicks",
    "target_url": "https://hooks.example.com/catch/123",
    "events": ["link_clicked", "link_created"],
    "status": "active",
    "method": "POST",
    "ignore_signing": "no",
    "headers": { "X-Source": "lmp" },
    "has_secret": "yes",
    "secret_mask": "••••••••••••••••••••••••••••••••••••k3m9",
    "secret": "Bn4wR8yT2xQ6cV0zL5jH9sD1fG3aP7uEoo2k3m9"
  },
  "message": "Signing secret regenerated."
}

Errors:

StatusWhen
404No webhook has that ID.

Send a test delivery ​

POST /webhooks/{id}/test

Sends one request to the webhook right now and returns how it went. The request has the event ping.

Permission: manage_options

Path parameters:

NameTypeWhat it is
idintegerThe webhook ID.

Body parameters: None.

The test goes out at once, signed like a real delivery, and does not use the queue. It is not stored, so it never shows in the deliveries list. It runs even when the webhook is paused. Its data holds message set to Link Manager Pro webhook test. The route answers 200 whether or not the receiver accepted the request. Read ok.

Response: 200. data holds:

FieldTypeWhat it holds
okbooleantrue when the receiver answered with a status from 200 to 299.
response_codeintegerThe receiver's HTTP status. 0 when there was no answer or the address was blocked.
errorstring or nullnull on success. Otherwise http_ and the status, a connection error text of up to 500 characters, or blocked_url (private/reserved host or non-https).
livebooleanWhether live events are sent. A test is sent either way.
live_notestringA short note when live is false. Empty otherwise.

The message is "Test delivered successfully." when ok is true. When it is false, the message is "Test delivery failed. See the response code and error." A delivered test resets the webhook's failure count. The test is sent either way. live is false when this site doesn't queue live events, and live_note then says why.

bash
curl -X POST -u USER:APPLICATION_PASSWORD \
  https://example.com/wp-json/link-manager-pro/v1/webhooks/31/test
json
{
  "success": true,
  "data": {
    "ok": true,
    "response_code": 200,
    "error": null,
    "live": true,
    "live_note": ""
  },
  "message": "Test delivered successfully."
}

Errors:

StatusWhen
404No webhook has that ID.