Opening gFinOS
Securing your workspace…
Issue wallets and cards from your app. All endpoints are same-shape between test and live — just swap the key prefix.
Register apps & mint keys: /merchant/sdk · OpenAPI: /api/public/openapi.json
# All SDK requests are Bearer-authenticated. Test keys start with sk_test_, live keys with sk_live_. # Mint keys under /merchant/sdk and store them server-side only. curl -s "https://project--5d71d9ce-9982-40e9-98e6-17efab07f151.lovable.app/api/public/sdk/v1/wallets/user_123" \ -H "Authorization: Bearer sk_test_..."
# Every write accepts an Idempotency-Key header. Repeat requests with the same key
# within 24h return the original response and the idempotent_replay: true flag.
curl -s -X POST "https://project--5d71d9ce-9982-40e9-98e6-17efab07f151.lovable.app/api/public/sdk/v1/wallets" \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000" \
-H "Content-Type: application/json" \
-d '{"externalUserId":"user_123","currency":"USD","email":"jane@example.com"}'# 60 requests / minute / key. Every response includes: # x-ratelimit-limit: 60 # x-ratelimit-remaining: 47 # x-efinos-schema-version: 1.1 # Exceeding the limit returns 429 with Retry-After: 60.
# Issue an embedded virtual card for a provisioned wallet.
curl -s -X POST "https://project--5d71d9ce-9982-40e9-98e6-17efab07f151.lovable.app/api/public/sdk/v1/cards" \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: card_user_123_v1" \
-H "Content-Type: application/json" \
-d '{"externalUserId":"user_123","kind":"virtual","currency":"USD","spendLimit":500}'// Verify every gFinOS webhook you receive. Signature is HMAC-SHA256 over the raw body.
// Your app-specific secret is derived from WEBHOOK_DISPATCH_SECRET + your app id, and
// is available in your app profile under /merchant/sdk (never commit it).
import { createHmac, timingSafeEqual } from "crypto";
export function verifyEfinosWebhook(rawBody: string, header: string, secret: string): boolean {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(header, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
// In your webhook handler:
// const sig = req.headers.get("x-efinos-signature");
// if (!verifyEfinosWebhook(await req.text(), sig, MY_APP_SECRET)) return new Response("bad sig", { status: 401 });wallet.provisioned — fired after issueWallet
card.issued — fired after issueCard
card.authorized — fired on tap or SDK spend
card.captured — fired on merchant capture
card.refunded — fired on refund
Every payload:
{
"event": "wallet.provisioned",
"schema_version": "1.1",
"sent_at": "2026-07-20T12:00:00Z",
"data": { ... }
}