pingd docs

Webhooks

A webhook is an inbound URL that turns arbitrary JSON into a published message on one topic. Use them to wire third-party services into your topics without writing a custom integration.

How it works

  1. You create a webhook attached to a topic, with a template that maps fields from incoming JSON into a message payload.
  2. pingd generates a one-time bearer-style token and returns the receive URL: POST /hooks/:token.
  3. The third-party service POSTs JSON to that URL.
  4. pingd renders the template against the JSON, publishes the message.
  5. Normal dispatch fanout happens (push, SSE, history).

Create a webhook

curl -s http://localhost:7685/topics/ci.github/webhooks \
  -H 'Authorization: Bearer $TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "template": {
      "title": "{{repository.full_name}} push",
      "body": "{{head_commit.author.name}}: {{head_commit.message}}",
      "tags": "github,{{repository.name}}",
      "priority": 2
    }
  }'
pingd-cli webhooks create \
  --topic ci.github \
  --template template.json
import requests

requests.post(
    "http://localhost:7685/topics/ci.github/webhooks",
    headers={"Authorization": "Bearer TOKEN"},
    json={
        "template": {
            "title": "{{repository.full_name}} push",
            "body": "{{head_commit.author.name}}: {{head_commit.message}}",
            "tags": "github,{{repository.name}}",
            "priority": 2,
        },
    },
)

Response (token shown once):

{
  "id": "uuid",
  "topicID": "uuid",
  "token": "whk_...",
  "template": { "...": "..." },
  "createdAt": "..."
}

The receive URL is:

POST /hooks/whk_...

Template fields

FieldTypeNotes
titletemplate stringOptional message title
subtitletemplate stringOptional subtitle
bodytemplate stringFalls back to the raw request body if empty
tagscomma-separated templateSplit on commas, trimmed
priority1–3Optional, default 2
ttlsecondsOptional, max 30 days

Template syntax

Templates use {{path.to.field}} lookups against the incoming JSON. Examples:

{{alert.title}}
{{source}}
{{event.repository.full_name}}

Missing fields render as empty string. There are no conditionals or loops, by design.

Receive payload

Webhook receive does not use bearer auth. The token in the URL is the credential.

curl -s http://localhost:7685/hooks/whk_example \
  -H 'Content-Type: application/json' \
  -d '{
    "alert": { "title": "Disk full" },
    "source": "nas",
    "message": "Volume /data is 95% full",
    "service": "storage"
  }'

Successful receives return 202 Accepted.

Endpoints

MethodPathAuth
GET/topics/:name/webhooksOwner / admin
POST/topics/:name/webhooksOwner / admin
GET/webhooks/:idOwner / admin
PATCH/webhooks/:idOwner / admin
DELETE/webhooks/:idOwner / admin
POST/hooks/:tokenToken in URL only
Webhook management is owner/admin-only. A user with rw permission on a topic cannot create webhooks for it.

Worked example: GitHub push events

Point GitHub's webhook at https://your-pingd/hooks/whk_.... For a payload like:

{
  "repository": {
    "full_name": "acme/inventory",
    "name": "inventory"
  },
  "head_commit": {
    "author": { "name": "alice" },
    "message": "fix tax rounding"
  }
}

The template renders a message on ci.github:

title:  acme/inventory push
body:   alice: fix tax rounding
tags:   github, inventory

Save your template to a file (template.json):

{
  "title": "{{repository.full_name}} push",
  "body": "{{head_commit.author.name}}: {{head_commit.message}}",
  "tags": "github,{{repository.name}}",
  "priority": 2
}

Rate limiting

Webhook receives are rate-limited per token and per IP, separately from the general API. Tune them with PINGD_WEBHOOK_RATE_LIMIT_PER_TOKEN and PINGD_WEBHOOK_RATE_LIMIT_PER_IP.

Security notes