Webhooks

Receive resource events when checkout, payout, and refund state changes occur.

Overview

Webhooks let your platform react to treasury events without polling. Configure endpoints in Developers → Webhooks. The dashboard now exposes endpoint filtering, delivery inspection, manual replay, and secret rotation so operators do not need to leave the app to manage delivery health.

Event Types

These are the current public event names emitted by the checkout, payout, and refund flows.

EventDescription
checkout.completedA checkout completed and the sale was booked to the ledger
checkout.failedA sandbox checkout was failed for decline and failure-path testing
refund_request.createdA buyer-facing refund request was opened and is waiting for review
refund_request.completedA refund request was approved and the refund was completed through Soledgic
refund_request.rejectedA refund request was rejected without moving funds
refund_request.cancelledA pending refund request was cancelled before review
refund.createdA refund was created inside Soledgic
sale.refundedA processor-backed refund finished and the sale is fully reflected as refunded
payout.createdA payout record was created
payout.processingA payout rail outcome is pending reconciliation
payout.executedA payout rail reported completion
payout.failedA payout rail reported failure
payout_request.*A creator payout request was created, reviewed, cancelled, failed, or completed
dispute.createdA processor dispute was opened for a charge or sale
dispute.funds_withdrawnDisputed funds were withdrawn by the processor
chargeback.createdA chargeback or chargeback-risk event was recorded
chargeback.funds_withdrawnChargeback funds were withdrawn by the processor
hold.createdFunds were placed on hold with a structured reason code
hold.releasedHeld funds were released to the recipient balance
hold.failedA hold release failed or could not be completed

Webhook Payload

Deliveries include a JSON body and signature headers.

Headers

HeaderDescription
Content-Typeapplication/json
X-Soledgic-Signaturet=<unix>,v1=<hex> HMAC-SHA256 signature for payload verification
X-Soledgic-EventThe event name, such as payout.executed
X-Soledgic-Delivery-IdStable delivery identifier for deduplication and replay handling
X-Soledgic-AttemptCurrent delivery attempt number

Example Payloads

checkout.completed

{
  "event": "checkout.completed",
  "data": {
    "session_id": "cs_uuid",
    "reference_id": "order_12345",
    "amount": 5000,
    "currency": "USD",
    "participant_id": "creator_001",
    "transaction_id": "txn_uuid",
    "status": "completed",
    "occurred_at": "2026-04-27T12:00:00Z"
  }
}

payout.executed

{
  "event": "payout.executed",
  "data": {
    "payout_id": "6f2f0ac5-4f50-4e96-b412-4f534f1c85c6",
    "participant_id": "creator_001",
    "amount": 15000,
    "currency": "USD",
    "rail": "ach",
    "external_id": "tr_123",
    "status": "completed",
    "occurred_at": "2026-04-27T14:30:00Z"
  }
}

refund.created

{
  "event": "refund.created",
  "data": {
    "refund_id": "txn_uuid",
    "original_sale_reference": "order_12345",
    "amount": 2500,
    "reason": "Customer requested",
    "status": "completed",
    "occurred_at": "2026-04-27T15:00:00Z"
  }
}

payout.failed

{
  "event": "payout.failed",
  "data": {
    "payout_id": "6f2f0ac5-4f50-4e96-b412-4f534f1c85c6",
    "participant_id": "creator_001",
    "amount": 15000,
    "error": "Insufficient funds in source account",
    "status": "failed",
    "occurred_at": "2026-04-27T14:35:00Z"
  }
}

Verifying Signatures

Always verify the signature before processing the payload.

Important: The webhook secret is shown when the endpoint is created. Store it outside your app code. New Soledgic webhook secrets start with wh_sec_slk.

import { createHmac, timingSafeEqual } from 'node:crypto';

function verifySoledgicSignature(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((part) => part.split('=')),
  );

  const timestamp = parts.t;
  const received = Buffer.from(parts.v1 || '', 'hex');
  const expectedHex = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');
  const expected = Buffer.from(expectedHex, 'hex');

  return received.length === expected.length && timingSafeEqual(received, expected);
}

const rawBody = await request.text();
const signature = request.headers.get('x-soledgic-signature') || '';

const isValid = verifySoledgicSignature(
  rawBody,
  signature,
  process.env.SOLEDGIC_WEBHOOK_SECRET || '',
);

if (!isValid) {
  throw new Error('Invalid webhook signature');
}

Retry Policy

  • Up to 5 retry attempts for non-2xx responses
  • Exponential backoff between attempts, capped at 4 hours
  • HTTP 429 responses are slowed down to at least 5 minutes before retry
  • Failed deliveries stay visible in webhook delivery history, including payload and response body
  • Operators can manually replay exhausted deliveries from Developers → Webhooks

Operations

The webhook settings screen is designed for production operations, not just initial setup.

  • Filter delivery history by endpoint, event, status, checkout ID, order ID, or payment ID
  • Inspect payloads, headers, attempts, and downstream response bodies
  • Replay stuck or exhausted deliveries after downstream fixes
  • Rotate endpoint secrets; the previous secret remains valid only during the short rotation grace window

Best Practices

Return 200 quickly

Acknowledge the delivery, then process work asynchronously

Deduplicate by event ID

Webhook delivery retries are normal and should be safe

Verify signatures

Never trust the body before checking the HMAC signature