cresvadevelopers

Webhooks Guide

Receive real-time notifications when events happen in your storefront — queries, negotiations, transactions, and more.

How webhooks work

When an event occurs (e.g., a negotiation is accepted or a transaction is completed), Cresva sends an HTTP POST request to your configured endpoint with the event data as a JSON payload.

  1. Register a webhook endpoint in your dashboard
  2. Cresva sends a POST request with the event payload
  3. Your server verifies the HMAC signature
  4. Your server processes the event and returns a 2xx response

Setting up an endpoint

Navigate to Settings → Webhooks in the Cresva dashboard. Add a new endpoint with:

  • URL: Your HTTPS endpoint (e.g., https://your-app.com/webhooks/cresva)
  • Events: Select which event types to subscribe to, or choose "All events"
  • Secret: A webhook secret will be generated — save this for signature verification

Your endpoint must respond with a 200 status code within 30 seconds, or the delivery will be marked as failed.

Verifying signatures

Every webhook request includes an X-ACP-Signature header with an HMAC-SHA256 signature of the request body. Always verify this signature to ensure the webhook is authentic.

javascript
import crypto from "crypto";

function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody, "utf-8")
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
}

// Express.js example
app.post("/webhooks/cresva", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-acp-signature"];
  const isValid = verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).send("Invalid signature");
  }

  const event = JSON.parse(req.body);
  console.log("Event received:", event.type);

  // Process the event...
  switch (event.type) {
    case "transaction.completed":
      handleTransactionCompleted(event.data);
      break;
    case "negotiation.accepted":
      handleNegotiationAccepted(event.data);
      break;
  }

  res.status(200).send("OK");
});

Event payload format

Every webhook event has this structure:

JSON
{
  "id": "evt_1234567890",
  "type": "transaction.completed",
  "created_at": "2026-03-27T10:30:00Z",
  "data": {
    "transaction_id": "txn_x9y8z7",
    "status": "COMPLETED",
    "total": 178.60,
    "currency": "USD",
    "items": [
      {
        "product_id": "prod_h7k2m",
        "title": "ProSound ANC-300 Wireless Headphones",
        "quantity": 1,
        "price": 164.99
      }
    ]
  }
}

Event types

Queries

query.completedquery.failed

Negotiations

negotiation.initiatednegotiation.counterednegotiation.acceptednegotiation.rejectednegotiation.expirednegotiation.withdrawn

Transactions

transaction.createdtransaction.confirmedtransaction.paidtransaction.fulfillingtransaction.completedtransaction.cancelledtransaction.disputedtransaction.resolved

Escrow

escrow.createdescrow.releasedescrow.disputed

Products

product.updatedproduct.out_of_stockproduct.back_in_stock

Pricing

price.changed

Offers

offer.createdoffer.expiredoffer.claimed

Trust

trust.updated

Bundles

bundle.createdbundle.expired

Feedback

feedback.received

Retry behavior

If your endpoint returns a non-2xx response or times out (30 seconds), Cresva retries with exponential backoff:

Attempt 1ImmediateFirst delivery attempt
Attempt 2+1 minuteFirst retry
Attempt 3+5 minutesSecond retry
Attempt 4+30 minutesThird retry
Attempt 5+2 hoursFourth retry
Attempt 6+24 hoursFinal retry

After 6 failed attempts, the endpoint is automatically disabled and an email notification is sent to the account owner. Re-enable it from the dashboard after fixing the issue.