Skip to main content
When a cardholder pays, the network sends Venly an authorization — Venly reserves the amount from the cardholder’s wallet, then settles it (money leaves to the company’s settlement wallet) or reverses it (money returns to the cardholder) as the transaction clears. On the Finance API, that authorization is a payment request. This guide takes a cardholder account from creation to a working authorization, and calls out exactly where Venly-managed (VENLY_MANAGED) and self-custody (SELF_CUSTODY) companies differ. The difference is one extra step — self-custody wallets need a one-time permit before they can be charged.
Base URL (staging): https://api-staging.venlyfinance.com/v1 Auth: OAuth2 Bearer token in the Authorization header. Tokens expire after 5 minutes — see the Authentication guide.
Dedicated card-management endpoints (issue, freeze, list transactions) are on the roadmap. The authorization → settlement plumbing this guide uses is live today on the Finance API.

Prerequisites

1

A client provisioned for the card program

Your client_id must carry the finance-api-card role plus manage:payment-requests (and manage:all-payment-requests if you authorize by card-provider reference). Your Venly contact enables these.
2

An HTTP client and a UUID generator

Examples use curl. Money-moving calls need an idempotencyKey (UUID v4) — uuidgen, [guid]::NewGuid(), or crypto.randomUUID().
3

Self-custody only: the cardholder's wallet

A wallet address you’ll register on the account, plus access to its owner key to sign one permit.
The finance-api-card role is what provisions the escrow wallet every card authorization needs. An account created by a client without that role is non-custodial-only and rejects authorizations with escrow-wallet-missing. The role is applied at account-create time — grant it before creating the cardholder account; it is not back-filled onto existing accounts.

The flow

Venly-managed accounts skip step 4 entirely — their wallets are permitted automatically.

Step 1 — Authenticate

curl -X POST https://login-staging.venly.io/auth/realms/VenlyFinance/protocol/openid-connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
Response
{ "access_token": "eyJhbGciOi...", "expires_in": 300, "token_type": "Bearer" }
Cache the token. Refresh on 401 or shortly before expires_in — don’t re-authenticate on every call.

Step 2 — Create the cardholder account

The account represents the cardholder; its wallet funds the card. Optionally attach a cardProviderReference so authorizations arriving by card-provider reference resolve to this account. The only structural difference between company types is the wallet address:
  • Venly-managed — Venly creates and holds the wallet. Omit address.
  • Self-custody — the cardholder controls the wallet. Send their address (required).
curl -X POST https://api-staging.venlyfinance.com/v1/accounts \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "cardholder-12345",
    "name": "Jane Doe — Card",
    "chain": "BASE",
    "cardProviderReference": { "type": "PAYMENTOLOGY", "referenceId": "ACC-12345" }
  }'
Response (201)
{
  "success": true,
  "result": {
    "id": "b2a1f0e9-8c7d-4e3a-9f21-0a1b2c3d4e5f",
    "externalId": "cardholder-12345",
    "name": "Jane Doe — Card",
    "kycStatus": "VERIFICATION_PENDING",
    "status": "ACTIVE",
    "createdAt": "2026-01-15T09:30:00",
    "version": 0
  }
}
cardProviderReference can only be set when the account is created. Decide the card link up front.

Step 3 — Verify the account

A new account is kycStatus: VERIFICATION_PENDING. A Venly admin reviews and verifies it — there’s no API call to trigger this. Poll until it flips:
curl https://api-staging.venlyfinance.com/v1/accounts/{accountId} \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response (200)
{ "success": true, "result": { "id": "b2a1f0e9-...", "kycStatus": "VERIFIED", "status": "ACTIVE", "version": 1 } }
Until VERIFIED, authorizations fail with account-not-active (“KYC status must be VERIFIED”). See Account verification.

Step 4 — Fund the wallet

The account wallet must hold the stablecoin the card spends. Read its address and balance:
curl https://api-staging.venlyfinance.com/v1/accounts/{accountId}/wallets \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Response (200)
{
  "success": true,
  "result": [
    {
      "id": "9f8e7d6c-5b4a-4938-8271-6a5b4c3d2e1f",
      "chain": "BASE",
      "type": "VENLY_MANAGED",
      "address": "0x01DA4aa698B545D6A32ef44DB37139D3b27eAF61",
      "balances": [
        { "asset": "USDC", "contractAddress": "0x036CbD53842c5426634e7929541eC2318f3dCF7e", "amount": { "total": "0", "available": "0", "reserved": "0" } }
      ],
      "amlStatus": "APPROVED"
    }
  ]
}
  • Venly-managed — fund the returned Venly address with the stablecoin. Gas is covered by Venly’s orchestration wallet; the account wallet only needs the token.
  • Self-custody — the cardholder funds their own address with the stablecoin.

Step 5 — Activate the wallet (self-custody only)

Venly-managed wallets are permitted automatically — skip to Step 6.
A self-custody wallet is created PENDING. Before it can be charged, the owner signs a one-time permit for every supported asset on the wallet, and the wallet becomes ACTIVE only once all of them are CONFIRMED. Until then authorizations fail with account-wallet-not-active.
A raw on-chain approve() sets the ERC-20 allowance but does not activate the wallet — only a confirmed permit does. And it’s all assets: a wallet that supports USDC and EURC needs both permits confirmed before it goes ACTIVE.
1

Get the messages to sign

GET /accounts/{accountId}/wallets/{walletId}/permits returns, per asset, a supportedAssetId and an EIP-712 typedData object.
2

Sign each with the owner's key

Sign typedData with the wallet owner key (off-chain, no gas). The signature must recover to the owner or the permit becomes FAILED. Produces v, r, s.
3

Submit each signature

POST .../permits with the supportedAssetId and signature → HTTP 200 with result.status.
4

Wait for every asset to confirm

Poll GET .../permits until each asset is CONFIRMED. When the last one confirms, the wallet activates.
curl -X POST https://api-staging.venlyfinance.com/v1/accounts/{accountId}/wallets/{walletId}/permits \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "supportedAssetId": "5d4c5b99-2ce8-40a1-bc1d-173dd400fa89",
    "signature": { "v": "28", "r": "0x...", "s": "0x..." }
  }'
Full signing example (ethers.js) and the allowance check are in Approving transfers without gas.

Step 6 — Create the authorization

With the account VERIFIED, funded, and (self-custody) ACTIVE, reserve funds. There are two endpoints:
curl -X POST https://api-staging.venlyfinance.com/v1/accounts/{accountId}/payment-requests \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 25.0,
    "currency": "USD",
    "externalId": "auth-67890",
    "description": "Card authorization #67890",
    "idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
  }'
Response
{
  "success": true,
  "result": {
    "id": "d4e5f6a7-b8c9-4012-8345-6789abcdef01",
    "accountId": "b2a1f0e9-8c7d-4e3a-9f21-0a1b2c3d4e5f",
    "amount": { "fiat": 25.0, "crypto": "25.000000" },
    "originalAmount": { "fiat": 25.0, "crypto": "25.000000" },
    "currency": "USD",
    "status": "RESERVED",
    "executions": [
      { "type": "AUTHORIZATION", "chain": "BASE", "asset": "USDC", "amount": 25.0, "status": "RESERVED", "transactionHash": "0xa1b2..." }
    ]
  }
}
The reserved amount now shows in the wallet’s reserved balance. Amounts carry both fiat and crypto — see Payment requests.
The reservation runs on-chain. On fast chains the response is RESERVED straight away; on others it returns PENDING and flips to RESERVED once the authorization transaction confirms. There’s no GET endpoint — re-send the create with the same idempotencyKey and body to read the latest state, and match on your externalId.

Step 7 — Settle or reverse

When the card transaction clears, resolve the hold:
  • Settle — money moves from escrow to the company’s settlement wallet (SETTLINGSETTLED). The settle amount can be equal to, below, or above the authorized amount.
  • Reverse — the full reserved amount returns to the cardholder (REVERSINGREVERSED). Use it to release a hold you won’t charge.
Both have by-reference variants that take the same cardProviderReference instead of the accountId.

Managed vs self-custody at a glance

StepVenly-managed (VENLY_MANAGED)Self-custody (SELF_CUSTODY)
Create accountNo addressaddress required
Wallet custodyVenly holds the keyCardholder holds the key
FundingFund the Venly-created addressCardholder funds their own address
GasPaid by Venly orchestrationPaid by Venly orchestration
Wallet activationAutomaticSign a permit for every supported asset
Verify KYCRequiredRequired
Authorize / settle / reverseIdenticalIdentical

Common pitfalls

The account was created by a client without finance-api-card, so it’s non-custodial-only and has no escrow wallet. Grant the role, then create a new cardholder account (escrow isn’t back-filled onto existing accounts).
A self-custody wallet that isn’t ACTIVE yet. Confirm a permit for every supported asset on the wallet — the wallet only activates once they’re all CONFIRMED. A raw approve() doesn’t count. See Permits & allowances.
The account is still VERIFICATION_PENDING. A Venly admin must verify it — poll GET /accounts/{id} until kycStatus is VERIFIED.
Self-custody accounts must include the cardholder’s wallet address on creation. Venly-managed accounts must not.
Tokens last 5 minutes. Catch 401, refresh, and retry — don’t pre-authenticate every request.

Next steps

Payment requests

The full reserve → settle/reverse/adjust lifecycle.

Permits & allowances

Gasless wallet activation for self-custody accounts.

Create a payment request

Endpoint reference, fields, and error codes.

Card Issuance overview

The white-label card program and its roadmap.