Read the raw body first
Signature verification must use the bytes Payclave sent. Parsing and reserializing JSON can change those bytes.
Webhook security guide
A webhook is an instruction to your backend, so authenticity and replay handling matter. Verify the exact request bytes with the endpoint secret, claim the event ID once, and acknowledge duplicate deliveries without repeating fulfillment.
Maintained by Payclave Product and Engineering. Updated .
Signature verification must use the bytes Payclave sent. Parsing and reserializing JSON can change those bytes.
The server SDK validates the t and v1 values in X-Payclave-Signature with a five-minute default tolerance.
Claim event.id under a unique database constraint before queuing fulfillment, email, inventory, or accounting work.
Each webhook endpoint has its own signing secret. Store that secret in server-only configuration and rotate it if it is exposed. API keys authenticate your calls to Payclave; webhook secrets authenticate calls from Payclave to your endpoint. They are not interchangeable.
Payclave sends X-Payclave-Signature, X-Payclave-Timestamp, X-Payclave-Delivery, and X-Payclave-Event headers. The signature header contains a Unix timestamp and one or more v1 signatures.
Read the request as text or bytes before a JSON body parser runs. The signed message combines the timestamp, a period, and the raw payload. The server SDK calculates HMAC SHA-256 and compares the result in constant time.
constructPayclaveWebhookEvent verifies the signature and then parses the event. Its default timestamp tolerance is 300 seconds. Keep server clocks synchronized and return a non-2xx response when verification fails.
import { constructPayclaveWebhookEvent } from "@payclave/sdk-server"
export async function POST(request: Request) {
const rawBody = await request.text()
const signature = request.headers.get("X-Payclave-Signature") ?? ""
const event = constructPayclaveWebhookEvent({
payload: rawBody,
signature,
secret: process.env.PAYCLAVE_WEBHOOK_SECRET!,
})
await eventStore.processOnce(event.id, async () => {
if (event.type === "invoice.paid") {
await queueFulfillment(event.data)
}
})
return new Response(null, { status: 204 })
}During secret rotation, Payclave can send both the new and previous v1 signatures in the same header. The header shape is t=<timestamp>,v1=<new>,v1=<old>. The SDK accepts the delivery when any v1 value matches the secret supplied by your endpoint.
Deploy code that can read the new secret, send a test delivery, and end the overlap only after the new path succeeds. Payclave keeps the previous secret valid for no more than 24 hours.
Successful delivery is at least once, not exactly once. Network timeouts, retries, and manual replay can deliver the same event again. Put event.id under a unique constraint and claim it in the same durable workflow that schedules the business action.
If the event already exists, return a 2xx response without repeating the action. Payclave treats any 2xx response as acknowledgement. Failed deliveries retry after 1 minute, 5 minutes, 30 minutes, and 2 hours before automatic retries stop after the fifth failed attempt.
Use invoice.paid for the first verified transition to a fully paid invoice. Treat payment.underpaid, payment.overpaid, invoice.expired, and payment.failed as operational outcomes that need their own order logic.
Check the event type, merchant mode, invoice identifier, external reference, and commercial total before fulfillment. Do not turn every signed event into the same business action.
For the complete request fields, response schemas, error codes, and event types, use the Payclave API reference.
Related guides
Create a Payclave stablecoin checkout session from your backend, use idempotency correctly, redirect to hosted checkout, and confirm payment safely.
Read the guideReconcile Payclave invoices, payment records, platform fees, and direct wallet settlement without treating a transaction hash as the complete order record.
Read the guideLearn how Payclave verifies stablecoin settlement, confirmation state, recipient, token, amount, expiry policy, and duplicate transaction use before paid status.
Read the guideTest the workflow
Use test keys and the API playground to verify checkout creation, payment status, and signed webhook handling.