0fra

Merchant onboarding

Two ways to add a sub-merchant — direct API call or self-serve invitation link.

You have two flows to choose from.

Option A — direct creation (you have their wallet)

If your merchants have already given you a wallet address, post it directly:

curl -X POST https://api.0fra.dev/v1/onboarding/merchants \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "vendor_2001",
    "name": "Acme Music Festival",
    "merchant_wallet": "0xabc…",
    "chain_id": 84532,
    "reserve_bps": 500
  }'

The merchant is created in active state immediately. Use the same external_id you already use in your own systems — 0fra treats it as your local primary key.

Like Stripe Connect's onboarding link, this lets the merchant bind their own wallet — you never touch their private key.

curl -X POST https://api.0fra.dev/v1/merchants/invitations \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "vendor_2001",
    "suggested_name": "Acme Music Festival",
    "chain_id": 84532,
    "reserve_bps": 500,
    "expires_in_seconds": 604800
  }'
{
  "id": "...",
  "token": "moi_live_a1b2c3...",
  "url": "https://dash.0fra.dev/onboard/moi_live_a1b2c3...",
  "expires_at": "2026-05-02T00:00:00Z",
  "external_id": "vendor_2001"
}

2. Email the URL to your merchant

The link is single-use, expires after the time you set, and is bound to the external_id you supplied.

3. Merchant completes onboarding

The page at /onboard/<token> walks them through:

  1. Connect wallet (any EOA — MetaMask, Rabby, Coinbase Wallet)
  2. Choose their display name
  3. Sign a free message (personal_sign) attesting wallet ownership

The signature is verified by 0fra using viem.verifyMessage. On success, the merchant record is created and the invitation marked completed.

Listing & rotating invitations

GET /v1/merchants/invitations

Returns up to 50 most recent invitations with their state (pending / completed / expired) and the wallet attached if completed. To rotate (e.g. merchant lost the link), generate a fresh invitation with the same external_id — it'll get a new token; the old one stays expired.

Programmatic management

Once a merchant exists, common operations:

GET    /v1/merchants                       # list
GET    /v1/merchants/:id                   # detail
# Note: status & reserve adjustments are on the roadmap and currently
# require contacting 0fra support.

What's not on the roadmap (yet)

  • KYC / KYB verification — MVP relies on the platform doing its own diligence
  • Identity-doc upload via 0fra-hosted form
  • Tax W-8/W-9 collection

If your jurisdiction requires KYC before payouts, do it in your own system before generating the invitation.

On this page