Webhooks

Webhooks allow you to receive real-time HTTP POST notifications when events occur in your Ping account — tickets created, updated, resolved, or chat sessions starting and ending.


POST/v1/api/webhooks

Register a webhook

Register a URL to receive webhook events. You can register up to 10 webhooks per business.

Request body

  • Name
    url
    Type
    string
    Description

    The HTTPS URL to receive webhook POST requests (required).

  • Name
    events
    Type
    array
    Description

    List of event types to subscribe to (required).

The response includes a secret field — save this immediately. It is only shown once and is used for signature verification.

Request

POST
/v1/api/webhooks
curl -X POST https://api.ping.co.zw/v1/api/webhooks \
  -H "X-Ping-Api-Key: pk_live_xxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.com/webhooks/ping",
    "events": ["ticket.created", "ticket.resolved", "comment.added"]
  }'

Response

{
  "result": "success",
  "message": "Webhook registered. Save the secret — it will not be shown again.",
  "webhook": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "url": "https://your-app.com/webhooks/ping",
    "events": ["ticket.created", "ticket.resolved", "comment.added"],
    "isActive": true,
    "failureCount": 0,
    "lastTriggeredAt": null,
    "dateCreated": "2026-05-04T08:00:00",
    "secret": "whsec_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4"
  }
}

GET/v1/api/webhooks

List webhooks

List all registered webhooks for your business.

Request

GET
/v1/api/webhooks
curl "https://api.ping.co.zw/v1/api/webhooks" \
  -H "X-Ping-Api-Key: pk_live_xxxxxxxxxxxx"

Response

{
  "result": "success",
  "webhooks": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "url": "https://your-app.com/webhooks/ping",
      "events": ["ticket.created", "ticket.resolved", "comment.added"],
      "isActive": true,
      "failureCount": 0,
      "lastTriggeredAt": "2026-05-04T09:30:00",
      "dateCreated": "2026-05-04T08:00:00"
    }
  ]
}

DELETE/v1/api/webhooks/:id

Delete a webhook

Remove a webhook registration by its ID.

Request

DELETE
/v1/api/webhooks/:id
curl -X DELETE "https://api.ping.co.zw/v1/api/webhooks/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -H "X-Ping-Api-Key: pk_live_xxxxxxxxxxxx"

Response

{
  "result": "success",
  "message": "Webhook deleted"
}

Webhook events

Subscribe to any combination of the following event types:

Ticket events

  • Name
    ticket.created
    Description

    A new ticket was created (via API, portal, email, or live chat).

  • Name
    ticket.updated
    Description

    A ticket was updated (status, priority, fields, etc.).

  • Name
    ticket.resolved
    Description

    A ticket status was set to "resolved".

  • Name
    ticket.assigned
    Description

    A ticket was assigned or reassigned to an agent.

Comment events

  • Name
    comment.added
    Description

    A new comment was added to a ticket.

Chat events

  • Name
    chat.started
    Description

    A new live chat session was initiated by a visitor.

  • Name
    chat.ended
    Description

    A live chat session was closed.


Webhook payload

All webhook deliveries are HTTP POST requests with a JSON body containing:

  • Name
    event
    Type
    string
    Description

    The event type (e.g., ticket.created).

  • Name
    timestamp
    Type
    string
    Description

    ISO 8601 timestamp of when the event occurred.

  • Name
    data
    Type
    object
    Description

    Event-specific payload data.

Headers sent with each delivery

  • Name
    X-Ping-Signature
    Type
    string
    Description

    HMAC-SHA256 signature of the request body.

  • Name
    X-Ping-Event
    Type
    string
    Description

    Event type (same as event field in body).

  • Name
    X-Ping-Delivery
    Type
    string
    Description

    Unique delivery ID for idempotency.

  • Name
    Content-Type
    Type
    string
    Description

    Always application/json.

Example: ticket.created

{
  "event": "ticket.created",
  "timestamp": "2026-05-04T08:00:00Z",
  "data": {
    "ticketNumber": "TKT-2026-00042",
    "publicId": "TKT-A1B2C3D4",
    "subject": "Cannot connect to Starlink",
    "status": "open",
    "priority": "high",
    "category": "technical",
    "channel": "api",
    "customerFirstName": "Tendai",
    "customerLastName": "Moyo",
    "customerEmail": "[email protected]",
    "dateCreated": "2026-05-04T08:00:00"
  }
}

Example: comment.added

{
  "event": "comment.added",
  "timestamp": "2026-05-04T09:35:00Z",
  "data": {
    "ticketNumber": "TKT-2026-00042",
    "comment": {
      "publicId": "a1b2c3d4-...",
      "authorType": "admin",
      "content": "Issue resolved after adapter replacement.",
      "isInternal": false,
      "dateCreated": "2026-05-04T09:35:00"
    }
  }
}

Signature verification

Every webhook delivery includes an X-Ping-Signature header containing an HMAC-SHA256 signature of the request body, signed with your webhook secret.

Verification steps

  1. Get the raw request body (before JSON parsing)
  2. Compute HMAC-SHA256 of the body using your webhook secret
  3. Compare the computed signature with the X-Ping-Signature header
  4. Reject the request if signatures don't match

Python

import hmac
import hashlib

def verify_ping_webhook(payload_body: str, signature: str, secret: str) -> bool:
    """Verify that a webhook request came from Ping."""
    expected = hmac.HMAC(
        secret.encode("utf-8"),
        payload_body.encode("utf-8"),
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

# In your webhook handler:
signature = request.headers.get("X-Ping-Signature")
secret = "whsec_your_webhook_secret"

if not verify_ping_webhook(request.body, signature, secret):
    return Response(status=401)

Node.js

const crypto = require('crypto')

function verifyPingWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  )
}

// In your Express handler:
app.post('/webhooks/ping', (req, res) => {
  const signature = req.headers['x-ping-signature']
  const secret = 'whsec_your_webhook_secret'

  if (!verifyPingWebhook(req.rawBody, signature, secret)) {
    return res.status(401).send('Invalid signature')
  }
  // Process event...
  res.status(200).send('OK')
})

Retry behavior

If your endpoint returns a non-2xx status code or doesn't respond within 10 seconds, Ping will retry the delivery:

AttemptDelay
1st retry5 seconds
2nd retry25 seconds
3rd retry125 seconds

After 3 failed attempts, the delivery is marked as failed. After 10 consecutive failures across any deliveries, the webhook is automatically disabled.

Best practices

  • Return 200 OK quickly (process events asynchronously)
  • Use the X-Ping-Delivery header for idempotency
  • Monitor your webhook's failureCount via the list endpoint
  • Re-register webhooks if they become disabled

Was this page helpful?