Skip to main content

Webhooks

SDK webhooks notify your backend when server-side borrow workflow milestones happen. The first supported event is fired when the BTC deposit is confirmed during the SDK lending workflow.

Configure an endpoint

Webhook settings are scoped to the API key used by the SDK request.
import {
  BorrowSDK,
  ChainType,
  SdkWebhookEventType,
} from '@satsterminal-sdk/borrow';

const sdk = new BorrowSDK({
  apiKey: process.env.SATS_TERMINAL_API_KEY!,
  wallet,
});

await sdk.updateWebhookConfig({
  enabled: true,
  url: 'https://api.example.com/webhooks/satsterminal',
  events: [SdkWebhookEventType.BTC_DEPOSIT_CONFIRMED],
});
Rotate the signing secret before accepting production traffic:
const { secret } = await sdk.rotateWebhookSecret();
Store the returned secret securely. It is only returned in the rotation response.

Event

sdk.borrow.btc_deposit_confirmed

Sent after the SDK lending workflow confirms the BTC bridge deposit and receives the bridge redeem transaction hash.
{
  "id": "SDK_BTC_DEPOSIT_CONFIRMED-<transactionId>",
  "type": "sdk.borrow.btc_deposit_confirmed",
  "apiVersion": "2026-05-28",
  "createdAt": "2026-05-28T12:00:00.000Z",
  "data": {
    "transactionId": "<sdkBorrowTransactionId>",
    "workflowId": "<temporalWorkflowId>",
    "status": "DEPOSIT_CONFIRMED",
    "btcDepositTxHash": "<btc deposit tx hash or null>",
    "bridgeRedeemTxHash": "<bridge redeem tx hash or null>",
    "depositAddress": "<btc deposit address or null>"
  }
}

Verify signatures

Each webhook request includes:
HeaderDescription
sats-terminal-eventWebhook event type
sats-terminal-deliveryDelivery/event id
sats-terminal-timestampUnix timestamp used in the signature
sats-terminal-signaturev1=<hex hmac>
Signatures are HMAC-SHA256 over ${timestamp}.${rawBody} using your webhook secret.
import crypto from 'node:crypto';

function verifySatsTerminalWebhook({
  rawBody,
  timestamp,
  signatureHeader,
  secret,
}: {
  rawBody: string;
  timestamp: string;
  signatureHeader: string;
  secret: string;
}) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  const received = signatureHeader.replace(/^v1=/, '');

  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(received, 'hex'),
  );
}