FastAPI 0.142 native OpenTelemetry: tag spans with a Sume job id

FastAPI 0.142.0 added native OpenTelemetry. Put the Sume job_id on the current span in a webhook route so a failed render shows up next to the HTTP trace.

4 min readSume
All posts

To find a slow or failed Sume render in your traces, add the Sume job_id as an attribute on the span that FastAPI 0.142 already creates for the request. The 0.142.0 release (2026-09-29) added native OpenTelemetry support, and the OpenTelemetry API lets any route read the current span with trace.get_current_span(). You add one line and no new library.

What the FastAPI releases say

The releases page lists five releases between 2026-09-29 and 2026-10-07, most of them about telemetry. They tell you that the feature is new and still moving, so pin the version and read each note before you upgrade.

FastAPI 0.142 releases, read 2026-10-08
VersionDateRelease note
0.142.02026-09-29Add native OpenTelemetry support
0.142.1Date not readFix repeated endpoint wrapping in included routers
0.142.2Date not readAllow startup when automatic OTel config fails
0.142.32026-10-07Cache OTel tracers
0.142.42026-10-07Isolate telemetry for excluded requests

Which Sume id to attach

A job webhook body has request_id and job_id, and the status URL for the job uses the same id. Put it on the span as sume.job_id, plus the event name, so a trace search by job id returns the delivery.

Run webhooks use the same signature scheme but carry request_id for dedupe. Attach whichever id the body has.

A webhook route that tags its span

The route below verifies the sume-v1 signature by hand. It accepts the comma-separated header that Sume sends during the 24 hour secret rotation, and it refuses an empty secret.

import hashlib, hmac, json, os, time
from fastapi import FastAPI, Request, HTTPException
from opentelemetry import trace

app = FastAPI()

def valid(body: bytes, ts: str, header: str, secret: str) -> bool:
    if not secret or abs(time.time() - int(ts)) > 300:
        return False
    mac = hmac.new(secret.encode(), ts.encode() + b"." + body, hashlib.sha256).hexdigest()
    sigs = [p.split("=", 1)[1] for p in header.split(",") if p.startswith("sume-v1=")]
    return any(hmac.compare_digest(mac, s) for s in sigs)

@app.post("/hooks/sume")
async def sume(request: Request):
    body = await request.body()
    h = request.headers
    secret = os.environ.get("SUME_COM_WEBHOOK_SIGNING_SECRET", "")
    if not valid(body, h.get("x-sume-webhook-timestamp", "0"), h.get("x-sume-webhook-signature", ""), secret):
        raise HTTPException(401)
    event = json.loads(body)
    trace.get_current_span().set_attribute("sume.job_id", event.get("job_id", ""))
    return {"ok": True}

Notes before you ship

How you turn the native instrumentation on is described in the FastAPI release notes, which this post does not repeat. The span call works with any OpenTelemetry SDK setup.

  • int(ts) raises on a bad header; wrap it if you do not trust the sender, or rely on the 401 path.
  • Dedupe on job_id before you do work; Sume retries up to 10 times.
  • Exclude health checks from tracing; 0.142.4 isolates telemetry for excluded requests.

Finding the delivery later

Once the attribute is on the span, a trace search for sume.job_id returns the HTTP request, its duration and its status code. Sume shows each delivery with a status that moves through pending, delivering, retrying, delivered, failed or exhausted, and the signing-secret fingerprint. Put the two views side by side: if Sume says retrying and your trace shows a 401, the secret is wrong; if the trace shows nothing, the request never reached you.

A delivery that ended exhausted can be replayed with POST /v1/jobs/{job_id}/webhook/redeliver, which re-sends the real terminal event with a fresh signature and does not spend one of the 10 automatic attempts.

Add the job id and the event name. Do not add the body, the signature or the secret to a span, because trace backends keep attributes for a long time and often share them widely.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume