Preview First Deposit
Read-only dry-run for the card-open first deposit. It computes exactly what /card/apply would charge for a given first_deposit_amount — the fee on the excess, the amount credited to the card, and the total wallet freeze — without opening a card, writing anything, or freezing funds.
Use it to render a "what you'll pay" breakdown before the customer confirms, so the numbers match the real open 1:1.
What is the first deposit?
The card config defines a base initial deposit (base_amount) that is credited 1:1 with no fee. A merchant may top this up by sending a larger first_deposit_amount on apply; the part above the base (the excess) is charged a recharge-style fee, and the remainder is credited to the card 1:1 (USDT/USD, no FX). The final first deposit sent to the provider is always an integer.
The config may additionally set a minimum first deposit (min_first_deposit_amount, always ≥ base_amount): the total first_deposit_amount must be at least that value, while only the base part stays fee-free. Example: base_amount=10, min_first_deposit_amount=500 → the customer must deposit at least 500, of which 10 is fee-free and 490 is charged the recharge fee.
Endpoint
| Item | Value |
|---|---|
| Method | POST |
| Path | /api/v1/openapi/card/first_deposit/preview |
| Auth | HMAC |
| Idempotency | Not required (read-only) |
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
header_id | string | ✅ | hdr_<id> (List Card Headers) |
package_id | string | ✅ | pkg_<id> (List Card Configs) |
first_deposit_amount | string | optional | Total first deposit, non-negative integer, in the pay/recharge asset unit (e.g. USDT). Empty = use the config base. Must be >= min_first_deposit_amount (which is itself >= base_amount); when the minimum is above the base, an empty value is rejected with first_recharge_below_min. Scientific notation, decimals, and signs are rejected. |
Example Request
{
"header_id": "hdr_119",
"package_id": "pkg_33",
"first_deposit_amount": "16"
}Response Fields
Always 200 (graceful)
This endpoint returns 200 even when the amount is not submittable. Read is_valid to decide whether apply would succeed, and invalid_reason for the stable reason code. Only malformed IDs, an unsupported card type, or a server fault return non-200.
| Field | Type | Description |
|---|---|---|
package_id | string | Echo, pkg_<id> |
card_type | string | Lowercase business code, e.g. virtual_v |
currency | string | Card currency (e.g. USD) |
pay_asset_symbol | string | Pay/recharge asset symbol (e.g. USDT) |
open_card_fee | string (decimal) | Open-card fee (pay asset) |
base_amount | string (decimal) | Base initial deposit — credited 1:1, no fee |
min_first_deposit_amount | string (decimal) | Effective minimum total first deposit (max(base_amount, package/header minimum)), base included. first_deposit_amount must be ≥ this; when it is above base_amount the empty default (base) is rejected |
request_amount | string (decimal) | The total first deposit being evaluated |
excess_amount | string (decimal) | Excess above base (request_amount − base_amount) |
excess_fee_amount | string (decimal) | Fee charged on the excess (fee asset) |
excess_fee_asset_symbol | string | Fee asset symbol (omitted when no excess) |
excess_settle_amount | string (decimal) | Excess after fee, before integer rounding |
excess_card_amount | string (decimal) | Excess actually credited to the card (after rounding) |
exchange_rate | string | Compatibility field; USDT/USD is 1:1, always empty |
first_recharge_card | string (decimal) | Total credited to the card (base + excess credited); integer |
total_freeze | string (decimal) | Total wallet freeze in the pay asset (open_card_fee + amount credited + excess fee + deposit when deposit_in_total_freeze=true); rounding remainder is not charged |
deposit_required | bool | Whether this application will freeze a card-open deposit (same gate decision as /card/apply: mode 2 always; mode 3 only when KYC is not yet passed; mode 4 after KYC passes) |
deposit_amount | string (decimal) | Deposit amount in the deposit asset; "0" when no deposit |
deposit_asset_symbol | string | Deposit asset symbol (e.g. USDT / USD); empty when no deposit |
deposit_refund_days | int | Days after card closure before the deposit refund can be requested (0 = enters refund review right after closure) |
deposit_in_total_freeze | bool | true = deposit asset equals the pay asset and the deposit is already included in total_freeze; false = not included (no deposit, or a different asset — show it separately) |
deposit_wallet_balance | string (decimal) | Available balance of the deposit asset; "0" when no deposit |
deposit_wallet_balance_sufficient | bool | Whether the deposit freeze would succeed, simulated in the real apply order (open fee + first deposit first, then deposit; USDT topped up 1:1 from USD); always true when no deposit |
fee_type | int | Recharge fee type: 1=fixed 2=percent 3=mixed |
fee_rate | string | Rate (percent / mixed); omitted otherwise |
fee_fixed | string | Fixed fee (fixed / mixed); omitted otherwise |
min_recharge_amount | string (decimal) | Config min recharge (display) |
max_recharge_amount | string (decimal) | Config max recharge — the excess is capped by this |
wallet_balance | string (decimal) | Your account's available balance in the pay asset |
wallet_balance_sufficient | bool | Whether the balance (incl. USD 1:1 top-up) covers total_freeze |
usd_balance | string (decimal) | Your USD balance (used only for the USDT→USD 1:1 top-up check) |
will_use_usd | bool | Whether the USD 1:1 top-up would be used |
usd_needed | string (decimal) | USD amount that would be drawn via the top-up |
is_valid | bool | Whether apply would be accepted with this amount |
invalid_reason | string | Stable reason code when is_valid=false (omitted when valid) |
warnings | string[] | Optional advisory codes |
Example Response (valid)
{
"code": 200,
"message": "OK",
"data": {
"package_id": "pkg_33",
"card_type": "virtual_v",
"currency": "USD",
"pay_asset_symbol": "USDT",
"open_card_fee": "1.000000000000000000",
"base_amount": "10",
"min_first_deposit_amount": "10",
"request_amount": "16",
"excess_amount": "6",
"excess_fee_amount": "1.22",
"excess_fee_asset_symbol": "USDT",
"excess_settle_amount": "4.78",
"excess_card_amount": "4",
"first_recharge_card": "14",
"total_freeze": "16.22",
"deposit_required": false,
"deposit_amount": "0",
"deposit_asset_symbol": "",
"deposit_refund_days": 0,
"deposit_in_total_freeze": false,
"deposit_wallet_balance": "0",
"deposit_wallet_balance_sufficient": true,
"fee_type": 3,
"fee_rate": "0.020000",
"fee_fixed": "1.100000000000000000",
"min_recharge_amount": "10.000000000000000000",
"max_recharge_amount": "100.000000000000000000",
"wallet_balance": "70.82999088",
"wallet_balance_sufficient": true,
"usd_balance": "0",
"will_use_usd": false,
"usd_needed": "0",
"is_valid": true
}
}In this example a 16 first deposit on a base=10 config keeps 10 free (1:1), and the 6 excess is charged a 1.22 fee, leaving 4 credited — so the card receives 14 and the wallet is frozen 16.22.
Example Response (not submittable)
{
"code": 200,
"message": "OK",
"data": {
"package_id": "pkg_33",
"card_type": "virtual_v",
"is_valid": false,
"invalid_reason": "first_recharge_exceeds_max",
"base_amount": "10",
"min_first_deposit_amount": "10",
"request_amount": "9999",
"max_recharge_amount": "100.000000000000000000"
}
}invalid_reason Codes
is_valid=false is paired with one of these stable codes (same keys returned as message_key by apply):
| invalid_reason | Meaning |
|---|---|
invalid_first_deposit_amount | Not a plain non-negative integer (decimals / signs / scientific notation / too many digits) |
first_recharge_below_base | Amount below base_amount |
first_recharge_below_min | Amount below min_first_deposit_amount (also returned when first_deposit_amount is omitted and the minimum is above the base) |
first_recharge_exceeds_max | Excess exceeds max_recharge_amount |
first_recharge_limit_exceeded | Excess exceeds the account's recharge limit |
first_recharge_asset_mismatch | Open-fee asset ≠ recharge asset; custom excess unsupported for this config |
first_recharge_excess_too_small | After fee + integer rounding nothing reaches the card; increase the amount |
insufficient_balance | Wallet (incl. USD top-up) can't cover total_freeze |
kyc_required | KYC is a hard gate for this card and the account has not passed it (same rejection /card/apply would return) |
card_deposit_config_invalid | The gate mode requires a deposit but the deposit is not fully configured on the platform side; nobody can open this card until it is fixed |
card_deposit_insufficient_balance | Balance of the deposit asset can't cover deposit_amount (only when the deposit asset differs from the pay asset) |
bank_card_config_not_found | Package inactive / not found |
bank_card_header_not_found | Header inactive / not found |
Common Errors
Non-200 responses (malformed input / unsupported type / server fault):
| HTTP | message_key | Description |
|---|---|---|
| 400 | openapi_invalid_header_id | header_id missing / wrong prefix / not found |
| 400 | openapi_invalid_package_id | package_id missing / wrong prefix |
| 400 | openapi_card_type_not_supported | Header's card type not in the virtual range |
| 400 | account_manager_bind_required / account_manager_unavailable / account_manager_open_disabled | Account-manager-controlled header your account cannot apply for (same rule as apply) |
| 401 | openapi_invalid_credentials | Auth failure |
| 500 | openapi_first_deposit_preview_failed | Server error |
Notes
- Numbers match apply exactly. The preview shares the open-card computation, so
total_freeze/first_recharge_cardare the same values apply will charge for the samefirst_deposit_amount. - No money moves. This call freezes nothing and writes nothing; safe to call as often as needed (subject to rate limits).
wallet_balanceis your own account balance — no other party's data is exposed.- The excess fee uses the same recharge fee formula as Create Recharge; the formula is also available per package via List Card Configs.