Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.cryptomate.me/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks through the full lifecycle of a virtual card: from issuance and funding, through per-card configuration and spending controls, to fraud protection and transaction visibility. All operations are available both via the API (API key auth) and the CryptoMate Portal.

How it works

Each virtual card is a prepaid Visa card backed by stablecoin collateral. At the account level, a single holding wallet on the Polygon network holds all funds. When a purchase is made, the holding wallet balance is verified and the appropriate amount is debited.
Holding wallet (USDC, Polygon)
    └── Virtual Card A  ── purchase → authorization → settled
    └── Virtual Card B  ── purchase → authorization → settled
    └── Virtual Card C  ...
Cards are issued against a card pack. Each company has a maximum number of active cards based on their contracted product. You can check available capacity via Get contracted product.

Creating a card

Use Create virtual card to issue a new card. The two required decisions at creation time are:

Approval method

ValueHow purchases are approved
TOP_UPThe cardholder pre-loads funds onto the card’s individual deposit wallet. Purchases are auto-approved against that balance.
WEBHOOKEach purchase triggers a real-time webhook to your endpoint. You have 1,200 ms to respond with "00" / "SUCCESS" to approve or "05" to decline. Timeout = decline.
The approval_method cannot be changed after card creation. If the wrong method is set, delete the card and reissue.

Spending limits

Set daily_limit, weekly_limit, and monthly_limit (in USD) at creation. These are enforced per card independently. You can update them at any time — see Spending limits below. Omitting a limit means no limit is applied for that time window.

Funding the account

All cards draw from a shared holding wallet. Before cards can transact you must fund this wallet with USDC on Polygon. Retrieve the holding wallet deposit address via Get holding balance. The minimum deposit is 10 USD.
A minimum balance is always locked in the holding wallet to preserve capacity for at least one transaction. Withdrawals that would breach this floor are rejected.
To return funds to your treasury, use Withdraw from holding. For cards using TOP_UP approval, each card also has a personal deposit wallet. Fetch those addresses with Get card top-up wallets. Deposits sent to these wallets are attributed to that specific card and used to settle its purchases.

Managing cards

Spending limits

Update per-card daily, weekly, and monthly limits independently with Update card limits. Changes take effect immediately on the next authorization.

Freeze and unfreeze

Use Freeze or unfreeze card to temporarily block a card. A frozen card declines all purchase attempts until unfrozen. The card state moves between ACTIVE and FROZEN without losing its configuration. Useful for: temporary employee suspension, suspicious activity hold, cardholder request.

Unblocking after a velocity block

If a velocity rule triggers, the card is automatically moved to BLOCKED. Manually unblock it with Unblock card. Unblocking also resets the authorization counter so the card can transact immediately.

Reissuing a card

Reissue card generates a new card number and CVV while keeping the same cardholder configuration, limits, and approval method. Use this when a card number is compromised. The old card is deactivated.

Updating contact information

WhatEndpoint
Email for notificationsUpdate email
Phone for OTP / 3DS SMSUpdate phone
Card PINUpdate PIN

Rotating the deposit wallet

If a TOP_UP card’s deposit wallet address needs to be rotated (e.g. for compliance or security), use Rotate deposit wallet. A new address is generated and associated with the card. The old address stops accepting deposits.

Deleting a card

Delete card permanently deactivates the card. Any remaining balance in the card’s individual wallet is returned to the holding wallet. This action is irreversible.

3DS authentication

3DS adds cardholder verification to e-commerce purchases. Two modes are available per card:
ModeBehavior
smsA one-time code is sent to the cardholder’s registered phone.
webhookA webhook is sent to your endpoint with the OTP challenge. Your system delivers it via your own channel.
Configure the mode with Set 3DS configuration. When a 3DS challenge fires, a notification_3ds_authorization webhook is delivered to your endpoint. For webhook mode, use Respond to 3DS authorization to submit the OTP entered by the cardholder.

Fraud protection

Velocity rules

Velocity rules define rate limits at the account level, applied per individual card. They protect against card probing and rapid fraudulent use. Each rule specifies:
  • max_authorizations — the maximum number of approved authorizations allowed
  • time_window_seconds — the sliding window over which the count is measured
You can configure up to 5 rules. When any rule is breached, the transaction is declined and the card is automatically blocked. Manage rules with: Example ruleset — no more than 3 purchases per minute, or 20 per hour:
{
  "rules": [
    { "max_authorizations": 3, "time_window_seconds": 60 },
    { "max_authorizations": 20, "time_window_seconds": 3600 }
  ]
}
When a velocity block fires, a card_blocked_by_velocity webhook is sent to your endpoint.

Risk score engine

The risk score engine evaluates every purchase in real time. It assigns a numeric score by combining up to six signals, each weighted independently. If the score reaches or exceeds a configurable threshold, the transaction is declined.

How the score is calculated

Each signal is binary (0 or 1). The final score is the weighted sum:
score = (signal_geo × w_geo)
      + (signal_mcc × w_mcc)
      + (signal_amount × w_amount)
      + (signal_time × w_time)
      + (signal_declines × w_declines)
      + (signal_country × w_country)
A transaction is rejected when score ≥ threshold.

Signals

SignalIDFires (= 1) when…Notes
Impossible travelgeo_distanceCard used in a different country less than 4 hours after the previous transactionCompares merchant country of current vs. last approved transaction
Unfamiliar MCCmcc_profileMerchant category code was never seen in the card’s 90-day historyRequires ≥ 5 historical transactions to activate
Amount above baselineamount_baselinePurchase amount exceeds the 95th percentile of the card’s historical amountsRequires ≥ 5 historical transactions to activate
Unusual hourtime_windowTransaction hour appears in less than 5% of the card’s historical transactionsRequires ≥ 5 historical transactions to activate
High decline ratedecline_rate3 or more transactions were declined on this card in the last 24 hoursDetects rapid retry / card probing
Foreign merchant countrymerchant_countryMerchant’s country differs from the cardholder’s country (set via metadata.country at card creation)Signal is 0 if metadata.country is not set
Signals that require a minimum history return 0 (no contribution) until the card has enough approved transactions. A brand-new card is only evaluated by geo_distance, decline_rate, and merchant_country.

Configuration

The engine is configured at the account level from the Portal (Cards → Configuration → Risk Score). Each weight can be set to 0 to disable that signal entirely. Setting all weights to 0 disables the engine for the whole account.
FieldDescription
thresholdScore at or above which a transaction is rejected. Range: 0.0 – any positive value.
geo_distance_weightWeight for the impossible travel signal.
mcc_profile_weightWeight for the unfamiliar MCC signal.
amount_baseline_weightWeight for the above-P95 amount signal.
time_window_weightWeight for the unusual hour signal.
decline_rate_weightWeight for the high recent decline rate signal.
merchant_country_weightWeight for the foreign merchant country signal.

Examples

Conservative setup — only block clear anomalies, high threshold:
{
  "threshold": 0.8,
  "geo_distance_weight": 0.5,
  "mcc_profile_weight": 0.2,
  "amount_baseline_weight": 0.2,
  "time_window_weight": 0.1,
  "decline_rate_weight": 0.4,
  "merchant_country_weight": 0.0
}
In this configuration a transaction is rejected only if, for example, both impossible travel (0.5) and high decline rate (0.4) fire simultaneously (score = 0.9 ≥ 0.8). A single signal is not enough to reach the threshold. Strict setup — any single anomaly blocks the transaction:
{
  "threshold": 0.3,
  "geo_distance_weight": 1.0,
  "mcc_profile_weight": 0.5,
  "amount_baseline_weight": 0.5,
  "time_window_weight": 0.3,
  "decline_rate_weight": 1.0,
  "merchant_country_weight": 0.3
}
Here, impossible travel alone (1.0) exceeds the threshold. Even an unusual hour (0.3) is enough to reject. Disabled engine — all weights set to 0, no transaction is ever rejected by risk score:
{
  "threshold": 1.0,
  "geo_distance_weight": 0.0,
  "mcc_profile_weight": 0.0,
  "amount_baseline_weight": 0.0,
  "time_window_weight": 0.0,
  "decline_rate_weight": 0.0,
  "merchant_country_weight": 0.0
}

Fail-safe behavior

The engine is designed to fail open: if the transaction history cannot be fetched, or if the configuration cannot be loaded, the risk score check is skipped and the transaction proceeds normally. This ensures that a Redis or database hiccup never causes unexpected declines.

Risk score fuse

The risk score engine evaluates each purchase and may decline transactions that appear high-risk. When a trusted cardholder is incorrectly blocked (false positive), you can arm a one-shot fuse that bypasses risk evaluation for the next single purchase on that card. After that purchase fires, the fuse is automatically consumed and normal validation resumes.
The fuse bypasses only the risk score check. All other controls (spending limits, velocity rules, frozen status, holding balance) still apply.
OperationEndpoint
Arm the fuseArm risk score fuse
Disarm before it firesDisarm risk score fuse
Check fuse stateGet risk score fuse state

Transactions

Searching transactions

Search transactions returns the transaction history for your account with filtering by card, date range, status, and more. Get transaction returns the full detail of a single transaction by ID.

Accumulated spending

Get accumulated spending returns the total amount spent within a given period, useful for dashboards and limit management.

PDF statements

Export transactions PDF generates a downloadable statement for a date range.

Webhooks reference

Register your webhook URL and key via the Portal (Settings → Webhooks) or via Subscribe webhook URL. All card events arrive with "product": "cards" in the envelope.
EventWhen it fires
authorizationA purchase needs your approval (WEBHOOK mode). Respond within 1,200 ms with "00" / "SUCCESS" to approve or "05" to decline.
authorizedA purchase was approved (TOP_UP / NONE mode, or your webhook approved it).
clearedAn authorized purchase cleared and funds settled.
declinedA purchase was declined (by your webhook, by velocity rules, by risk score, or by insufficient balance).
reversalAn authorized purchase was reversed by the merchant or network.
refundA merchant refund was applied to the card.
depositFunds arrived at a card’s top-up wallet.
visa_direct_depositA Visa Direct deposit credited the card.
warranty_withdrawA withdrawal from the holding balance completed.
notification_3ds_authorizationA 3DS challenge was triggered. Deliver the OTP to the cardholder.
card_blocked_by_velocityA velocity rule triggered and blocked the card. Sent once per ACTIVE → BLOCKED transition.
See Webhooks for the full envelope format and authentication.

Quick reference