Guides

Withdraw to a bank or a wallet

Withdraw USDC to an Icelandic bank account in your own name, or crypto to a wallet. Crypto withdrawals are confirmed from a link in your email.

Before you start

  • A key with withdrawals:create. Add transactions:read if the same key should list your other withdrawals and transactions; withdrawals:create alone can read withdrawals and your saved bank accounts.
  • Withdrawals switched on for the API: withdrawals_enabled in GET/v1/status.
  • Enough of the asset in your available balance. The amount you ask for includes the fee.

Bank withdrawals

Bank withdrawals are always from your USDC balance, to an Icelandic bank account in the account holder's name: we pay to your kennitala, so the bank refuses an account in someone else's name. There's nothing else to choose: an ordinary account is paid in krónur at Arion's rate, and an Arion foreign-currency account (03xx-38-…) in US dollars.

Your saved accounts

GET/v1/bank-accounts lists the accounts you have withdrawn to before, most recently used first:

# Signed by sign-headers.sh: see Authentication
curl https://public-api.myntkaup.is/v1/bank-accounts \
  $(./sign-headers.sh GET /v1/bank-accounts | sed 's/^/-H /')
import { Myntkaup } from "@myntkaup/api";

const client = new Myntkaup(); // reads the key from the environment
const { data: accounts } = await client.bankAccounts.list();
console.log(accounts.map((account) => account.account_number));
from myntkaup import Myntkaup

client = Myntkaup()  # reads the key from the environment
accounts = client.bank_accounts.list()["data"]
print([account["account_number"] for account in accounts])
Response · 200
{
  "data": [
    {
      "account_number": "0133-26-012345",
      "payout_currency": "ISK",
      "added_at": "2026-03-02T14:05:00Z",
      "last_used_at": "2026-10-28T09:12:00Z"
    }
  ]
}

Withdraw

POST/v1/withdrawals/bank takes the amount of USDC, with at most 2 decimals, and the bank account: a saved one, or a new one as 0000-00-000000 or 12 digits. The Idempotency-Key header is required.

# Signed by sign-headers.sh: see Authentication
body='{"amount":"250.00","bank_account":"0133-26-012345"}'
idempotency_key=$(uuidgen)   # reuse it to retry safely
curl https://public-api.myntkaup.is/v1/withdrawals/bank \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $idempotency_key" \
  $(./sign-headers.sh POST /v1/withdrawals/bank "$body" | sed 's/^/-H /') \
  --data "$body"
import { Myntkaup } from "@myntkaup/api";

const client = new Myntkaup(); // reads the key from the environment
// Sends a random Idempotency-Key unless you pass idempotencyKey.
const withdrawal = await client.withdrawals.createBank({
  amount: "250.00",
  bankAccount: "0133-26-012345",
});
console.log(withdrawal.id, withdrawal.status);
from myntkaup import Myntkaup

client = Myntkaup()  # reads the key from the environment
# Sends a random Idempotency-Key unless you pass idempotency_key.
withdrawal = client.withdrawals.create_bank(amount="250.00", bank_account="0133-26-012345")
print(withdrawal["id"], withdrawal["status"])
Response · 201
{
  "id": "wd_01JB8X2Q4M7Z3V9K5T1N6R0P2W",
  "type": "bank",
  "status": "pending",
  "asset": "USDC",
  "network": null,
  "amount": "250",
  "fee": "7",
  "address": null,
  "memo": null,
  "bank_account": "0133-26-012345",
  "tx_id": null,
  "source": "public_api",
  "created_at": "2026-11-03T10:15:10Z",
  "confirm_by": null,
  "completed_at": null
}
  • The amount goes on hold at once, and the withdrawal is pending until we pay it out, on a business day. It is then completed.
  • The minimum is 15 USDC and the fee is taken from the amount (fee in the answer). Below the minimum, or not more than the fee: 422 amount_below_minimum.
  • A foreign-currency account at a bank other than Arion: 400 invalid_request.
  • The account owner gets an email for every bank withdrawal made through the API.

Crypto withdrawals

A crypto withdrawal made through the API is a request. We email a link to the account's email address, and nothing is sent, or even held, until someone opens that link and confirms it, within 30 minutes. The email names the API key the request came from.

POST/v1/withdrawals/crypto takes:

  • asset and amount, fee included;
  • address, and memo for XRP (destination tag) and HBAR;
  • network, only for USDC: ethereum (the default, ERC-20) or solana;
  • travel_rule: who the address belongs to, as the app asks. The law requires it for every crypto withdrawal.
travel_ruleWhen
{ "address_owner": "self", "wallet_type": "self_hosted" }your own wallet, whose keys you hold
{ "address_owner": "self", "wallet_type": "hosted", "vasp": "kraken" }your own account at an exchange
{ "address_owner": "other", "wallet_type": "self_hosted", "beneficiary": { "type": "individual", "full_name": "Anna Jónsdóttir" } }someone else's wallet
{ "address_owner": "other", "wallet_type": "hosted", "vasp": "other", "vasp_name": "Example Exchange", "beneficiary": { "type": "company", "company_name": "Dæmi ehf.", "registration_number": "5501012340" } }a company's account at an exchange not in the list

vasp is one of binance, coinbase, kraken, crypto_com, bybit, okx, bitget, kucoin, gate_io, htx, mexc, bitfinex, gemini, bitstamp, upbit, bithumb, bitpanda, etoro, revolut or other. To another Myntkaup customer's deposit address, we fill the travel rule in from their account.

# Signed by sign-headers.sh: see Authentication
body='{"asset":"BTC","amount":"0.01","address":"bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh","travel_rule":{"address_owner":"self","wallet_type":"self_hosted"}}'
idempotency_key=$(uuidgen)   # reuse it to retry safely
curl https://public-api.myntkaup.is/v1/withdrawals/crypto \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $idempotency_key" \
  $(./sign-headers.sh POST /v1/withdrawals/crypto "$body" | sed 's/^/-H /') \
  --data "$body"
import { Myntkaup } from "@myntkaup/api";

const client = new Myntkaup(); // reads the key from the environment
// Nothing is sent until the link in the confirmation email is opened.
const withdrawal = await client.withdrawals.createCrypto({
  asset: "BTC",
  amount: "0.01",
  address: "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  travelRule: { addressOwner: "self", walletType: "self_hosted" },
});
console.log(withdrawal.status, withdrawal.confirm_by);
from myntkaup import Myntkaup

client = Myntkaup()  # reads the key from the environment
# Nothing is sent until the link in the confirmation email is opened.
withdrawal = client.withdrawals.create_crypto(
    asset="BTC",
    amount="0.01",
    address="bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
    travel_rule={"address_owner": "self", "wallet_type": "self_hosted"},
)
print(withdrawal["status"], withdrawal["confirm_by"])
Response · 202
{
  "id": "wd_01JB8X2Q4M7Z3V9K5T1N6R0P2W",
  "type": "crypto",
  "status": "awaiting_confirmation",
  "asset": "BTC",
  "network": "bitcoin",
  "amount": "0.01",
  "fee": "0.0005",
  "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "memo": null,
  "bank_account": null,
  "tx_id": null,
  "source": "public_api",
  "created_at": "2026-11-03T10:15:10Z",
  "confirm_by": "2026-11-03T10:45:10Z",
  "completed_at": null
}

The answer is 202 with status awaiting_confirmation and confirm_by, when the link stops working. Then:

  1. Open the email and confirm. The amount goes on hold and the withdrawal is pending while we check and send it.
  2. Once it's sent it is completed, with the blockchain transaction in tx_id.
  3. A link not used in time leaves the withdrawal expired, and nothing was held. Ask again if you still want it.

Refusals before anything is saved:

  • 422 asset_not_withdrawable: the asset, or that network, can't be withdrawn through the API;
  • 422 amount_below_minimum: below the asset's minimum, or not more than its fee;
  • 422 insufficient_funds: not enough available;
  • 400 invalid_request: an address that isn't valid for the asset, a missing memo, an incomplete travel_rule, or a Myntkaup deposit address of your own;
  • 403 account_restricted: crypto withdrawals are frozen on the account, or your profile is missing your address (finish it in the webapp);
  • 403 forbidden: withdrawals are switched off for the API.

Follow up

GET/v1/withdrawals lists bank and crypto withdrawals from every channel, newest first, filtered by type, status, asset, from and to:

# Signed by sign-headers.sh: see Authentication
curl "https://public-api.myntkaup.is/v1/withdrawals?type=bank&limit=10" \
  $(./sign-headers.sh GET "/v1/withdrawals?type=bank&limit=10" | sed 's/^/-H /')
import { Myntkaup } from "@myntkaup/api";

const client = new Myntkaup(); // reads the key from the environment
const page = await client.withdrawals.list({ type: "bank", limit: 10 });
console.log(page.data.map((withdrawal) => [withdrawal.id, withdrawal.status]));
from myntkaup import Myntkaup

client = Myntkaup()  # reads the key from the environment
page = client.withdrawals.list(type="bank", limit=10)
print([(withdrawal["id"], withdrawal["status"]) for withdrawal in page["data"]])

GET/v1/withdrawals/{id} returns one.

Security checklist

  • A key used only for withdrawals, with withdrawals:create and, as we recommend, a tight IP allowlist.
  • One Idempotency-Key per withdrawal, stored before sending and reused on retries.
  • Crypto addresses checked before they are sent: we can't reverse a withdrawal to the wrong address.
  • Every confirmation email read before you confirm it: it shows the amount, the address and the key.
  • The request_id of every withdrawal is logged.