Helpdesk Webhooks: Get Ticket Events Into Your Own System

Polling misses transitions and burns quota. Here is what GoPimi pushes instead, and the two things receivers get wrong.

Most of what we have written about GoPimi is about getting mail in. This post is about the other direction: a ticket closes, and your CRM, your billing system, or your Slack channel needs to know. If you are still on the inbound side, start with Helpdesk API Integration.

The Polling Version, and Where It Leaks

The first version everyone writes is a cron job. Call GET /workspaces/{id}/tickets every minute, compare against the last snapshot, act on the differences.

It works until it does not. A ticket that goes open, then pending, then closed between two polls shows up as open to closed. A ticket that is reopened and closed again inside the same minute is invisible. And replies do not change the list at all, so you end up polling each open ticket's messages too. The request count now scales with your open tickets, not with what actually happened.

The API allows 120 requests a minute per resource group. Polling spends that budget on responses that say nothing changed.

What GoPimi Sends Instead

Register a webhook from Settings or the API, pick the events you care about, and GoPimi queues an HTTP POST to your URL every time one fires. Setup is on the webhooks guide, so here is only what the guide does not say.

There are 17 events. Tickets: ticket.created, ticket.updated, ticket.closed, ticket.reopened, ticket.assigned, ticket.replied, ticket.tagged, ticket.deleted. Conversations: conversation.created, conversation.replied, conversation.closed, conversation.assigned, conversation.tagged. Contacts: contact.created, contact.updated, contact.deleted. And sla.breached.

One detail matters: ticket.updated fires on every ticket update, including the ones that also fire ticket.closed, ticket.reopened, or ticket.assigned. Subscribe to the specific events, or to the generic one, not both.

The body is JSON with four keys: event, timestamp, workspace_id, and data. Three headers ride along: X-GoPimi-Event, X-GoPimi-Delivery (a UUID), and X-GoPimi-Signature.

Webhooks are owner-only and need a paid plan. The free plan allows none. Paid plans have no cap.

The Two Things Receivers Skip

The signature check, done properly. The header value is sha256= followed by a hex HMAC-SHA256 of the request body, keyed with the secret GoPimi shows you once at creation. Two mistakes are common. Comparing the digest against the header without stripping the prefix, which rejects every genuine delivery. And hashing a re-serialized copy of the JSON instead of the raw bytes, which fails whenever your JSON library formats differently than PHP.

Redelivery. GoPimi treats anything other than a 2xx as a failure, and gives you ten seconds to answer. Then it retries: five attempts, spaced 5 seconds, 5 minutes, 1 hour, and 6 hours apart. So if your handler updated the CRM and then timed out, the same event arrives again.

The obvious dedupe key is wrong. X-GoPimi-Delivery and timestamp are generated fresh on every attempt. What stays byte-identical across retries is event plus data. Key on those, and answer 200 before you do the slow work.

const crypto = require('crypto');

function verify(rawBody, header, secret) {
  const sent = String(header || '').replace(/^sha256=/, '');
  const mine = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return sent.length === mine.length
    && crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(mine));
}

// Retries change the delivery id and timestamp, never the data.
const body = JSON.parse(rawBody);
const dataHash = crypto.createHash('sha256').update(JSON.stringify(body.data)).digest('hex');
const dedupeKey = body.event + ':' + dataHash;

Every attempt is logged on GoPimi's side with the HTTP status, the first kilobyte of your response, the duration, and the attempt number. Pull them from GET /workspaces/{id}/webhooks/{id}/deliveries, or send a test ping from the same place while you are wiring things up.

Ready to stop polling? Setup, event list and quotas are on the webhooks guide. The endpoints for creating webhooks, rotating the secret, and reading delivery logs are in the API reference. If you want a hosted helpdesk that pushes events to you instead of making you poll, GoPimi does that on every paid plan.