For the complete documentation index, see llms.txt. This page is also available as Markdown.

Webhooks

Receive webhooks for various events happening in Pylon.

Webhooks are user-defined HTTP callbacks that are triggered by specific events within Pylon.

This page covers webhooks that Pylon sends to you.

Setup

  1. Go to Settings > Integrations & dev > Webhooks and click Create Webhook.

  2. Enter a Name and the Target URL, and pick the HTTP method (POST by default; GET, PUT, PATCH, and DELETE are also available).

  3. Optionally, add Custom headers, for example an Authorization header with a Bearer token. Turn on Secret for a header to encrypt its value and hide it after saving.

  4. Click Create and copy the webhook secret. Pylon shows it only once. You need it to verify deliveries.

  5. Go to Settings > Workflows > Triggers and create a trigger with a Send webhook action. Use the trigger's conditions to control when the webhook fires. Macros can also send webhooks.

The target must be a public http or https URL. Private and internal network addresses are blocked. If your endpoint sits behind a firewall, allowlist Pylon's IP addresses for your region.

Payload

In the Send webhook action, write the request body in JSON payload. Type { to insert variables such as {{ issue.id }} or {{ issue.title }}. The variables you can use depend on the trigger's object. See Template variables for the full list, including tags, the latest message, and its author.

Follow these rules when you write the payload:

  • The payload must be valid JSON before variables are filled in.

  • Put every variable inside a JSON string. Pylon escapes each value for JSON but doesn't add quotes, so "id": "{{issue.id}}" works and "id": {{issue.id}} does not.

  • Every value arrives as a string, including numbers, dates, and true/false. Convert them on your side if needed.

  • Lists such as {{ issue.tags }} arrive as one comma-separated string, and missing values arrive as "".

You can also use variables in the webhook's Target URL, for example https://example.com/hooks/{{ issue.account.id }}. Variables in the URL are not URL-encoded, so use URL-safe values such as IDs.

Delivery and retries

  • Timeout: each attempt waits up to 20 seconds for a response.

  • Success: any status below 400 counts as delivered. A successful delivery adds a "sent webhook" entry to the activity of the object that fired the trigger.

  • Retries: Pylon makes up to 3 attempts in total. If an attempt gets a 4xx or 5xx status, times out, or can't connect, Pylon retries after about 4 seconds, then again after about 8 seconds. Retries include the X-Pylon-Retry-Number header.

  • At-least-once: a delivery can arrive more than once, for example when your endpoint processes a request but responds too slowly. Make your handler idempotent.

  • No ordering guarantee: deliveries can arrive in a different order from the events that caused them.

  • No event ID header: to de-duplicate, include an identifier such as {{issue.id}} together with the field that changed in your payload.

Pylon's own headers (Content-Type, the signature and timestamp headers, and X-Pylon-Retry-Number) replace any custom header with the same name. Every request is sent with Content-Type: application/json.

Test and monitor

  • Click Test in the create or edit dialog to send a test request with the body {"data":{"type":"ping"}}. Tests sent while creating a webhook are unsigned, because the secret doesn't exist yet. Tests sent from Edit on a saved webhook are signed.

  • The Logs tab on the Webhooks page lists every attempt with its status, method, payload, response, duration, and request ID. Retries appear as separate entries. Payloads and responses over about 65 KB are truncated in the log.

Verifying webhooks

When you create a webhook, Pylon shows its secret once. Store it securely: Pylon uses it to sign every delivery from a trigger or macro, and every test sent from a saved webhook, so your endpoint can confirm the request came from Pylon and has not been tampered with or replayed.

The secret can't be viewed again or rotated. To replace it, create a new webhook, point your triggers at it, and delete the old one.

Each delivery includes these headers:

Header
Value

X-Pylon-Timestamp

The time the request was sent, in Unix seconds.

X-Pylon-Signature-V2

Hex-encoded HMAC-SHA256 of <timestamp>.<body>, keyed with your webhook secret.

X-Pylon-Signature

Legacy. Hex-encoded HMAC-SHA256 of the body only.

X-Pylon-Retry-Number

Present on retries only: 1 on the first retry, 2 on the second.

To verify a delivery:

  1. Read X-Pylon-Timestamp and reject the request if it is more than 5 minutes from your server's current time.

  2. Build the signed string by joining the timestamp, a period (.), and the raw request body exactly as received, before any JSON parsing.

  3. Compute the HMAC-SHA256 of that string using your webhook secret as the key, and hex-encode it. Use the secret string as-is; do not hex-decode it.

  4. Compare your result to X-Pylon-Signature-V2 using a constant-time comparison.

Retries are signed again with a fresh timestamp, so a retried delivery passes the timestamp check.

Last updated

Was this helpful?