Send to an Anchor
Resolve a phone number or alias to get account details, then create a payment intent — the two-step pattern for paying someone you know by identifier, not by account address.
Sometimes you know the recipient by their phone number, email, or alias — not by their bank account address. In these cases, you resolve the anchor first to get the account details, then create the intent with the resolved information. This two-step pattern is common for loan disbursements, refunds to mobile wallets, and payroll to phone-linked accounts.
What you're building
In this guide you'll resolve a tel:+573001234567 anchor to find the recipient's wallet address, then create an intent to disburse a loan from the merchant's treasury to the resolved account. The same pattern works for any scenario where you have a human-readable identifier but need a wallet address before you can send.
Prerequisites
- A connection to a ledger — see How to Connect to a Ledger
- A funded source wallet (treasury or operating account)
- The recipient's anchor identifier (phone number, email, or alias)
Send the payment
Resolve the anchor
Before creating the intent, look up the anchor to find the wallet address it points to. Submit the identifier URL-encoded — tel:+573001234567 becomes tel%3A%2B573001234567 in the path.
minka anchor read "tel:+573001234567"curl "https://{ledger-host}/v2/anchors/tel%3A%2B573001234567" \
-H "Authorization: Bearer {your-token}" \
-H "x-ledger: {your-ledger}"// SDK setup: see How to Connect to a Ledger
// /moving-money/connect-to-ledger
// The SDK handles URL encoding automatically — pass the handle as-is
const { anchor } = await sdk.anchor.read('tel:+573001234567')
// The resolved wallet address is in anchor.data.target
const targetWallet = anchor.data.targetimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://{ledger-host}/v2/anchors/tel%3A%2B573001234567"))
.header("Authorization", "Bearer {your-token}")
.header("x-ledger", "{your-ledger}")
.GET()
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer {your-token}");
client.DefaultRequestHeaders.Add("x-ledger", "{your-ledger}");
var response = await client.GetAsync(
"https://{ledger-host}/v2/anchors/tel%3A%2B573001234567");
var body = await response.Content.ReadAsStringAsync();The curl examples use a Bearer token for authentication. In production, every mutation also requires a cryptographic signature — see Sign Payments for the full signing flow. The TypeScript SDK handles signing automatically.
The response includes the wallet address and any custom fields registered with the anchor:
{
"data": {
"wallet": "tel:+573001234567",
"target": "svgs:12345@recipient-bank.co",
"schema": "account",
"symbol": "cop",
"custom": {
"name": "Carlos Mendoza",
"documentType": "cc",
"documentNumber": "1023456789"
}
}
}The target field is the wallet address you'll use in the next step. The anchor maps the phone number to a specific account — in this case, Carlos's savings account at recipient-bank.co.
Create the intent
Use the resolved wallet address from target as the intent's target. The rest of the intent follows the same structure as any push payment.
minka intent createThe CLI walks you through each field:
| Prompt | What to enter | Example |
|---|---|---|
| Handle | A unique payment reference | loan-disbursement-001 |
| Source | The funding account | treasury@bank.co |
| Target | The resolved wallet address | svgs:12345@recipient-bank.co |
| Amount | Amount in smallest currency unit | 50000000 (500,000.00 COP) |
| Symbol | Currency handle | cop |
| Commit | Settlement mode | auto |
minka intent create submits a real payment. Use a test ledger for experimentation.
curl -X POST "https://{ledger-host}/v2/intents" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {your-token}" \
-H "x-ledger: {your-ledger}" \
-d '{
"data": {
"handle": "loan-disbursement-001",
"claims": [
{
"action": "transfer",
"amount": "50000000",
"symbol": { "handle": "cop" },
"source": { "handle": "treasury@bank.co" },
"target": { "handle": "svgs:12345@recipient-bank.co" }
}
],
"config": { "commit": "auto" }
}
}'// SDK setup: see How to Connect to a Ledger
// /moving-money/connect-to-ledger
const { response } = await sdk.intent
.init()
.data({
handle: 'loan-disbursement-001',
claims: [
{
action: 'transfer',
amount: '50000000',
symbol: { handle: 'cop' },
source: { handle: 'treasury@bank.co' },
target: { handle: anchor.data.target },
},
],
config: { commit: 'auto' },
})
.hash()
.sign([{ keyPair }])
.send()import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
String json = """
{
"data": {
"handle": "loan-disbursement-001",
"claims": [
{
"action": "transfer",
"amount": "50000000",
"symbol": { "handle": "cop" },
"source": { "handle": "treasury@bank.co" },
"target": { "handle": "svgs:12345@recipient-bank.co" }
}
],
"config": { "commit": "auto" }
}
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://{ledger-host}/v2/intents"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer {your-token}")
.header("x-ledger", "{your-ledger}")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());using System.Net.Http;
using System.Text;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer {your-token}");
client.DefaultRequestHeaders.Add("x-ledger", "{your-ledger}");
var json = """
{
"data": {
"handle": "loan-disbursement-001",
"claims": [
{
"action": "transfer",
"amount": "50000000",
"symbol": { "handle": "cop" },
"source": { "handle": "treasury@bank.co" },
"target": { "handle": "svgs:12345@recipient-bank.co" }
}
],
"config": { "commit": "auto" }
}
}
""";
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(
"https://{ledger-host}/v2/intents", content);
var body = await response.Content.ReadAsStringAsync();The source is where the funds come from — a treasury wallet or disbursement pool. The target is the wallet address returned by the anchor resolution. The claims array holds the transfer instruction. Setting config.commit to "auto" tells the ledger to settle immediately once participants confirm.
Check the result
minka intent read loan-disbursement-001curl "https://{ledger-host}/v2/intents/loan-disbursement-001" \
-H "Authorization: Bearer {your-token}" \
-H "x-ledger: {your-ledger}"// SDK setup: see How to Connect to a Ledger
// /moving-money/connect-to-ledger
const { intent } = await sdk.intent.read('loan-disbursement-001')import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://{ledger-host}/v2/intents/loan-disbursement-001"))
.header("Authorization", "Bearer {your-token}")
.header("x-ledger", "{your-ledger}")
.GET()
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());using System.Net.Http;
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer {your-token}");
client.DefaultRequestHeaders.Add("x-ledger", "{your-ledger}");
var response = await client.GetAsync(
"https://{ledger-host}/v2/intents/loan-disbursement-001");
var body = await response.Content.ReadAsStringAsync();A completed status means the funds have settled — the treasury was debited and Carlos's savings account was credited atomically. If the status is rejected, the response includes the reason from the participant that couldn't complete.
{
"data": {
"handle": "loan-disbursement-001",
"claims": [
{
"action": "transfer",
"amount": "50000000",
"symbol": { "handle": "cop" },
"source": { "handle": "treasury@bank.co" },
"target": { "handle": "svgs:12345@recipient-bank.co" }
}
]
},
"meta": {
"status": "completed"
}
}Why resolve at payment time? The anchor might resolve to a different account over time — a user can change which bank account their phone number points to. Always resolve the anchor at the moment of payment to get the current mapping, not a cached one from an earlier lookup.
Common patterns
The same two-step flow works for any scenario where you have an identifier but need a wallet address. Only the anchor type and accounts change.
| Pattern | Anchor | Source | Use case |
|---|---|---|---|
| Loan disbursement | tel:+573001234567 | treasury@bank.co | Pay out a loan to a phone-linked account |
| Email refund | email:customer@mail.co | ops:returns@merchant.co | Refund to customer's registered email |
| Alias payroll | emp:12345@company.co | ops:payroll@company.co | Monthly salary to employee alias |
What you learned
- How to resolve an anchor identifier to a wallet address before creating an intent
- Why anchor resolution should happen at payment time, not cached in advance
- How the two-step pattern — resolve then pay — works for any human-readable identifier
Common errors
| Error | Cause | Fix |
|---|---|---|
anchor-not-found | No anchor is registered for the given identifier | Verify the anchor handle and that it has been registered on this ledger |
anchor-expired | The anchor's TTL has elapsed and it is no longer valid | Create a new anchor — expired anchors cannot be reactivated |
target-wallet-inactive | The wallet that the anchor resolves to has been deactivated | Contact the wallet owner or check the wallet status |
Next steps
- About Anchors — how anchors map identifiers to wallets
- About Claims — the building blocks inside every intent
- How to Send a Payment to an Account — when you already know the wallet address
Send to an Account
Push funds to a specific bank account — payroll, vendor payments, refunds, or disbursements — using a single intent that works across institutions and networks.
Using Static QR
Create a permanent QR code for a store counter — the bridge reads the merchant profile and generates the complete network payload in a single API call.