Webhooks

Get notified in real-time when things happen in your workspace.

Overview

Webhooks send HTTP POST requests to your server when events occur in GoPimi — tickets created, conversations updated, contacts changed, SLA breached. Each workspace can have multiple webhooks, each subscribed to specific event types.

Setting Up a Webhook

  1. Go to Settings → Webhooks in your workspace
  2. Click Add Webhook
  3. Enter your endpoint URL (must be HTTPS)
  4. Select the events you want to receive
  5. Save — GoPimi generates a signing secret for verification

You can also manage webhooks via the API:

curl -X POST \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/gopimi",
    "events": ["ticket.created", "ticket.updated"]
  }' \
  /api/v1/workspaces/WORKSPACE_ID/webhooks

Event Types

17 event types across tickets, conversations, contacts, and SLA:

EventTriggered When
ticket.createdNew ticket created
ticket.updatedAny ticket update (also fires alongside the specific events below)
ticket.closedTicket status changed to closed
ticket.reopenedTicket status changed from closed back to open or pending
ticket.assignedTicket agent changed
ticket.repliedAgent posted an email reply on a ticket (internal notes do not fire it)
ticket.taggedTag added to or removed from a ticket
ticket.deletedTicket deleted
conversation.createdNew conversation created (shared or personal inbox)
conversation.repliedReply posted on a conversation
conversation.closedConversation status changed to closed
conversation.assignedConversation agent changed
conversation.taggedTag added to or removed from a conversation
contact.createdNew contact created
contact.updatedContact fields changed
contact.deletedContact deleted
sla.breachedSLA first-response or resolution deadline missed (breach_type in the payload says which)

Conversation events fire for both shared and personal inbox conversations. The payload includes an owner_id field — null for shared conversations, or the user ID of the owner for personal inbox conversations.

Payload Format

Every webhook delivery sends a JSON payload:

{
  "event": "ticket.created",
  "timestamp": "2026-03-26T14:30:00Z",
  "workspace_id": 1,
  "data": {
    "id": 42,
    "subject": "Ticket #42 - Cannot access dashboard",
    "status": "open",
    "contact": { "id": 5, "name": "Jane Doe", "email": "[email protected]" },
    "agent": null,
    "created_at": "2026-03-26T14:30:00Z"
  }
}

Verifying Signatures

Every delivery includes an X-GoPimi-Signature header — sha256= followed by the hex HMAC-SHA256 of the raw request body using your webhook's signing secret. Strip the prefix, recompute over the raw bytes, and compare in constant time before trusting the payload. See Helpdesk Webhooks: Get Ticket Events Into Your Own System for the redelivery handling that goes with it.

// PHP example
$payload = file_get_contents('php://input');
$expected = hash_hmac('sha256', $payload, $webhookSecret);

// Header is "sha256=<hex>"; strip the prefix before comparing.
$sent = substr($_SERVER['HTTP_X_GOPIMI_SIGNATURE'] ?? '', strlen('sha256='));

if (!hash_equals($expected, $sent)) {
    http_response_code(401);
    exit('Invalid signature');
}
// Node.js example (requestBody must be the raw bytes, not re-serialized JSON)
const crypto = require('crypto');

const expected = crypto
  .createHmac('sha256', webhookSecret)
  .update(requestBody)
  .digest('hex');

// Header is "sha256=<hex>"; strip the prefix, then compare in constant time.
const sent = String(req.headers['x-gopimi-signature'] || '').replace(/^sha256=/, '');

if (sent.length !== expected.length
    || !crypto.timingSafeEqual(Buffer.from(sent), Buffer.from(expected))) {
  return res.status(401).send('Invalid signature');
}

Retry Behavior

If your endpoint returns a non-2xx status or times out:

Quotas

The number of webhooks per workspace is governed by your plan's resource quotas. Check Settings → Usage to see your current limits.

Frequently Asked Questions

How are webhook payloads signed?

Using HMAC-SHA256 of the full JSON body with the webhook's per-webhook secret, sent in the X-GoPimi-Signature header as sha256=<hex>. Verify this on your endpoint before trusting the payload.

How many times will a failed delivery retry?

Up to five times with exponential backoff. Every attempt is recorded in webhook_deliveries with HTTP status, response body, and timing.

Which HTTP status codes count as a successful delivery?

Any 2xx response. 3xx, 4xx, and 5xx responses trigger retry.

Where can I inspect past deliveries?

Call GET /workspaces/{id}/webhooks/{webhook}/deliveries to see every delivery attempt for a given webhook, with status, response, and timing.

Related