Skip to content

Make alert webhook channels user-definable: custom headers + payload template #2180

Description

@gusfraser

Supersedes #1045, #1507, and arguably #383/#1540)

A webhook channel is configured by three things — type, format, url — where format picks from a closed set (auto, slack-blockkit, slack-legacy, raw-data). You can point it anywhere, but you can't influence what Logfire actually sends.

That one limitation shows up as several open issues that look separate but aren't:

Issue Wanted Blocked by
#1045 MS Teams / Google Workspace body shape
#1507 DingTalk body shape
#1540 Google Chat body shape
(this one) any authenticated endpoint request headers

All of them get answered the same way today: run a small proxy that reshapes the payload and adds an Authorization header. As #1507 puts it, that's "a potential failing point" sitting in the alerting path.

My case

I'd like alerts to reach an automated agent (a Claude Routine) that starts root-cause analysis when something fires, opens a GitHub issue where warranted but I can't add auth. Slack and Discord work today only because their webhook URL is the credential.

Proposal

Add two optional fields to the webhook config, used when format = "custom":

Purely additive.

PagerDuty already needed its own channel type partly because a webhook couldn't produce its body shape or set its Authorization header. Templating turns that class of integration into configuration rather than new provider code.

Example alert (following was "SELECT 1 AS canary" to illustrate, from a real capture:

{
  "organization_name": "acme",
  "project_name": "acme-qa",
  "alert_name": "Example alert",
  "timestamp": "2026-08-02T21:00:45.204370Z",
  "n_rows": 1,
  "data": [[1]],
  "columns": [
    { "name": "canary", "type": { "type_name": "Int64", "is_array": false }, "nullable": false }
  ],
  "errors": null,
  "description": "Example description",
  "links": {
    "org": "https://logfire-eu.pydantic.dev/acme",
    "project": "https://logfire-eu.pydantic.dev/acme/acme-qa",
    "alert": "https://logfire-eu.pydantic.dev/acme/acme-qa/alerts/<id>?alertRunId=<run-id>",
    "alert_settings": "https://logfire-eu.pydantic.dev/acme/acme-qa/alerts/<id>?alertRunId=<run-id>/edit",
    "explore": "https://logfire-eu.pydantic.dev/acme/acme-qa/explore?tab=...&query=...&since=...&until=..."
  }
}

Most of that is already template-friendly; links in particular is exactly what a Teams or Google Chat card wants.

Data is positional.. arrays of values, with names carried separately in columns... so possibly needs a "rows" view (data) and json helper (positioning).

Header values are the same class of secret as the webhook URL itself, which is already masked on read so they could just get the same treatment, delivery logs included.

On send path; similar to current approach, need to succeed before saving (although potentially via API we don't need to do this, to start with?)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions