Appearance
Withdraw
A Withdraw (also called on-ramp — moving from fiat to crypto) lets a user spend EUR from their Holyheld card balance and receive tokens at a wallet address. Unlike a Deposit, the user does not sign a blockchain transaction in your application — they confirm the request inside the Holyheld mobile app, and BRRR submits the on-chain transfer on their behalf.
How a Withdraw works
- Your application requests a Withdraw: destination wallet, target token and network, EUR amount to spend.
- BRRR validates the destination address, locks a quote, and creates a request identified by a
requestUid. - The user receives a push notification from the Holyheld app and has 3 minutes to approve.
- On approval, BRRR debits the user's card balance, converts EUR to tokens at the quoted rate, and submits the transfer on-chain.
- Your application polls the request until it reaches a terminal state — approved with transaction hash, declined, or expired.
If the 3-minute window expires, the requestUid becomes invalid. To retry, your application must create a new request from scratch.
Choose your integration surface
| Surface | When to use | Auth |
|---|---|---|
| SDK | A user-facing app where the end user holds a Holyheld card and confirms in the mobile app | SDK API key |
| Card API | A platform building its own Withdraw UX on top of the same primitives | X-Api-Key |
The Multi-tenant Settlement and OTC APIs are Deposit-shaped surfaces; for Withdraws, the SDK and Card API are the integration surfaces today.
Withdraw with the SDK
The requestOnRamp method (which initiates a Withdraw) returns a requestUid immediately. Your application then waits for user confirmation via watchRequestId. EVM example:
typescript
import HolyheldSDK, { Network } from '@holyheld/sdk';
const sdk = new HolyheldSDK({ apiKey: process.env.HOLYHELD_SDK_API_KEY });
await sdk.init();
const settings = await sdk.getServerSettings();
if (!settings.external.isOnRampEnabled) throw new Error('Withdraws unavailable');
const wallet = await sdk.validateAddress('0x...');
if (!wallet.isOnRampAllowed) throw new Error('Address not eligible');
const request = await sdk.evm.onRamp.requestOnRamp({
walletAddress: '0x...',
tokenAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC on Ethereum
tokenNetwork: Network.ethereum,
EURAmount: '50',
});
// Prompt the user to open the Holyheld app and approve within 3 minutes.
const outcome = await sdk.evm.onRamp.watchRequestId(request.requestUid, {
timeout: 180_000,
waitForTransactionHash: true,
});
if (outcome.success) {
console.log('Tokens en route:', outcome.hash);
} else {
// Declined or window expired — start over from requestOnRamp.
}See the requestOnRamp and watchRequestId references for parameters and the full outcome shape. getOnRampEstimation lets you preview fees and the expected token output before submitting.
Withdraws are EVM-only at present. Solana support is on the SDK roadmap; check the Supported Networks page for the current matrix.
The 3-minute confirmation window
The mobile-app confirmation is intentional — it's how BRRR enforces that the cardholder authorizes the spend, even when the request originates from a third-party application. Three implications for your UX:
- After
requestOnRampresolves, surface a clear "open your Holyheld app" prompt with a visible countdown. - Don't pre-emptively retry. A second
requestOnRampwhile the first is pending will create a competing request. - If
watchRequestIdresolves withsuccess: false, the cause is either a user decline or window expiry. Both require a new request to retry.
Funding source and limits
Withdraws debit the user's Holyheld card EUR balance. If the balance is insufficient, requestOnRamp throws FailedOnRampRequest with a descriptive message — show it to the user and prompt them to top up their card before retrying. The current minimum and maximum per-request amounts are returned by getServerSettings() under minOnRampAmountInEUR and maxOnRampAmountInEUR.
Errors
Common Withdraw codes thrown via HolyheldSDKError include FailedOnRampRequest (the request couldn't be created — usually balance or eligibility) and FailedWatchOnRampRequestTimeout (network error during polling, distinct from a user decline). The full list lives in SDK error handling.
Where to next
requestOnRampandwatchRequestIdreferencegetOnRampEstimationvalidateAddress— eligibility checks before submitting- Webhooks — for server-side delivery of Withdraw outcomes
