---
title: "Webhook"
method: POST
path: "/billing/webhook"
---

# Webhook

`POST /billing/webhook`

## Request

- Base URL: `https://api-docs.session-exchange.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api-docs.session-exchange.com/billing/webhook`
- Auth: none declared

Stripe webhook. Verifies the signature, then on `checkout.session.completed`
fulfils the stored intent (entitlement for items; owner membership for tenants).
Idempotent per Stripe event id.

Status contract (what Stripe sees — docs/go-live-eu-marketplace-runbook.md):
  - 400 `bad_signature` ONLY for a delivery whose signature does not verify (details.reason:
    missing_header | malformed_header | timestamp_outside_tolerance | no_matching_secret);
  - 400 `bad_payload` for a verified body that is not a JSON object (Stripe never sends one);
  - 503 `webhook_unconfigured` / `billing_unavailable` when WE cannot verify or record yet —
    Stripe retries, and the log no longer blames a signature that was never checked;
  - 200 for every verified event, including types we do not handle, thin (v2) events, events
    from the other Stripe mode, duplicates and events for a checkout we do not know;
  - 5xx when fulfilment itself fails, so Stripe redelivers.

## Response `200`

Successful Response

- unknown

---

[API](https://skmtc.dev/session-exchange/apis/a4sx.md) · [All operations](https://skmtc.dev/session-exchange/apis/a4sx/llms.txt) · [OpenAPI document](https://skmtc.dev/session-exchange/apis/a4sx/revisions/abaefc33dfc7?raw)
