Webhooks

Real-time event notifications.

Receive signed JSON events within 200ms of every state change across your platform's virtual accounts, payments, and KYB verifications.

Overview

Webhooks allow your application to react to events in the Mainstreetspine system without polling. Register one or more HTTPS endpoints and we'll POST events to them as state changes occur.

All webhook deliveries include a signature header you can use to verify the payload originated from Mainstreetspine.

Event Types

Subscribe to individual event types or use * to receive all events for your platform.

virtual_account.createdNew virtual account provisioned and active
virtual_account.balance_updatedAccount balance changed (credit or debit)
payment.initiatedPayment submitted to payment rail
payment.completedPayment settled successfully
payment.failedPayment failed — reason code included
payment.returnedACH return received from receiving bank
kyb.approvedBusiness entity KYB verification approved
kyb.pending_reviewKYB routed to manual review queue
kyb.rejectedKYB verification rejected — reason included
ledger.entry.createdNew ledger entry posted to sub-ledger

Payload Schema

All events share the same envelope schema.

JSON
{
  "id": "evt_Xm9jQp3L",
  "type": "payment.completed",
  "created_at": "2025-04-14T14:32:00Z",
  "platform_id": "plt_8xHQ2mNk",
  "data": {
    // event-specific resource object
  }
}

Signature Verification

Every webhook delivery includes an X-MSS-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook secret.

Node.js — Verify signature
const crypto = require('crypto');

function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

Retry Logic

If your endpoint returns a non-2xx status or times out (30s), we retry the delivery with exponential backoff:

  • Attempt 1: immediate
  • Attempt 2: +5 minutes
  • Attempt 3: +30 minutes
  • Attempt 4: +2 hours
  • Attempt 5: +12 hours

After 5 failed attempts, the event is marked as undelivered and logged in your webhook delivery dashboard. You can manually replay any event from the partner portal.