Skip to main content
Applies to self-custody accounts. (New to that term? See Venly-managed vs self-custody.) Do this after the account is verified and its wallet’s AML check is approved.

Why an allowance is needed

A self-custody account holder controls their own wallet. For Venly to move that wallet’s tokens during a payment or transfer, the wallet must grant Venly’s orchestration wallet an ERC-20 allowance — permission to pull funds via transferFrom. Which assets your tenant can settle in is configuration, not a fixed list — read GET /supported-assets rather than hard-coding one.
Gasless permits are a property of the token contract on a specific chain, not of the symbol. The same symbol can support permits on one chain and not another, and a token without permit support has to go through allowance verification instead. Check with your Venly contact before committing to an asset that isn’t already enabled for you.

Two wallets, two responsibilities

Each account has two wallets, and each needs an allowance to the orchestration wallet: So on a self-custody account the only permit you sign is the account wallet’s. The escrow wallet — and, on Venly-managed accounts, the account wallet too — is permitted for you automatically.

Why a permit instead of a plain approve

A normal approve() costs gas and needs the wallet to hold the chain’s native coin. Instead, the holder signs an EIP-2612 message (off-chain, no gas) and Venly’s orchestration wallet submits it on-chain and pays the gas.
A permit is the route for a wallet controlled by a single key. If your wallet is a smart contract — a Safe, for example — it can’t produce the one signature a permit needs. Use approve() plus allowance verification instead.For a single-key wallet, prefer the permit: an approve() on its own leaves the wallet PENDING until you also ask Venly to verify it.

The permit flow (self-custody account wallet)

1

Get the message to sign

GET /accounts/{accountId}/wallets/{walletId}/permits returns, per asset, a supportedAssetId and an EIP-712 typedData object. walletId sets the chain (e.g. Base vs Avalanche). The nonce and the token’s domain name/version are read from the contract for you.
2

Sign it with the owner's key

Sign typedData with the key for the wallet’s owner address. The signature must recover to the owner — a signature from any other wallet makes the permit FAILED. Produces v, r, s.
3

Submit the signature

POST .../permits with the supportedAssetId and the signature. Returns HTTP 200 with result.status — check the status, not just the code. Re-submitting an already-confirmed permit returns 409.
4

Wait for CONFIRMED

Settlement is asynchronous — poll GET .../permits until CONFIRMED (or FAILED). On CONFIRMED the wallet activates and the allowance goes live.

Permit status lifecycle

Wallet status lifecycle

A wallet starts PENDING and becomes ACTIVE only once all its permits are CONFIRMED. Both activation routes end at the same place:
Payments and transfers require the wallet to be ACTIVE — until then they’re rejected with account-wallet-not-active.
VERIFYING_ALLOWANCE is easy to misread as “nearly ready”. It isn’t a usable state — don’t offer the wallet to an end-user until it reads ACTIVE.
The wallet’s status isn’t exposed by GET .../wallets (which shows amlStatus only). Use the permit status as your activation signal — once the account wallet’s permit is CONFIRMED, the wallet is ACTIVE — or read the status returned by allowance verification. The per-account permitStatus gives you the same answer per asset.

Contract wallets: activating from an allowance

A smart-contract wallet — a Safe, say — is controlled by its owners rather than a single key, so it can’t produce the one signature a permit needs. It grants the same permission the ordinary way instead, with an on-chain approve(), and then you ask Venly to check.
1

Grant the allowance on-chain

From the wallet itself, approve(orchestrationWallet, amount) for each asset you intend to use. Read the orchestration wallet address from GET .../allowances.Approve generously. The allowance is spent down as funds move, and when it runs out the wallet stops being usable until you approve again.
2

Ask Venly to verify

POST /accounts/{accountId}/wallets/{walletId}/allowance-verification — no request body. The allowance is the consent: only the wallet’s controller could have granted it, so no proof or signature is required.
3

Read the outcome

Every asset covered → the wallet is ACTIVE immediately.Some assets missing → VERIFYING_ALLOWANCE, with a verificationExpiresAt. Venly keeps re-checking until then, so granting the rest is enough; you don’t have to call again. Read assets[] to see exactly which are outstanding — allowanceSufficient is per asset.
If the window closes with assets still uncovered, the wallet reverts to PENDING and you start over. Grant every allowance you need before calling verification, rather than racing the clock afterwards.
Two things that catch people out:
  • This route never produces a permitTxId. There’s no permit transaction, so don’t wait for one.
  • It’s for self-custody account wallets only. A Venly-managed wallet activates through the permit flow automatically; calling verification on one is rejected.

Signing the message

Take the typedData from the permit response and sign it with the owner’s key. With ethers.js:
The signature must recover to the wallet owner. Signing with a different wallet (or signing the JSON as a personal message instead of EIP-712 typed data) produces a signature that the token contract rejects — the permit silently becomes FAILED.
Then submit it. Send v, r, and s as strings in the signature body — ethers returns v as a number, so convert it (String(v)). Use the supportedAssetId from the GET .../permits response:

Verifying the allowance

Before initiating a payment or transfer, confirm the allowance is in place:
Each entry returns the asset, the orchestrationWallet that holds the allowance, and the allowance as a human-readable decimal (on-chain raw value ÷ 10^decimals). The authoritative activation signal is the permit status: CONFIRMED (above) — not the allowance figure. An unlimited permit reports a very large allowance (uint256-max), but the value can also be finite; use this endpoint to read the current spending headroom and the permit status to confirm the wallet is active.
Once a permit is CONFIRMED, the allowance persists until it’s used up — the holder doesn’t sign again for every transfer.

Next steps

Get wallet allowances

Confirm the allowance is live before moving funds.

Create a crypto transfer

Move funds once the wallet is ACTIVE.