Free API and Test Merchant
A complete, approval-free path for integrating identifier resolution into a prototype wallet. You will generate a key, read the shared Test Merchant QR, resolve it, and send a real Base Sepolia USDC payment from your wallet.
Requirements
- A Rezva dashboard account at /dashboard.
- An active Free API key from /dashboard/free-api.
- A wallet that can send Base Sepolia USDC.
- A configured Rezva resolver URL. Use the URL in the QR payload.
The Test Merchant is a protocol demo merchant. It is not a real merchant and must only receive testnet funds.
1. Get a Free API key
Open the Free API dashboard and select Generate key. The complete plaintext key is shown only at that moment. Store it in a server-side environment variable; do not commit it or put it in a browser bundle.
# .env.local (never commit this file) REZVA_FREE_API_KEY=rzva_free_… REZVA_API_BASE_URL=https://api.example.com
Regeneration revokes the previous key. If a key is exposed, revoke it immediately from the dashboard and generate a replacement.
2. Read the Test Merchant QR
Open Test Merchant in the Free API dashboard. The same persistent merchant identifier, wallet, and QR payload are used by every developer in the environment. A QR scanner should parse the JSON rather than treating the entire QR text as an address.
{
"resolver_url": "https://api.example.com/v1/resolve",
"type": "custom",
"value": "rezva-test-merchant"
}Save resolver_url, type, and value. The wallet sends only type and value to the resolver.
3. Resolve the identifier
Make an authenticated POST request to the QR’s resolver URL. The key is valid for this resolver operation only.
TypeScript
const response = await fetch(
"https://api.example.com/v1/resolve",
{
method: "POST",
headers: {
"content-type": "application/json",
authorization: `Bearer ${process.env.REZVA_FREE_API_KEY}`,
},
body: JSON.stringify({
type: "custom",
value: "rezva-test-merchant",
}),
},
);
if (!response.ok) {
throw new Error(`Resolve failed: ${response.status} ${await response.text()}`);
}
const resolved = await response.json();Python
import os
import requests
response = requests.post(
"https://api.example.com/v1/resolve",
headers={
"content-type": "application/json",
"authorization": f"Bearer {os.environ['REZVA_FREE_API_KEY']}",
},
json={"type": "custom", "value": "rezva-test-merchant"},
timeout=10,
)
response.raise_for_status()
resolved = response.json()A successful response includes lifecycle state and one or more payment options. The resolver does not create, sign, broadcast, or confirm the blockchain transaction.
{
"protocol_version": "1",
"status": "active",
"payment_ready": true,
"identifier": {
"type": "custom",
"value": "rezva-test-merchant",
"normalized_value": "rezva-test-merchant"
},
"payment_options": [
{
"chain": "base",
"asset": "USDC",
"address": "0xMerchantWallet",
"token_contract": "0xBaseSepoliaUsdc",
"network_identifier": "eip155:84532",
"payment_ready": true
}
]
}4. Choose the payment option safely
Never select the first array item blindly. Filter by the chain and asset your wallet supports, then require both readiness flags.
const option = resolved.payment_options.find(
(candidate) =>
candidate.chain === "base" &&
candidate.asset === "USDC" &&
candidate.network_identifier === "eip155:84532" &&
candidate.payment_ready === true,
);
if (resolved.status !== "active" || !resolved.payment_ready || !option) {
throw new Error("The destination is not ready for Base Sepolia USDC payment");
}
// Show these values and get explicit user confirmation:
console.log({
chain: option.chain,
network: option.network_identifier,
asset: option.asset,
tokenContract: option.token_contract,
recipient: option.address,
});addressis the recipient for the token transfer.token_contractidentifies the USDC contract to call.network_identifiereip155:84532is Base Sepolia.- Respect
minimum_amount,maximum_amount,fixed_amount, andexpirationwhen present.
5. Execute and verify the payment
- Switch the wallet to Base Sepolia (chain ID 84532).
- Show the token contract and recipient to the user.
- Ask for the amount and explicit confirmation.
- Use the wallet’s own signer to call the USDC
transferfunction. - Wait for the transaction receipt and require a successful status.
- Verify the recipient and amount in the USDC
Transferevent.
// The wallet owns this step. Rezva never receives private keys.
const txHash = await wallet.sendUsdc({
chainId: 84532,
token: option.token_contract,
recipient: option.address,
amount: "0.01",
});
const receipt = await wallet.waitForTransaction(txHash);
if (receipt.status !== "success") {
throw new Error("Base Sepolia transaction failed");
}For the demo flow, verify the real transaction independently at https://sepolia.basescan.org/ by searching the transaction hash or the shared merchant wallet. The dashboard’s Detected payments table also shows the confirmed transaction after its ledger scan.
Errors and limits
401— missing, invalid, revoked, or misplaced key.404— the typed identifier was not found.400— unsupported type or invalid identifier value.429— the Free API limit was reached; honorRetry-After.
Each Free API key allows 10 resolver requests per minute and 100 resolver requests per UTC day. It cannot register, update, suspend, remove identifiers, manage namespaces, or access operator data.
if (response.status === 429) {
const retryAfter = Number(response.headers.get("retry-after") ?? "60");
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
}Continue with the API Reference for field definitions, or use the OpenAPI specification to generate a client.
Open Free API testing