Webhooks

Home » Integrations » Webhooks

NiroHelp fires a canonical event for every meaningful thing that happens to a ticket — created, replied to, assigned, overdue, resolved, and more. A webhook sends that event as a signed HTTPS request to a URL of your own, so you can wire NiroHelp into Make, n8n, a serverless function, or anything else that takes a POST.

Where the screen is

NiroHelp → Integrations → Webhooks. Like the rest of Integrations, it needs the manage_options capability, so only administrators see it.

Adding an endpoint

  1. Go to NiroHelp → Integrations → Webhooks.
  2. Click Add endpoint.
  3. Fill in:
    • Endpoint URL — where the request goes. See URL rules below.
    • Description — a note for yourself; shown in the endpoint list.
    • Include message bodies — off by default. On sends the full text of replies and notes; off sends a short excerpt only.
    • Events — which events this endpoint receives. Pick All public events, specific events, or both.
  4. Click Add endpoint.

The endpoint is created active, and its signing secret is shown once — see below.

URL rules

  • Must be https:// — plain HTTP is refused, unless the site has WP_DEBUG on.
  • Can’t resolve to this server or a private network address. NiroHelp checks this the same way WordPress does for any outgoing request, so a URL that can’t be reached safely is rejected before it’s ever saved.

Choosing events

All public events is a subscription, not a shortcut — it’s stored as * and keeps covering new events added in later releases, which ticking every current checkbox individually would not.

It deliberately stops at public events. A private event carries agent-only content — today that’s only Private note added — and it is never included in “All public events.” If you want it, choose it by name as well.

A handful of events are listed as Not emitted yet. Their names are published so you can build against them now, but nothing fires them yet — see the table below.

Events you can subscribe to

EventFires when
ticket.createdA ticket was submitted, created in wp-admin, or imported.
ticket.updatedUrgency, product, reason, order or a custom field changed. At most one per ticket per request.
ticket.repliedThe customer, an agent or the AI replied.
ticket.customer_repliedThe customer specifically replied. The same reply also fires ticket.replied.
ticket.note_addedPrivate. An agent-only note was added.
ticket.assignedA ticket was assigned, reassigned or unassigned.
ticket.status_changedA ticket moved from one status to another.
ticket.resolvedA ticket was marked resolved. The same change also fires ticket.status_changed.
ticket.overdueA ticket has waited on an agent longer than Overdue after, under Settings → Tickets → Behaviour.
ticket.sentiment_negativeA ticket’s sentiment turned negative. Fired on the change, not on every re-read.
customer.createdA customer account was created.
ai.answeredThe AI auto responder replied to a ticket.
ai.escalatedThe AI held an answer back, or couldn’t answer — a person is needed.
ticket.reopenedNot emitted yet. A customer replied to a finished ticket and it reopened.
sla.breachedNot emitted yet. A ticket missed an SLA target.

The signing secret

Every endpoint gets its own secret, prefixed whsec_. It’s shown exactly once — right after you add the endpoint, or right after you rotate it — in a Copy your signing secret dialog. Close that dialog and it’s gone; the endpoint list only ever shows a hint of it afterwards.

Every delivery carries an X-NiroHelp-Signature header:

X-NiroHelp-Signature: t=<unix timestamp>,v1=<hex>

v1 is an HMAC-SHA256 of "{t}.{body}", computed with your endpoint’s secret. To verify a request came from your site, recompute that HMAC on the raw request body and compare it to v1:

function verify_signature( $header, $body, $secret ) {
    $parts = [];
    foreach ( explode( ',', $header ) as $pair ) {
        [ $key, $value ]  = explode( '=', $pair, 2 );
        $parts[ $key ]    = $value;
    }

    $expected = hash_hmac( 'sha256', $parts['t'] . '.' . $body, $secret );

    return hash_equals( $expected, $parts['v1'] );
}

Rotating a secret retires the old one immediately — update anywhere it’s stored before you rotate, or that receiver starts failing signature checks until it’s updated.

Managing endpoints

Each row offers:

  • Send test — posts a sample event right now and shows what the receiver answered. This is the one thing on the screen that waits for a response; everything else is queued.
  • Edit — change the URL, description, body setting or events.
  • Disable / Enable — stop or resume deliveries without deleting the endpoint. A disabled endpoint keeps its secret and its event list.
  • Rotate secret — issues a new secret and shows it once, the same as creation. Confirmed first.
  • Delete — removes the endpoint and its delivery log. Confirmed first.
  • Deliveries — opens the delivery log for that endpoint.

Delivery, retries and the circuit breaker

Sending a webhook never blocks a ticket being saved — every delivery is queued. A failed attempt retries up to 5 times, on a backoff of roughly 1 minute, 5 minutes, 30 minutes, 2 hours, then 12 hours.

5 consecutive failures disable the endpoint automatically. Rather than retrying a dead URL forever, NiroHelp switches it off and flags it in wp-admin so an administrator notices and fixes it — a URL that changed, a certificate that expired, a firewall that started blocking the request. Re-enable it from the endpoint row once it’s fixed.

The delivery log

Each endpoint’s Deliveries view lists every attempt: When, Event, Attempt, Result, Response, Duration, and a Redeliver action to retry a specific one on demand.

How long attempts are kept is a Settings field on the same screen — Keep the delivery log for, in days. Default 14, from 1 to 90. Older attempts, and the payloads they could be redelivered from, are deleted daily.

The payload

Every event is a JSON body: an id, the event name, a schema version, when it happened, who or what caused it, and the ticket or comment it concerns. A payload is capped at 64KB — past that, the comment body is trimmed first, then other fields, and truncated is set so a receiver can tell.

Troubleshooting

  • Nothing arrives. Check the endpoint is Active, not disabled by the circuit breaker. Use Send test first — it reports the receiver’s answer immediately, instead of waiting on the delivery log.
  • “Enter a full URL, starting with https://.” — the URL isn’t valid, or isn’t HTTPS. See URL rules above.
  • “That address could not be resolved, or it points at this server or a private network.” — NiroHelp refuses to send webhooks to itself or to a private/internal address, the same way WordPress refuses it for any outgoing request.
  • Signature never matches. Recompute the HMAC over the raw request body, before any JSON re-encoding — re-serializing the body can change whitespace and break the comparison.

Was this doc helpful?

Scroll to Top