Patching Supabase Postgres 17.11 vs Sume's 10-attempt webhook budget

Supabase's September 25 Postgres 15.19 and 17.11 releases fix 44 CVEs. A restart can outlast Sume's ten 30-second webhook attempts, so plan a redeliver.

4 min readSume
All posts

Supabase's changelog lists Postgres 15.19 and 17.11 as fixing 44 CVEs, dated September 25. If your Sume webhook handler writes to a Supabase database, applying the patch means a restart, and Sume's webhook budget is ten attempts at a default 30 second spacing, so a long enough outage leaves some jobs with a failed delivery even though the jobs themselves finished.

The fix is not to avoid patching. It is to know the budget, keep a polling backup, and use Sume's redeliver endpoint after the window.

The two sets of facts

Supabase facts are from its changelog, read 2026-10-03. Sume facts are from the Webhooks page of the docs.

Postgres patch and webhook delivery facts (read 2026-10-03)
ItemValueSource
Postgres releases15.19 and 17.11Supabase changelog
CVEs fixed44Supabase changelog
Release dateSeptember 25Supabase changelog
Automatic webhook attemptsUp to 10 totalSume Webhooks docs
SpacingFixed, 30 seconds by defaultSume Webhooks docs
Timeout per attempt10 secondsSume Webhooks docs

How long the retry window lasts

Ten attempts with 30 seconds between them give nine gaps, about 270 seconds, plus up to 10 seconds of timeout on each attempt if your endpoint hangs rather than refusing at once. That is derived arithmetic from the documented defaults, so a delivery window of roughly five minutes is a fair planning figure. Your own spacing may differ if it was changed.

A Postgres restart that completes inside that window loses nothing, because the next attempt succeeds. One that runs longer exhausts the attempts. The docs say ten refused attempts leave a failed delivery and a job that still reached its real terminal state, so the work is not lost, only the notification.

Recovering after the window

Two recovery paths exist in the docs. Polling GET /v1/jobs/:id/status and then GET /v1/jobs/:id/result works for any job you have an id for. And POST /v1/jobs/{job_id}/webhook/redeliver, which needs jobs:write, re-sends the real terminal event with a fresh timestamp and signature, and does not consume one of the automatic ten.

The receiver should already be idempotent on job_id, as the docs require, so a redelivered event for a job you did record is harmless.

import os
import requests


def redeliver(job_ids):
    key = os.environ.get("SUME_API_KEY")
    if not key:
        raise RuntimeError("SUME_API_KEY is not set")
    out = {}
    for job_id in job_ids:
        r = requests.post(
            f"https://api.sume.com/v1/jobs/{job_id}/webhook/redeliver",
            headers={"Authorization": "Bearer " + key},
            timeout=30,
        )
        out[job_id] = r.status_code
    return out


if __name__ == "__main__":
    print(redeliver([]))

A maintenance-window plan

  • Before the restart, list jobs still queued or processing with GET /v1/jobs.
  • After it, list jobs that completed during the window and are missing from your table.
  • Redeliver those, or poll their results, and let the job_id upsert absorb duplicates.
  • Schedule patching for a quiet hour so fewer jobs finish mid-restart.

Making the receiver restart-safe

The receiver itself can help. If it cannot reach the database, it should return a non-2xx at once rather than hang, so the attempt is spent quickly and the next one lands closer to recovery. It should never return 2xx before the event is durably stored, because a 2xx ends the retries for that delivery.

Finally, write down the window. If your host publishes expected downtime for a patch, compare it with the roughly five minute retry span, and if the downtime is longer, add the redeliver step to the runbook before the change, not after it.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume