Skip to main content
This is a list of all event types Pandabase can send to your configured webhook endpoints. Each event is delivered as a POST request with a JSON payload.

Webhook structure

Every webhook delivery contains the event type, a unique event ID, a timestamp, and a data object with order, customer, and geo information.
{
  "event": "PAYMENT_COMPLETED",
  "id": "evt_cm5x7k2a000001j0g8h3f9d2e",
  "timestamp": "2026-03-07T12:00:00.000Z",
  "data": {
    "order": {
      "id": "ord_cm5x7k2a000001j0g8h3f9d2e",
      "orderNumber": "cs_cm5x7k2a000001j0g8h3f9d2e",
      "status": "COMPLETED",
      "amount": 5000,
      "currency": "USD",
      "customFields": {
        "discord": "johndoe#1234"
      },
      "metadata": {
        "campaign": "spring_sale",
        "ref": "partner_abc"
      },
      "items": [
        {
          "productId": "prd_cm5x7k2a000001j0g8h3f9d2e",
          "variantId": null,
          "name": "Pro Plan",
          "quantity": 1,
          "amount": 5000
        }
      ]
    },
    "customer": {
      "id": "cus_cm5x7k2a000001j0g8h3f9d2e",
      "email": "buyer@example.com"
    },
    "geo": {
      "ip": "1.2.3.4",
      "country": "US",
      "city": "Miami",
      "region": "FL"
    }
  }
}

Webhook headers

Every delivery includes these headers for verification and deduplication:
HeaderDescription
X-Pandabase-SignatureHMAC-SHA256 hex digest of the JSON body, signed with your webhook secret
X-Pandabase-TimestampUnix timestamp (milliseconds) of when the delivery was sent
X-Pandabase-IdempotencyUnique delivery identifier — use this to deduplicate retried deliveries
Content-TypeAlways application/json
User-AgentPandabase (https://pandabase.io)

Verifying signatures

Always verify the X-Pandabase-Signature header to confirm the webhook was sent by Pandabase and hasn’t been tampered with.
import crypto from "crypto";

function verifyWebhook(
  rawBody: string,
  signature: string,
  secret: string,
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

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

// In your handler:
const signature = request.headers["x-pandabase-signature"];
if (!verifyWebhook(request.rawBody, signature, WEBHOOK_SECRET)) {
  return response.status(401).send("Invalid signature");
}
Always use constant-time comparison (e.g. timingSafeEqual, hmac.compare_digest) when verifying signatures to prevent timing attacks.

Retries

Failed deliveries are retried up to 5 times with exponential backoff starting at 1 second (1s, 2s, 4s, 8s, 16s). Your endpoint should return a 2xx status code to acknowledge receipt. Any other status code or a timeout (15 seconds) is treated as a failure and triggers a retry. Use the X-Pandabase-Idempotency header to deduplicate deliveries — the same event may be delivered more than once due to retries.

Event types

PAYMENT_PENDING

Fired when a customer initiates payment at checkout. The order has been created and a payment intent is awaiting confirmation.
At this stage, geo data (city, region, country) may be null — it is enriched asynchronously after checkout. Subsequent events will include full geo data.

PAYMENT_COMPLETED

Fired when a payment is successfully collected. This is the primary event to listen for when fulfilling orders. The order status will be PROCESSING (if fulfillment is in progress) or COMPLETED (if already fulfilled). The items array contains all purchased products, customFields includes any data the customer submitted at checkout, and metadata contains any developer-defined key-value pairs set when creating the checkout session.

PAYMENT_FAILED

Fired when a payment fails, is canceled by the customer, or expires without being completed. The order is moved to CANCELLED status. If a coupon was applied, its usage count is automatically restored.

PAYMENT_REFUNDED

Fired when a charge is refunded, whether initiated through the Pandabase dashboard or detected from an external refund. The order status is set to REFUNDED.

PAYMENT_DISPUTED

Fired when a customer opens a chargeback dispute on a payment. The order moves to CHARGEBACK status and the disputed amount plus a $20.00 dispute fee is deducted from the store’s available balance.

PAYMENT_DISPUTE_WON

Fired when a dispute is resolved in the merchant’s favor. The disputed amount and fee are restored to the store’s available balance and the order returns to COMPLETED status.

PAYMENT_DISPUTE_LOST

Fired when a dispute is resolved against the merchant. The deducted funds remain lost and the order stays in CHARGEBACK status.

Best practices

  1. Return 200 quickly. Process webhook logic asynchronously if it takes more than a few seconds. Pandabase times out after 15 seconds.
  2. Deduplicate with idempotency keys. Store the X-Pandabase-Idempotency header value and skip events you’ve already processed.
  3. Verify signatures. Always validate X-Pandabase-Signature before processing the payload.
  4. Use event filtering. Subscribe only to the events you need to reduce noise and processing overhead.
  5. Handle out-of-order delivery. In rare cases, events may arrive out of order (e.g. PAYMENT_COMPLETED before PAYMENT_PENDING). Use the timestamp field and order status to handle this gracefully.