e2e-hooks — webhook receiver + redirect target ================================================ Capture-and-respond endpoint for testing webhooks + post-payment redirects. Pick any ; it is created on first POST (no signup). Pass https://hooks.ottu.me/h/ as BOTH webhook_url and redirect_url. Base URL: https://hooks.ottu.me (prepend it to every path below) PUBLIC ROUTES (no auth — the unguessable is the capability) METHOD PATH WHAT IT DOES POST /h/ ingest a webhook; reply per the bucket's behaviour (default 200) per-call override via query: ?status=&delayMs= e.g. ?status=500 or ?status=503&delayMs=2000 (status 100-599, delayMs max 25000) GET /h/ redirect target — render the captured delivery (?session_id= picks one) GET /console/ human console: every delivery + payload/headers/query/timing GET /health {"ok":true} GET /help this page API ROUTES (auth: Bearer HOOKS_TOKEN) METHOD PATH WHAT IT DOES GET /api/hooks?bucket=&since= list deliveries for a bucket, newest first (since = ISO) GET /api/hooks/ one delivery (full record) GET /api/hooks//body raw request body bytes PUT /api/buckets//behavior set the response behaviour (JSON below) GET /api/buckets//behavior read the current behaviour DELETE /api/buckets/ clear the bucket's deliveries + behaviour URL CONVENTION resource identity -> path: /h/, /api/hooks/, /api/buckets//... modifiers + filters -> query: ?status=&delayMs= (per-call POST override), ?bucket=&since= (list) (bucket is a PATH segment when it names the resource, a QUERY param when it filters /api/hooks) BEHAVIOUR JSON (PUT .../behavior) — all fields optional; default is 200 {"ok":true} { "status": 200, "delayMs": 0, "body": {"ok":true}, "headers": {"x-foo":"bar"}, "sequence": [ ... ] } status 100-599 delayMs 0-25000 (artificial delay before replying — test webhook timeouts) body object -> JSON, string -> text/plain headers extra response headers sequence per-delivery: the Nth delivery uses sequence[min(N, len-1)] ("fail twice then 200"). A FLAT status/delay/body/headers (no sequence) applies to EVERY delivery. Precedence: inline ?status/?delayMs query > sequence stage > top-level field > default. CAPTURED PER DELIVERY (returned by the API; shown in /console) id, receivedAt, method, sessionId, query, headers, contentType, body, parseError, respondedStatus, respondedDelayMs (sessionId = body.session_id or ?session_id=) CONTRACT (Ottu checkout): the payer reaches redirect_url only if the webhook returns 200; otherwise Ottu shows its own post-payment page. EXAMPLES curl -X POST https://hooks.ottu.me/h/demo -H 'content-type: application/json' -d '{"result":"success"}' open https://hooks.ottu.me/console/demo curl -X POST 'https://hooks.ottu.me/h/demo?status=500&delayMs=1000' -d '{}' # 500 after 1s curl -X PUT https://hooks.ottu.me/api/buckets/demo/behavior -H "authorization: Bearer $HOOKS_TOKEN" \ -d '{"status":500,"delayMs":2000}' # constant 500 after 2s