Connect your wallet or exchange to the Colombian peso. Your users convert USDC ⇄ COP with PSE pay-ins and Bre-B pay-outs — VANK handles compliance, KYC and settlement.
An anchor is the bridge between the Stellar network and local money. The VANK Anchor connects USDC (the digital dollar issued by Circle) with the Colombian peso, using the standard Stellar ecosystem protocols: SEP-10 for authentication and SEP-24 for interactive deposits and withdrawals.
Your user pays pesos via PSE from their bank and receives USDC in their Stellar account. VANK collects the fiat, runs compliance and delivers the USDC on-chain.
Your user sends USDC to the anchor and instantly receives pesos at their Bre-B key — Colombia's instant payment system. The quote is shown before confirming, with no hidden spread.
Your integration speaks standard SEP-10 + SEP-24: if your wallet already connects to other ecosystem anchors (MoneyGram Ramps, for instance), connecting to VANK is the same code pointing at a different domain.
Start on testnet — no permission needed
The sandbox is open: point your integration at dev-stable-anchor.thisisvank.com and start today. Writing to us comes later, to move to production.
Set up your testnet wallet
A Stellar testnet account with a trustline to test USDC. The issuer is in the test environment's stellar.toml.
Implement SEP-10 and pick your path
Authentication first (SEP-10). Then you pick one of two: with SEP-24 you open our form and we guide the user, which is a few lines with the Stellar Wallet SDK. With SEP-6 everything goes over the API and you own the screen. Code for both is below, and SEP-6 has its own section.
Certify on testnet
Three cases you run yourself: a deposit, a withdrawal and a refunded withdrawal. Section 7 has what each one proves and what we check.
Approval
VANK reviews your certification and use case, and coordinates the integration agreement with you.
Production
You provide your app's final domain and move to mainnet: anchor.thisisvank.com, with Circle's USDC.
Everything starts at the environment's stellar.toml: it holds EVERY endpoint you will use, the key the anchor signs with and the supported asset. Do not hardcode any of these values — read them from the toml, which is the source of truth and differs between environments.
| toml field | Protocol | What you need it for |
|---|---|---|
WEB_AUTH_ENDPOINT | SEP-10 | Authenticate. It is the first step of everything else. |
TRANSFER_SERVER | SEP-6 | Deposit and withdrawal over the API, without our form. It is the endpoint of section 5. This field also tells you whether SEP-6 is enabled in that environment: if the toml publishes it, it is. |
TRANSFER_SERVER_SEP0024 | SEP-24 | Deposit and withdrawal by opening our form. |
KYC_SERVER | SEP-12 | User identity. And on a SEP-6 withdrawal, this is where the destination Bre-B key travels. |
ANCHOR_QUOTE_SERVER | SEP-38 | Firm quotes, to guarantee the rate for your user. |
SIGNING_KEY | SEP-10 | The public key the anchor signs the challenge with. Verify it. |
CURRENCIES | SEP-1 | The supported USDC and its ISSUER, which differs between testnet and production. |
| Sandbox (testnet) | Production (mainnet) | |
|---|---|---|
| Anchor domain | dev-stable-anchor.thisisvank.com | anchor.thisisvank.com |
| stellar.toml | /.well-known/stellar.toml | /.well-known/stellar.toml |
| Network | Stellar testnet | Stellar mainnet |
| USDC issuer | test issuer — read it from the toml | Circle |
# Read the environment's stellar.toml — it is the source of truth curl https://dev-stable-anchor.thisisvank.com/.well-known/stellar.toml # production: anchor.thisisvank.com
SEP-10 proves you control the Stellar account: you request a challenge, sign it and exchange it for a JWT that authorizes the remaining calls. Watch this part, it is the one most often missed: the challenge is signed with TWO keys. The user account's key, which says who operates, and YOUR DOMAIN's key, which says which app originates it. The second one is an access requirement and is explained below.
# Without the SDK, SEP-10 is two calls (any language):
GET https://dev-stable-anchor.thisisvank.com/auth?account=G... # → { transaction, network_passphrase }
# Verify the challenge BEFORE signing: read_challenge_transaction(xdr, toml SIGNING_KEY, passphrase, home_domain, web_auth_domain) in your language's SDK
# Sign it with the account key (and your domain key if you sent client_domain)
POST https://dev-stable-anchor.thisisvank.com/auth {"transaction": "<xdr firmado>"} # → { token } (JWT, 24 h)yarn add @stellar/typescript-wallet-sdk
import { Wallet, SigningKeypair, DomainSigner } from "@stellar/typescript-wallet-sdk";
const HOME_DOMAIN = "dev-stable-anchor.thisisvank.com"; // sandbox · production: anchor.thisisvank.com
const CLIENT_DOMAIN = "tu-dominio.com"; // yours, the one publishing your SIGNING_KEY
async function authenticate(authSecretKey: string) {
const wallet = Wallet.TestNet(); // production: Wallet.MainNet()
const anchor = wallet.anchor({ homeDomain: HOME_DOMAIN });
const sep10 = await anchor.sep10();
const authKey = SigningKeypair.fromSecret(authSecretKey);
// Your backend co-signs the challenge with your domain key.
// The SECRET key never leaves it, and never reaches the browser.
const walletSigner = new DomainSigner("https://" + CLIENT_DOMAIN + "/sign", {});
return await sep10.authenticate({
accountKp: authKey,
walletSigner,
clientDomain: CLIENT_DOMAIN,
});
}The /sign endpoint is yours and lives on your backend. It receives the challenge, signs it with your domain's secret key and returns it. That secret key never leaves your server and never reaches the browser — which is why the backend signs and not the client:
// On YOUR backend. It receives the challenge, signs it with your domain key and returns it.
// POST /sign → { transaction, network_passphrase }
import { Transaction, Keypair } from "@stellar/stellar-sdk";
app.post("/sign", (req, res) => {
const { transaction, network_passphrase } = req.body;
const tx = new Transaction(transaction, network_passphrase);
tx.sign(Keypair.fromSecret(process.env.CLIENT_DOMAIN_SECRET!)); // your SIGNING_KEY's secret
res.json({ transaction: tx.toXDR(), network_passphrase });
});The file is minimal — this is ALL it needs to contain:
# https://your-domain/.well-known/stellar.toml VERSION = "2.7.0" SIGNING_KEY = "G...YOUR_APPS_PUBLIC_KEY"
The call returns a URL and an id. You open the URL in a webview and there the user sees the full breakdown before confirming: the final rate, the commission and VAT SEPARATELY, the net total they will receive, and a countdown with the real time left on that rate. Nothing is buried inside the price. The id is your handle to poll the status.
// Withdrawal (off-ramp): the user sends USDC and receives COP
const { url, id } = await anchor.sep24().withdraw({
authToken,
withdrawalAccount: USER_STELLAR_PUBLIC_KEY,
assetCode: "USDC",
lang: "en",
});
// Open `url` in a webview: VANK's UI guides the rest// Deposit (on-ramp): the user pays COP (PSE) and receives USDC
const { url, id } = await anchor.sep24().deposit({
authToken,
destinationAccount: USER_STELLAR_PUBLIC_KEY,
assetCode: "USDC",
lang: "en",
});Track the status with the SDK watcher (recommended) or the raw endpoint. There are no webhooks to your URL: the on_change_callback parameter is accepted but never fires, so poll. A 403 only says forbidden: the JWT is missing, invalid or expired (it lasts 24 h); redo SEP-10. The lifecycle is standard SEP-24:
const watcher = anchor.sep24().watcher();
const { stop } = watcher.watchOneTransaction({
authToken,
assetCode: "USDC",
id: transactionId,
onMessage: (tx) => {
if (tx.status === "pending_user_transfer_start") {
// Withdrawal: send the USDC now. Deposit: wait for the user's payment
}
},
onSuccess: (tx) => { /* completed */ },
onError: (tx) => { /* error */ },
});# Without the SDK: start and poll (sandbox; production: anchor.thisisvank.com)
curl -X POST "https://dev-stable-anchor.thisisvank.com/sep24/transactions/deposit/interactive" \
-H "Authorization: Bearer $SEP10_JWT" -H "Content-Type: application/json" \
-d '{"asset_code":"USDC","account":"G...","lang":"es"}' # or /withdraw/interactive → { url, id }
curl "https://dev-stable-anchor.thisisvank.com/sep24/transaction?id=$TRANSACTION_ID" \
-H "Authorization: Bearer $SEP10_JWT"| Status | What it means | Your action |
|---|---|---|
incomplete | The form was opened and not yet submitted. | Nothing. It does NOT expire on its own: it stays there indefinitely. If your user abandoned it, start another — and to resume it you must request the link again. |
pending_user_transfer_start | Withdrawal: the anchor awaits your USDC. Deposit: awaiting the user's PSE payment. | Withdrawal: send the USDC to the account and memo the transaction provides. Deposit: nothing. |
pending_anchor | VANK received the funds and is processing. | Nothing — keep watching. |
pending_external | Withdrawal: the pesos are on their way via Bre-B. | Nothing — usually seconds. If the provider fails, the transaction moves to error and the refund goes out on its own. |
completed | Delivered: COP at the user's account or USDC in their wallet. | Show the receipt (more_info_url). |
error | The operation could not be completed. | Show the reason; if USDC was received, the refund is automatic. |
If a withdrawal cannot be completed after the USDC is received, the refund to the source account is automatic, with no human intervention, and for the full amount: the service was not delivered, so no fee is deducted.
Every operation goes through identity verification and beneficiary screening (restricted lists, PEP, OFAC) before money moves. The control is fail-closed: if screening cannot run, the operation does not proceed.
Everything above uses SEP-24: your app opens our form and we guide the user. With SEP-6 your app requests, asks and confirms over the API, and you own the screen. The WITHDRAWAL is entirely programmatic: nothing is ever opened. The DEPOSIT ends in a link, and not by our choice: the peso payment goes through PSE, where the person picks their bank, so no payment URL exists before that choice. Below is exactly where that link comes from.
npm install @stellar/stellar-sdk # sign SEP-10 and the on-chain payment # Nothing else: the rest of SEP-6 is plain HTTP (fetch).
Authenticate first with SEP-10, same as step 3 of this guide. The rest is plain HTTP:
const ANCHOR = "https://dev-stable-anchor.thisisvank.com"; // SEP-6 lives TODAY in the sandbox
const auth = { Authorization: `Bearer ${authToken}` }; // SEP-10 JWT (step 3)
// 1) Request the withdrawal. Returns an id; you cannot send the USDC yet.
const { id } = await fetch(
`${ANCHOR}/sep6/withdraw?asset_code=USDC&account=${account}&amount=2&type=breb`,
{ headers: auth },
).then((r) => r.json());
// 2) Poll the transaction and REACT to its status.
const tx = async () =>
(await fetch(`${ANCHOR}/sep6/transaction?id=${id}`, { headers: auth }).then((r) => r.json()))
.transaction;
// Right after creation it is incomplete: wait for the engine's FIRST cycle (up to ~30s) before deciding anything. Ask only once now and you skip the next step.
let t = await tx();
while (t.status === "incomplete") { await sleep(5000); t = await tx(); }
if (t.status === "pending_customer_info_update") {
// 3) Data is missing. ASK which — never guess.
const need = await fetch(
`${ANCHOR}/sep12/customer?account=${account}&transaction_id=${id}`,
{ headers: auth },
).then((r) => r.json());
console.log(need.fields); // each field carries a description for your user
// 4) Send them BOUND to this transaction. bank_account_number = the Bre-B key.
await fetch(`${ANCHOR}/sep12/customer`, {
method: "PUT",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({
account,
transaction_id: id,
type: "sep6-withdrawal", // withdrawal context: this is how SEP-12 knows the key is required
// YOUR USER, the one withdrawing. Same as what they type into our form in SEP-24.
first_name: "Ana", last_name: "Pérez",
email_address: "ana@example.com",
id_type: "CC", id_number: "1000000001",
// THE BENEFICIARY: only their key. You do NOT send their name or document — we resolve them against the Bre-B directory.
bank_account_number: "@llaveDeTuUsuario",
}),
});
}
// 5) POLL — do not expect an immediate answer. After the PUT, the transaction
// can take up to ~30s to advance: our engine works in cycles.
while (["incomplete", "pending_customer_info_update"].includes((t = await tx()).status)) await sleep(5000);
// Once it reaches pending_user_transfer_start you have the destination and memo.
// t.withdraw_anchor_account · t.withdraw_memo (memo_type "id") · t.amount_out = net COP
// t.fee_details.total comes in USDC: it is the same fee the quote gave you in COP, in another unit
// t.message = the resolved beneficiary: show it to the user BEFORE signing
// 6) Send the USDC to that account with THAT memo, then keep polling until completed.| Status | What it means and what you do |
|---|---|
incomplete | WITHDRAWAL: just created; we are calculating and verifying, it moves on the next cycle, up to ~30s. DEPOSIT: it STAYS here, and that is correct — we are waiting for your user to pay through the link. Do not expect it to move on its own. |
pending_customer_info_update | We need customer data. Query GET /sep12/customer with this transaction_id, show the fields to your user and send them with PUT. The transaction's message field tells you what is missing. |
pending_user_transfer_start | All set. Send the USDC to withdraw_anchor_account with withdraw_memo. Before signing, show the beneficiary that comes in message. |
pending_anchor · pending_external | We received your USDC and the peso payout is under way. Just wait. |
completed | The beneficiary received the pesos. |
error | It could not continue and message says why. If you had already sent the USDC, the refund is automatic and for the full amount. |
There are two ways to start a deposit, and they do not lock the same things:
| Endpoint | amount is in | What gets locked | When to use it |
|---|---|---|---|
GET /sep6/deposit | USDC | Nothing. It is an intention: the form opens with the amount editable, your user types the pesos they will pay and it settles at the current rate. | When you do not need to promise your user a number. Careful: amount=5000 here is five thousand dollars. |
GET /sep6/deposit-exchange + quote_id | COP | Amount and rate. The form opens with both locked, and what was promised is what arrives. | When you guarantee the rate to your user. It is the one in the code below. |
Both return the same thing, and it is what confuses most: only an id and a fixed sentence telling you to check the transaction. The link is not there. It lives in the transaction, in the more_info_url field, already signed. You open it in a webview, your user picks their bank and pays through PSE, and once the payment is confirmed the USDC lands in their Stellar account and the transaction moves to completed. Recommended minimum: 2,000 COP, because the deposit fee is 1,785 and it is deducted before converting.
// 1) Firm quote (optional, but it is what guarantees the rate for your user)
// USDC_ISSUER: the issuer you read from the environment's stellar.toml (section 3). Never hardcoded.
const q = await fetch(`${ANCHOR}/sep38/quote`, {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({
sell_asset: "iso4217:COP", buy_asset: `stellar:USDC:${USDC_ISSUER}`,
sell_amount: "5000", sell_delivery_method: "bank_transfer",
country_code: "CO", context: "sep6",
}),
}).then((r) => r.json());
// q.price · q.buy_amount · q.fee.total ← the fee comes SEPARATELY, not inside the price
// 2) Start the deposit with that quote.
const { id } = await fetch(
`${ANCHOR}/sep6/deposit-exchange?amount=5000&destination_asset=USDC` +
`&source_asset=iso4217:COP"e_id=${q.id}&account=${account}&type=bank_transfer`,
{ headers: auth },
).then((r) => r.json());
// CAREFUL: this response does NOT carry the link. Only { how, id }.
// 3) The link lives in the TRANSACTION.
const t = await fetch(`${ANCHOR}/sep6/transaction?id=${id}`, { headers: auth })
.then((r) => r.json()).then((r) => r.transaction);
// t.more_info_url → open it in a webview. Its token lives 10 min; if it expires, re-read and you get a new one.
// t.user_action_required_by → how long your user has. TODAY it comes null: use the quote's expires_at
// 4) Your user picks a bank and pays through PSE. Keep polling until completed.Your app opened the more_info_url in a webview and keeps polling GET /sep6/transaction. This is what your user does at each moment and the status your polling will see meanwhile.
| Moment | Status your polling sees | What is happening |
|---|---|---|
| Opens the link | incomplete | Sees the locked amount, the frozen rate, commission and VAT separately and the net USDC, with the real countdown. Fills in their details, authorizes KYC (it runs on the ID number they type), picks their bank and presses Continue. |
| Submitted the form | pending_user_transfer_start | We created the order and the quote is consumed: the 10-minute window ends here. We send them to their bank's portal (PSE). That payment may take as long as it takes. |
| The bank confirmed | pending_anchor | We received the pesos and are sending the USDC to their Stellar account. |
| Done | completed | stellar_transaction_id carries the hash and message says “USDC enviado on-chain”. Your user sees “Pago confirmado” with that same hash. Show it to them. |
| Abandoned it | incomplete | It stays there, it does not expire on its own. If they want to pay later, start another deposit: the link and the quote have expired. |
Every SEP-6 operation comes in two versions: the plain one and the -exchange one. The difference is one single thing, whether the rate is locked before money moves or not:
| Without a quote | With a firm quote | |
|---|---|---|
| Endpoints | /sep6/deposit · /sep6/withdraw | First POST /sep38/quote, which gives you a quote_id. Then /sep6/deposit-exchange or /sep6/withdraw-exchange with that quote_id. |
| The rate | The one at settlement time. It can move between requesting the operation and completing it. | The quote's, frozen for that transaction. Single use, it does not work for another one. |
| What you can promise your user | Nothing exact. They see the number at the end. | The exact number before they confirm: the quote carries the price and the fee separately, and what was promised is what arrives. |
| When to use it | When your app does not show an amount before operating. | When your app tells them “you will receive X” before they pay or send. It is what any serious app does. |
| How long it lasts | Not applicable. | 10 minutes on deposits, because the person opens the link, verifies and picks a bank. 5 minutes on withdrawals, which are programmatic. |
What has to happen within the window: on a deposit, your user submits the form; the PSE payment may take longer. On a withdrawal, you send the key and the data via SEP-12; our engine consumes the quote when it creates the order.
With the test data in section 7, against the sandbox. Two things worth checking there, because they are the ones that surprise in production: make TWO withdrawals in a row and confirm we ask for the key again on the second, and make one of 1.11 USDC to see the automatic refund. If you test the DEPOSIT with the SDF Demo Wallet, note that this tool receives the more_info_url and does not show it to you: it only logs the transaction status. Take the POST /auth token and the deposit id from its logs, call GET /sep6/transaction?id=… with that token, and the link is right there. Once you pay, the Demo Wallet picks up the completed status and closes on its own.
# SEP-6 · deposit — does NOT need SEP-12: identity is verified in the more_info_url form
GET /sep6/deposit?asset_code=USDC&account=G...&amount=2&type=bank_transfer
# → pay through the transaction's more_info_url (same form, same test PSE)
# SEP-6 · withdrawal — DOES need SEP-12: PUT /sep12/customer (Authorization: Bearer <SEP-10 JWT>)
{
"account": "G...YOUR_TESTNET_ACCOUNT",
"type": "sep6-withdrawal",
"first_name": "Tester", # who withdraws: your user
"last_name": "Sandbox",
"email_address": "tester@example.com",
"id_type": "CC",
"id_number": "1000000001",
"bank_account_number": "@alphamunKey01" # the beneficiary: only their Bre-B key
}
GET /sep6/withdraw?asset_code=USDC&account=G...&amount=1&type=breb # amount=1.11 → refundedIf your product is not a Stellar wallet, VANK can also create and custody a Stellar account for each of your users: you reference it with your own identifier and never touch private keys — VANK manages them securely. It is the fast path for fintechs that want to offer USDC without running crypto infrastructure.
Access to the custodial integration follows the same request and certification process in this guide — write to us and we will share the specific technical documentation.
Certification is not paperwork you wait on: you run it yourself, today, in the open sandbox. Three cases, each proving your integration handles a different moment well. Here is what to run and, above all, what we will look at — so you know whether you will pass before sending it.
| Case | What it proves | What we check |
|---|---|---|
| 1 · A deposit | That you take the user to pay and credit the USDC to their account. | That the USDC reached the account you declared, and that your app showed the peso amount before the person paid. |
| 2 · A withdrawal | That you ask for the destination, send the USDC to the right place and follow the operation to the end. | That you showed the user the resolved beneficiary BEFORE signing, and that the net amount you displayed matches what we paid. |
| 3 · A refunded withdrawal | That you handle the case where the payout fails. Withdraw exactly 1.11 USDC: the sandbox rejects it on purpose. | That you reacted to the error status, explained it to the user with our message, and reflected the returned USDC instead of writing it off. |
https://tu-dominio/.well-known/stellar.toml with your app's SIGNING_KEY, and your SEP-10 authentication must send that clientDomain (co-signing the challenge — the Wallet SDK supports it). It is the ecosystem standard and it is what lets us attribute your transactions to you.Everything you need to complete certification without waiting for us. This data is fictitious and shared: there is no real person or money behind it, and you must not use real documents in the sandbox.
| What | Value | Note |
|---|---|---|
| Environment | dev-stable-anchor.thisisvank.com | Stellar testnet. Use your own testnet wallet (or the SDF Demo Wallet). |
| Testnet funds | XLM: Friendbot · USDC: trustline to the issuer in the stellar.toml, then request it at https://faucet.circle.com (Stellar testnet) | Trustline first, then Circle's faucet: without the trustline the USDC cannot land. Your own test deposit also leaves you USDC. |
| KYC identity | Tester Sandbox · CC 1000000001 | Fictitious identity, verified instantly. Email and phone: anything. |
| Deposit (test PSE) | ≥ 5.000 COP · bank BANCO UNION COLOMBIANO · the min_amount in /info is in USDC | With less, the sandbox's fixed fee eats the amount. Follow the test-PSE steps below. |
| Withdrawal (test Bre-B) | key @alphamunKey01 · ≥ 1 USDC | Test holder in the sandbox's Bre-B directory. We ask for it on EVERY withdrawal: the destination is never inherited from the previous one. |
| Refunded withdrawal | same key · exactly 1.11 USDC | The sandbox rejects that amount on purpose: you will see the transaction in error and the USDC back in your account, automatically. |
| Via API (SEP-6 / SEP-12) | first_name=Tester last_name=Sandbox id_type=CC id_number=1000000001 | For the withdrawal, the Bre-B key goes in the field bank_account_number. |
Deposit form, field by field (every field marked * is required; type exactly this):
| Field | What to type | Note |
|---|---|---|
| Amount to deposit * | 5000 | In COP. Any less and the sandbox's fixed fee eats it. |
| Full name * | Tester Sandbox | |
| Document type * | CC · Cédula de Ciudadanía | It is the first option in the list. |
| Document number * | 1000000001 | Digits only, no dots. |
| Email * | tester@example.com | Anything in email format. |
| Phone * | 3001234567 | Any number. |
| I authorize the identity verification (KYC) * | tick the box | Within seconds you see “Identity verified · KYC complete”. |
| Payment method * | PSE · bank BANCO UNION COLOMBIANO | Exactly that one: the list also has “BANCO UNION” and “BANCO UNION COLOMBIANO FD2”, which do not work for the test. |
| Pay | press the button | Takes you to the test-PSE screen (steps below). |
Test-PSE screen (the gateway's sandbox asks you to confirm the payment manually):
bankProcessDate: the same date the screen shows · transactionState: OK · authorizationID: 12.Call Return: SUCCESS - TransactionState: OK.Withdrawal form, field by field
| Field | What to type | Note |
|---|---|---|
| Amount to withdraw * | 1 USDC — or 1.11 for the refunded withdrawal | You see the rate and the fee before confirming. |
| Your full name (the person withdrawing) * | Tester Sandbox | This is the identity of the person withdrawing, not the beneficiary's: the key below identifies them. With an external wallet the form says it this way; from the VANK panel you will see it as “Account holder full name”. |
| Document type * | CC · Cédula de Ciudadanía | It is the first option in the list. |
| Document number * | 1000000001 | Digits only, no dots. It is the test identity with verification prepared in the sandbox. |
| Email * | tester@example.com | Anything in email format. |
| Phone * | 3001234567 | Any number. |
| I authorize the identity verification (KYC) * | tick the box | It runs on the ID number you typed. It is the first check; the second is on the beneficiary, and the key handles it. |
| How you want to receive your money * | Bre-B key | Bank account shows as “coming soon”: do not use it. |
| Beneficiary's Bre-B key * | @alphamunKey01 | Only the key. The form resolves the test beneficiary and screens them by itself: their name and document are NOT typed. The fields above belong to the person withdrawing, not to them. |
| Confirm | press the button | Send the USDC to the account and memo shown (your wallet usually does it for you). Pesos arrive within minutes; with 1.11 USDC you will see the rejection and the USDC back. |
Via API (SEP-6 / SEP-12) — the same data, with the standard field names:
# SEP-6 · deposit — does NOT need SEP-12: identity is verified in the more_info_url form
GET /sep6/deposit?asset_code=USDC&account=G...&amount=2&type=bank_transfer
# → pay through the transaction's more_info_url (same form, same test PSE)
# SEP-6 · withdrawal — DOES need SEP-12: PUT /sep12/customer (Authorization: Bearer <SEP-10 JWT>)
{
"account": "G...YOUR_TESTNET_ACCOUNT",
"type": "sep6-withdrawal",
"first_name": "Tester", # who withdraws: your user
"last_name": "Sandbox",
"email_address": "tester@example.com",
"id_type": "CC",
"id_number": "1000000001",
"bank_account_number": "@alphamunKey01" # the beneficiary: only their Bre-B key
}
GET /sep6/withdraw?asset_code=USDC&account=G...&amount=1&type=breb # amount=1.11 → refunded| Protocols | SEP-10 (authentication) · SEP-24 (with form) · SEP-6 (over API) · SEP-12 (customer data) · SEP-38 (firm rate) |
| Asset | USDC on Stellar (issuer in each environment's stellar.toml; Circle in production) |
| Minimum deposit | 5.000 COP |
| Minimum withdrawal | 1 USDC |
| Fiat pay-in | PSE (Colombia) |
| Fiat pay-out | Bre-B — instant payments to any key (Colombia) |
| Quotes | Final rate plus the fee declared separately, firm and single-use (SEP-38). 10 min on deposits, 5 on withdrawals |
| Support | contacto@vank.co |
Every number in this section comes from the chain: your browser reads the USDC payments of the anchor's treasury account on the Stellar public network and adds them up. No VANK server is involved. A deposit is USDC the treasury delivers to a user; a withdrawal is USDC it receives from a user.
Reading the chain…
SEP protocols are open standards of the Stellar ecosystem — the full specification lives at stellar.org. VANK · Powered by Stellar.
Tell us where you collect, which currencies you handle and who you pay. The team helps you map the right setup.