0Bit Documentation

Webhook signing

Verify 0Bit webhook signatures using the raw body, reject forged deliveries, dedupe event ids, and replay safely.

Webhook signing protects your backend from forged events. A valid webhook proves the event body was signed with your webhook secret for the relevant environment. It does not remove the need to validate object state, dedupe event ids, and reconcile with trusted API reads when necessary.

Use the raw body

Signature verification must run against the exact bytes sent to your endpoint. If your framework parses JSON first, verification can fail even when the event is legitimate.

Verification flow

Express example

import express from "express";
import { GateClient } from "@0bit/gate";

const gate = new GateClient({ apiKey: process.env.OBIT_SECRET_KEY! });
const app = express();

app.post("/webhooks/0bit", express.raw({ type: "application/json" }), (req, res) => {
  try {
    const event = gate.webhooks.constructEvent(
      req.body,
      req.header("Gate-Signature"),
      process.env.OBIT_WEBHOOK_SECRET!,
    );

    // Persist event.id and the product object state before acknowledging.
    res.sendStatus(200);
  } catch {
    res.status(400).send("invalid signature");
  }
});

What to verify

CheckWhy
Header existsMissing signatures should not reach business logic.
Signature matches raw bodyConfirms the payload was signed by 0Bit for this endpoint.
Timestamp freshness, where providedReduces replay-window risk.
Environment secretSandbox events must use sandbox whsec_*; live events must use live whsec_*.
Event id uniquenessPrevents duplicate fulfillment during retries or replay.
Object statusEnsures your state change matches the current product object.

Framework notes

Framework patternImplementation detail
ExpressUse express.raw({ type: "application/json" }) only on the webhook route.
Next.js route handlerRead await request.text() or bytes before parsing JSON.
API gatewayDisable body rewriting/compression on the webhook path if it changes signed bytes.
Queue-first handlerVerify at the edge, then enqueue the verified raw event or normalized event record.

Dedupe table

Store a small event ledger in your application database.

ColumnPurpose
event_idUnique dedupe key.
event_typeRoutes the handler.
object_idJoins to session, quote, trade, payment, settlement, or transaction records.
received_atAudit and replay diagnostics.
processed_atConfirms durable handling completed.
statusreceived, processed, ignored_duplicate, failed, or your equivalent.
request_idSupport traceability when present.

Returning 2xx without storing the event makes replay and support investigation much harder. Store first, then trigger downstream work.

Replay behavior

Webhook replay is for recovery, not for changing business state twice. Your handler must be safe when a previously processed event is replayed.

Replay caseCorrect behavior
Event already processedReturn 2xx and do not run fulfillment twice.
Event received but not processedResume processing from the durable event record.
Event invalidReturn 400; replaying an invalid event should not make it valid.
Object state has advancedRead trusted object state and apply only the valid transition.

Security rules

  • Keep whsec_* values in server secrets only.
  • Do not log webhook secrets or full signature headers.
  • Do not store raw PII-heavy payloads unless policy requires it.
  • Rotate webhook secrets if an endpoint or log sink is exposed.
  • Use separate webhook secrets for sandbox and live.
  • Treat browser callbacks as untrusted for fulfillment.
  • Monitor dead-lettered deliveries and verification failures.

On this page