Skip to content

Webhooks

Webhooks let FairShare send split-session events to your backend in near real time.

Use webhooks to:

  • update order/payment state in your system
  • trigger fulfillment only after a terminal event (for example split_session.completed)
  • keep your system in sync without polling

Webhooks are environment-specific:

  • Sandbox endpoints receive only sandbox events.
  • Live endpoints receive only live events.

If you move to live, create a live webhook endpoint separately. For more details, see Sandbox vs Live.

Endpoint Requirements

Your webhook endpoint should:

  • accept POST requests with Content-Type: application/json
  • be publicly reachable over HTTPS
  • return a 2xx response quickly after basic validation and enqueueing
  • avoid long synchronous work in the request thread

FairShare treats these as successful delivery:

  • any 2xx status
  • 409 (useful when your side already processed the event and treats it as duplicate)

Webhook Headers

Each delivery includes:

  • X-SplitPay-Event: event name (for example split_session.completed)
  • X-SplitPay-Delivery: unique delivery attempt ID
  • X-SplitPay-Timestamp: Unix timestamp (seconds)
  • X-SplitPay-Signature: HMAC signature, format v1=<hex>

Signature Verification

Verify every webhook before processing.

Signing format:

  1. Build the signed payload string: timestamp + "." + rawBody
  2. Compute HMAC-SHA256 using your webhook signing key
  3. Compare against X-SplitPay-Signature value after removing the v1= prefix

Also enforce a timestamp tolerance window (for example 5 minutes) to reduce replay risk.

Event Payload Shape

All webhook events use this envelope:

json
{
  "id": "event_uuid",
  "type": "split_session.completed",
  "created": "2026-04-08T10:15:30Z",
  "data": {
    "object": {}
  }
}
  • id: unique event ID (stable identifier for dedupe on your side)
  • type: event type
  • data.object: event-specific object payload

Example (split_session.completed):

json
{
  "id": "7f7dfef6-c76a-4ef0-a631-fd8caea3abec",
  "type": "split_session.completed",
  "created": "2026-04-08T10:15:30Z",
  "data": {
    "object": {
      "id": "9f6e96d3-7b18-4f0e-96b8-e2eb9f6a2a1c",
      "merchantId": 123,
      "status": "COMPLETED",
      "environment": "SANDBOX",
      "gateway": "STRIPE",
      "currency": "QAR",
      "total": 200.00,
      "collected": 200.00,
      "createdAt": "2026-04-08T10:00:00Z",
      "splitType": "EQUAL_COUNT",
      "expectedContributors": 4,
      "minContribution": null,
      "convenienceFeeType": "FIXED_PER_CONTRIBUTION",
      "convenienceFeeValue": 2.50,
      "splitSessionLink": "https://app.fairsharepay.com/contributor/9f6e96d3-7b18-4f0e-96b8-e2eb9f6a2a1c",
      "leadContributorName": "Jane Doe",
      "leadContributorEmail": "jane@example.com",
      "completedAt": "2026-04-08T10:15:29Z",
      "meta": "order-123"
    }
  }
}

Webhook Events

EventWhat it means
contribution.createdA new contribution was created for a split session.
split_session.createdA new split session was created.
split_session.lead_authorizedThe lead contributor's payment was successfully authorized.
split_session.partialThe split session has received some funding but is not complete.
split_session.completedThe split session is fully completed.
split_session.expiredThe split session expired before completion.
split_session.canceledThe split session was canceled.
split_session.refundedA completed split session was refunded.
split_session.status_changedGeneric status transition event with fromStatus and toStatus.

Delivery and Retries

Delivery is asynchronous and at-least-once. You may receive duplicates or out-of-order events.

Retry behavior:

  • retried on network failure / timeout
  • retried on HTTP 408, 429, and 5xx
  • not retried on other 4xx responses
  • up to multiple retry cycles with increasing backoff

Because delivery is at-least-once, your handler must be idempotent.

Idempotent Processing Pattern

Recommended server-side flow:

  1. Verify signature and timestamp.
  2. Parse JSON and extract event.id, type, and data.object.id.
  3. Check if event.id has already been processed.
  4. If already processed, return 200 (or 409) immediately.
  5. Enqueue work or apply state transition transactionally.
  6. Mark event.id as processed.
  7. Return 2xx.

Delivery Attempts in Dashboard

In the dashboard, each attempt shows:

  • attempt time
  • event type
  • result
  • HTTP status code
  • request body
  • response body

Use this view to debug signature failures, endpoint timeouts, and non-retryable 4xx responses.

  1. Create webhook endpoint in sandbox.
  2. Implement signature verification and idempotent processing.
  3. Test success and failure scenarios and review delivery attempts.
  4. Promote the same implementation to live.
  5. Create a separate live webhook endpoint and signing key.