Agent skill

Laso Finance Webhooks Skill

Receive and verify Laso Finance webhooks, which follow the Standard Webhooks specification. Use when registering a Laso Finance webhook URL with POST /register-webhook, storing the whsec_ signing secret, debugging webhook-id / webhook-timestamp / webhook-signature verification, or handling notification.transaction, notification.deposit, notification.account, notification.card and notification.giftCard events for an AI agent's bank account.

Install this skill

npx skills add hookdeck/webhook-skills --skill laso-finance-webhooks


Laso Finance provides banking, cards and payouts for AI agents. An agent registers one webhook URL per account with POST /register-webhook and then receives every account notification (banking application status, transfers, deposits, card orders, withdrawals) as a signed JSON POST. Signatures follow Standard Webhooks, so each delivery carries webhook-id, webhook-timestamp and webhook-signature headers.

When to Use This Skill

  • How do I receive Laso Finance webhooks?
  • How do I verify a Laso Finance webhook signature (webhook-signature)?
  • Where do I get the Laso Finance webhook secret (whsec_...)? (Only from the POST /register-webhook response.)
  • How do I handle notification.transaction, notification.deposit or notification.withdrawal events?
  • Why is my Laso Finance webhook verification failing, or failing only on Hookdeck retries and replays?
  • Why did Laso Finance stop sending webhooks? (50 consecutive failures disable the registration.)

Verification (core)

HeaderValue
webhook-idmsg_... message id. Your idempotency key (the body has no event id)
webhook-timestampUnix seconds when the delivery was signed
webhook-signatureOne or more space-separated v1,<base64> entries

Signed content is {webhook-id}.{webhook-timestamp}.{raw body}, HMAC-SHA256, base64 output. The key is the base64-decoded part of the whsec_... secret after the prefix. Compare against every v1, entry; ignore other versions. If webhook-id is absent, read svix-id, svix-timestamp and svix-signature instead, as Hookdeck does. The examples use the standardwebhooks library (npm and PyPI):

const { Webhook } = require('standardwebhooks');

// rawBody: exact request body (Buffer or string), NOT re-serialised JSON
function verifyLaso(rawBody, headers, secret = process.env.LASO_FINANCE_WEBHOOK_SECRET) {
  const p = headers['webhook-id'] ? 'webhook' : 'svix'; // fall back to the svix-* names
  new Webhook(secret).verify(rawBody, {
    'webhook-id': headers[`${p}-id`],
    'webhook-timestamp': headers[`${p}-timestamp`],
    'webhook-signature': headers[`${p}-signature`],
  }, { jsonParse: false }); // throws on a bad signature or stale timestamp → respond 401
}

The library always enforces a fixed 5-minute timestamp window. Laso Finance itself never retries, but Hookdeck retries and replays resend the original webhook-timestamp, so the library rejects them once they are more than 5 minutes old. If you receive through Hookdeck and need late retries to pass, use the manual verifier in references/verification.md, which can drop or widen the window. In Express, read the body with express.raw({ type: '*/*' }) so any content type verifies, and parse JSON only after the check.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Event Type and Payload

Every body is the same envelope. The event name is the top-level type, which is notification. plus the notification category:

{
  "type": "notification.transaction",
  "timestamp": "2026-07-31T18:04:05.000Z",
  "data": {
    "user_id": "usr_abc123",
    "title": "Bank transfer complete",
    "text": "$250.00 landed in your agent wallet.",
    "category": "transaction"
  }
}

data is the notification as the account owner sees it: title and text are human-readable, and category repeats the bare category. There is no event id in the body: use the webhook-id header.

Common Event Types

EventCategory
notification.accountBanking application and account status. Also the signed test delivery sent when you register
notification.transactionBank transfers and payouts, such as a transfer completing
notification.depositAgent wallet deposits
notification.withdrawalWithdrawals
notification.cardCard orders
notification.giftCardGift card orders
notification.refundRefunds (named in the docs, not described)
notification.balanceUpdateBalance changes (named in the docs, not described)

The docs list these eight under "Categories include" without describing each one, so the list is open-ended and the descriptions above are inferred from the names and the docs' examples. The examples handle all eight and ignore other types. There is no per-type selection: one webhook receives every notification. See references/overview.md.

Delivery Semantics

Respond with any 2xx within 10 seconds, then process asynchronously. Deliveries are not retried: a failed or timed-out delivery is lost, and the status endpoints (listBankingTransactions, /get-withdrawal-status) remain the source of truth, so reconcile against them. After 50 consecutive failures the registration is disabled; GET /get-webhook shows the failure counters and last attempt, and registering again re-enables it. Deduplicate on webhook-id. Return 400 for missing headers and 401 for a bad signature.

Environment Variables

LASO_FINANCE_WEBHOOK_SECRET=whsec_xxxxx   # full value from the register response; handlers fail closed (500) without it

The secret is returned only in the POST /register-webhook response and GET /get-webhook never returns it. Registering again rotates it, so update the variable at the same time.

Local Development

# Express / Next.js (use 8000 for FastAPI). No install or account required.
npx hookdeck-cli listen 3000 laso-finance --path /webhooks/laso-finance

Register the printed URL with POST /register-webhook (the URL must be public HTTPS). Registering sends a signed notification.account test delivery to it.

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →