{"id":"webhook-verify","title":"Verify webhook signatures from 8 providers","description":"Constant-time verification of inbound webhook signatures from Stripe, GitHub, Shopify, Slack, Twilio, Linear, Clerk (Svix) and SendGrid HMAC, each using the exact signing string that provider documents. Every scheme uses Web Crypto HMAC with a timing-safe compare and, where the provider signs a timestamp, a configurable clock-skew tolerance (default 300s) to block replay. Returns the verdict and the specific failure reason: missing header, skewed timestamp, decoded-payload mismatch, or malformed secret. No network calls; the raw body and headers are the inputs.","roi":"One wrong character of normalization (newline, case, missing timestamp in the string) silently accepts forged webhooks. This is the tested reference for all eight.","audience":"developer","package":"code-preflight-kit","channels":["x402","mcp"],"endpoint":{"method":"POST","url":"https://agents.writerslogic.com/api/v1/webhook-verify","path":"/api/v1/webhook-verify","contentType":"application/json"},"price":{"amount":"0.005","currency":"USDC","network":"base","perCall":true,"failedCallsCharged":false},"inputSchema":{"type":"object","additionalProperties":false,"required":["source","secret","rawBody","headers"],"properties":{"source":{"type":"string","enum":["stripe","github","shopify","slack","twilio","linear","clerk","sendgrid-hmac"]},"secret":{"type":"string","maxLength":500,"description":"The signing secret; for Clerk/Svix a 'whsec_<base64>' value is accepted."},"rawBody":{"type":"string","maxLength":2000000,"description":"The exact request body bytes as a string. Reparsed JSON will not match; send the raw bytes."},"headers":{"type":"object","description":"Request headers as sent by the provider (string values). Names are case-insensitive."},"url":{"type":"string","maxLength":2000,"description":"Full request URL. Required for Twilio, ignored elsewhere."},"formParams":{"type":"object","description":"Form parameters parsed from a Twilio POST (string values); order does not matter."},"toleranceSeconds":{"type":"integer","description":"Max allowed |now - signed timestamp|. Default 300s."},"nowIso":{"type":"string","maxLength":40,"description":"Override 'now' for deterministic tests; ISO-8601 instant."}}},"resultSchema":{"type":"object","properties":{"status":{"type":"string","enum":["pass","fail","unknown","unsupported"],"description":"pass: every rule held. fail: a rule was violated (see witnesses). unknown: the input lacks the evidence to decide. unsupported: outside what this engine covers. Missing evidence is never a pass."},"source":{"type":"string"},"valid":{"type":"boolean"},"bodyBytes":{"type":"integer"},"witnesses":{"type":"array","items":{"type":"object","properties":{"ruleId":{"type":"string"},"detail":{"type":"string"},"path":{"type":"string","description":"JSON pointer or dotted path into the input."},"offset":{"type":"integer"},"index":{"type":"integer"},"expected":{"description":"What the rule required."},"actual":{"description":"What the input had."}}}},"versions":{"type":"object","description":"Every semantic dependency of this answer (engine, tables and their as-of dates, tokenizer, calendar, rounding profile)."},"limits":{"type":"array","items":{"type":"string"}},"deterministic":{"type":"boolean","description":"Always true: pure code, no model."}}},"outputSchema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["pass","fail","unknown","unsupported"],"description":"pass: every rule held. fail: a rule was violated (see witnesses). unknown: the input lacks the evidence to decide. unsupported: outside what this engine covers. Missing evidence is never a pass."},"source":{"type":"string"},"valid":{"type":"boolean"},"bodyBytes":{"type":"integer"},"witnesses":{"type":"array","items":{"type":"object","properties":{"ruleId":{"type":"string"},"detail":{"type":"string"},"path":{"type":"string","description":"JSON pointer or dotted path into the input."},"offset":{"type":"integer"},"index":{"type":"integer"},"expected":{"description":"What the rule required."},"actual":{"description":"What the input had."}}}},"versions":{"type":"object","description":"Every semantic dependency of this answer (engine, tables and their as-of dates, tokenizer, calendar, rounding profile)."},"limits":{"type":"array","items":{"type":"string"}},"deterministic":{"type":"boolean","description":"Always true: pure code, no model."}}},"_provenance":{"type":"object","description":"C2PA-aligned manifest and W3C VC 2.0 (eddsa-jcs-2022) issued by the hub DID; verify at POST /api/v1/verify-provenance"}},"required":["data","_provenance"]},"example":{"source":"github","secret":"mysecret","rawBody":"{\"a\":1}","headers":{"x-hub-signature-256":"sha256=0000000000000000000000000000000000000000000000000000000000000000"}},"openapiOperation":{"operationId":"webhook_verify","summary":"Verify webhook signatures from 8 providers","description":"Constant-time verification of inbound webhook signatures from Stripe, GitHub, Shopify, Slack, Twilio, Linear, Clerk (Svix) and SendGrid HMAC, each using the exact signing string that provider documents. Every scheme uses Web Crypto HMAC with a timing-safe compare and, where the provider signs a timestamp, a configurable clock-skew tolerance (default 300s) to block replay. Returns the verdict and the specific failure reason: missing header, skewed timestamp, decoded-payload mismatch, or malformed secret. No network calls; the raw body and headers are the inputs.\n\nPrice: 0.005 USDC on Base (x402).","x-x402-price-usdc":"0.005","x-x402-amount":"5000","x-payment-info":{"protocols":["x402"],"price":{"mode":"fixed","currency":"USD","amount":"0.005"}},"x-audience":"developer","x-sample-url":"https://agents.writerslogic.com/api/v1/samples/webhook-verify","security":[{"x402":[]},{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["source","secret","rawBody","headers"],"properties":{"source":{"type":"string","enum":["stripe","github","shopify","slack","twilio","linear","clerk","sendgrid-hmac"]},"secret":{"type":"string","maxLength":500,"description":"The signing secret; for Clerk/Svix a 'whsec_<base64>' value is accepted."},"rawBody":{"type":"string","maxLength":2000000,"description":"The exact request body bytes as a string. Reparsed JSON will not match; send the raw bytes."},"headers":{"type":"object","description":"Request headers as sent by the provider (string values). Names are case-insensitive."},"url":{"type":"string","maxLength":2000,"description":"Full request URL. Required for Twilio, ignored elsewhere."},"formParams":{"type":"object","description":"Form parameters parsed from a Twilio POST (string values); order does not matter."},"toleranceSeconds":{"type":"integer","description":"Max allowed |now - signed timestamp|. Default 300s."},"nowIso":{"type":"string","maxLength":40,"description":"Override 'now' for deterministic tests; ISO-8601 instant."}}},"example":{"source":"github","secret":"mysecret","rawBody":"{\"a\":1}","headers":{"x-hub-signature-256":"sha256=0000000000000000000000000000000000000000000000000000000000000000"}}}}},"responses":{"200":{"description":"Paid result with provenance","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"status":{"type":"string","enum":["pass","fail","unknown","unsupported"],"description":"pass: every rule held. fail: a rule was violated (see witnesses). unknown: the input lacks the evidence to decide. unsupported: outside what this engine covers. Missing evidence is never a pass."},"source":{"type":"string"},"valid":{"type":"boolean"},"bodyBytes":{"type":"integer"},"witnesses":{"type":"array","items":{"type":"object","properties":{"ruleId":{"type":"string"},"detail":{"type":"string"},"path":{"type":"string","description":"JSON pointer or dotted path into the input."},"offset":{"type":"integer"},"index":{"type":"integer"},"expected":{"description":"What the rule required."},"actual":{"description":"What the input had."}}}},"versions":{"type":"object","description":"Every semantic dependency of this answer (engine, tables and their as-of dates, tokenizer, calendar, rounding profile)."},"limits":{"type":"array","items":{"type":"string"}},"deterministic":{"type":"boolean","description":"Always true: pure code, no model."}}},"_provenance":{"type":"object","description":"C2PA-aligned manifest and W3C VC 2.0 (eddsa-jcs-2022) issued by the hub DID; verify at POST /api/v1/verify-provenance"}},"required":["data","_provenance"]}}}},"400":{"description":"Invalid request (no payment consumed)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"401":{"description":"Invalid X-API-KEY","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}},"402":{"description":"Payment required or invalid","content":{"application/json":{"schema":{"type":"object","description":"x402 v2 PaymentRequired (also base64 in the PAYMENT-REQUIRED header)","properties":{"x402Version":{"type":"integer"},"error":{"type":"string"},"code":{"type":"string"},"resource":{"type":"object"},"accepts":{"type":"array","items":{"type":"object"}},"extensions":{"type":"object"}}}}}},"502":{"description":"Upstream failure; the payment is released or not settled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}}}}}}}},"links":{"docs":"https://agents.writerslogic.com/docs/webhook-verify/","docsJson":"https://agents.writerslogic.com/docs/webhook-verify.json","sample":"https://agents.writerslogic.com/api/v1/samples/webhook-verify","openapi":"https://agents.writerslogic.com/openapi.json","catalog":"https://agents.writerslogic.com/api/v1/catalog","verifyProvenance":"https://agents.writerslogic.com/api/v1/verify-provenance","index":"https://agents.writerslogic.com/docs/index.json"}}