Your first testnet payment
This is the whole loop: register a payment intent, sign it in your wallet, and watch it settle on testnet. Velarum never sees your key.
Before you start
You need a project API key, a testnet wallet with a little gas + test USDC (see Testnet faucets), and a Signer that talks to that wallet. The Signer is the only place your key is ever used — Velarum just calls it.
The code
import asyncio
from velarum import (VelarumClient, CreateAgentPaymentRequest,
InitiatorType, IntentSummary, PaymentIntentDetails,
BroadcastHintRequest)
from my_wallet import WalletSigner # YOU implement this; Velarum never sees your key
async def main():
# `signer` is explicit and required — there is no default signer (HC-NC-1).
async with VelarumClient(api_key="vlk_test_...", signer=WalletSigner()) as client:
# 1) Register the intent — Velarum returns it at `intent_created`.
payment = await client.payments.create(CreateAgentPaymentRequest(
agent_id="agt_123", initiator_type=InitiatorType.HUMAN,
payment_intent=PaymentIntentDetails(
amount="1.00", asset_id="base_sepolia_usdc",
settlement_chain_id="base-sepolia", destination="0xRecipient")))
# 2) YOU sign + broadcast in your wallet (Velarum never holds the key).
_signed, tx_hash = await client.signer.sign_and_broadcast(
chain_id="base-sepolia", unsigned_tx=b"<assembled-by-velarum>",
intent_summary=IntentSummary(
amount="1.00", destination="0xRecipient",
asset_id="base_sepolia_usdc", chain_id="base-sepolia"))
# 3) Tell the worker which tx_hash to observe.
await client.payments.submit_broadcast_hint(
payment.id, BroadcastHintRequest(chain_id="base-sepolia", tx_hash=tx_hash))
# 4) Watch authoritative status until terminal.
async for s in client.payments.watch_until_terminal(payment.id):
print(s.status) # chain_pending → … → settled
asyncio.run(main())What just happened
payments.create registered an intent at intent_created. Then your WalletSigner.sign_and_broadcast — not Velarum — signed and broadcast the transaction and returned its tx_hash. submit_broadcast_hint hands that hash to the worker, which observes the transaction on-chain and advances the status; without this hint the intent would stay at intent_created and watch_until_terminal would never finish. watch_until_terminal re-fetches authoritative status each poll until a terminal state — it never treats a local cache as truth.
You hold the keys (HC-NC-1)
Notice the signer=WalletSigner() argument: it is explicit and mandatory. Velarum assembles the unsigned transaction, but signing and broadcasting happen inside your WalletSigner. There is no path where Velarum holds or uses your key.
Run it end-to-end
Want to run the exact SDK flow with zero setup? The repo ships a self-contained, mocked version (a stub signer + a mocked backend) you can execute immediately:
pip install "velarum[dev]" # respx is a dev dependency used to mock the API
python apps/docs-site/examples/quickstart/first_payment.py
# status progression: ['chain_pending', 'settled']