{"about":"Copy-paste receivers for Private Watch webhooks. Every delivery is an HTTP POST signed with HMAC-SHA256 over the raw body using your webhookSecret (returned once, at watch creation). Verify, then trust the body.","headers":{"signature":"x-seneschal-signature: sha256=<HMAC-SHA256(webhookSecret, rawBody)>","watch_id":"x-seneschal-watch-id: <watchId>","event":"x-seneschal-event: <event name, e.g. payment | low_credit | test>"},"verify":{"node":"// Verify a Private Watch webhook (Node 18+, no dependencies).\n// IMPORTANT: verify against the RAW request body bytes, not re-serialised JSON.\nimport { createHmac, timingSafeEqual } from 'node:crypto';\n\nexport function verifyWatchWebhook(rawBody, headers, webhookSecret) {\n\tconst sent = String(headers['x-seneschal-signature'] ?? '');\n\tconst expected = 'sha256=' + createHmac('sha256', webhookSecret).update(rawBody).digest('hex');\n\tconst a = Buffer.from(expected);\n\tconst b = Buffer.from(sent);\n\treturn a.length === b.length && timingSafeEqual(a, b);\n}\n\n// Express example — capture the raw body:\n//   app.post('/webhooks/payments', express.raw({ type: 'application/json' }), (req, res) => {\n//   \tif (!verifyWatchWebhook(req.body, req.headers, process.env.WEBHOOK_SECRET)) return res.status(401).end();\n//   \tconst event = JSON.parse(req.body);   // event.event, event.credit, …\n//   \tres.status(200).end();\n//   });","python":"# Verify a Private Watch webhook (Python 3, stdlib only).\n# IMPORTANT: verify against the RAW request body bytes, not re-serialised JSON.\nimport hashlib, hmac\n\ndef verify_watch_webhook(raw_body: bytes, headers, webhook_secret: str) -> bool:\n\tsent = headers.get(\"x-seneschal-signature\", \"\")\n\texpected = \"sha256=\" + hmac.new(webhook_secret.encode(), raw_body, hashlib.sha256).hexdigest()\n\treturn hmac.compare_digest(expected, sent)\n\n# Flask example:\n#   @app.post(\"/webhooks/payments\")\n#   def payments():\n#   \tif not verify_watch_webhook(request.get_data(), request.headers, WEBHOOK_SECRET):\n#   \t\tabort(401)\n#   \tevent = request.get_json()   # event[\"event\"], event[\"credit\"], …\n#   \treturn \"\", 200","php":"<?php\n// Verify a Private Watch webhook (PHP 8, no dependencies).\n// IMPORTANT: verify against the RAW request body, not re-serialised JSON.\nfunction verify_watch_webhook(string $rawBody, array $headers, string $webhookSecret): bool {\n\t$sent = $headers['x-seneschal-signature'] ?? '';\n\t$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $webhookSecret);\n\treturn hash_equals($expected, $sent);\n}\n\n// Plain PHP example:\n//   $raw = file_get_contents('php://input');\n//   $headers = array_change_key_case(getallheaders(), CASE_LOWER);\n//   if (!verify_watch_webhook($raw, $headers, getenv('WEBHOOK_SECRET'))) { http_response_code(401); exit; }\n//   $event = json_decode($raw, true);   // $event['event'], $event['credit'], …\n//   http_response_code(200);"},"create":{"pay_in_coin":"curl -s -X POST https://api.seneschal.space/v1/private/watch-crypto -H 'content-type: application/json' -d '{\"chain\":\"monero\",\"address\":\"<your-address>\",\"viewKey\":\"<your-private-view-key>\",\"webhookUrl\":\"https://example.com/webhooks/payments\"}' — create AND fund in XMR/ZEC; the response carries the coin payment quote. No USDC, no EVM wallet, no x402.","pay_in_usdc":"POST https://api.seneschal.space/v1/private/watch via any x402 client ($0.10 — the fee IS your starting credit). See https://api.seneschal.space/v1/private/info."},"test":"POST https://api.seneschal.space/v1/private/watch/{watchId}/test (header x-watch-token) fires a synthetic SIGNED webhook at your endpoint so you can verify your handler end-to-end before real money moves.","checkout":{"about":"Same watch, same webhook secret, same verification code — checkout invoices (accept XMR/ZEC on your site) deliver invoice_seen / invoice_paid / invoice_underpaid / invoice_expired events with your orderId echoed back.","create_invoice":"curl -s -X POST https://api.seneschal.space/v1/checkout/invoices -H 'content-type: application/json' -d '{\"watchId\":\"<watchId>\",\"watchToken\":\"<watchToken>\",\"amountUsdCents\":500,\"orderId\":\"order-123\",\"description\":\"Coffee\"}' — returns payTo, the EXACT amount, memo (Zcash), a wallet URI and a hosted pay page URL to hand to the buyer.","mint_embed_key":"curl -s -X POST https://api.seneschal.space/v1/checkout/keys -H 'content-type: application/json' -d '{\"watchId\":\"<watchId>\",\"watchToken\":\"<watchToken>\",\"label\":\"my site\",\"maxAmountUsdCents\":2000}' — a restricted ck_… key safe for client-side embeds (can only create invoices paying YOUR wallet).","info":"GET https://api.seneschal.space/v1/checkout — full how-to, fees, limits."},"languages":["node","python","php"]}