🛠 Velarum Docs V0 — Phase-0 testnet only(per ADR-NC-027),内容陆续填入中。
ReferencePython SDK

Velarum Python SDK

velarum is the official Python client for the non-custodial Agent Payment API. It wraps requests, maps every backend error to a typed exception, and — critically — never holds your keys: signing happens in a Signer you supply (HC-NC-1).

  • Package: velarum · Version: 0.1.0a1 (Alpha — surface may change before 1.0)
  • Install: pip install velarum
  • Requires: Python ≥ 3.10. The client is async (httpx).

Constructing the client

VelarumClient(api_key, signer, base_url="https://api.velarumai.com", timeout=30.0). The signer is required for any flow that produces an on-chain transaction; there is no default signer — if you omit it, calls that need signing raise at call time. Use it as an async context manager so the HTTP pool closes cleanly.

import asyncio
from velarum import VelarumClient
from my_wallet import MyWalletSigner  # your Signer implementation (see below)
 
async def main():
    async with VelarumClient(api_key="vlk_test_...", signer=MyWalletSigner()) as client:
        agent = await client.agents.get("agt_123")
        print(agent.status)
 
asyncio.run(main())

The Signer protocol (HC-NC-1)

A Signer is the only place a private key is ever touched, and it lives in your process — a browser wallet bridge, a hardware wallet, a mobile enclave, an MPC service, etc. Velarum invokes it but never serializes or caches the signed bytes. You must implement two methods:

from velarum import Signer, IntentSummary
 
class MyWalletSigner:  # structurally satisfies velarum.Signer
    async def sign_and_broadcast(self, chain_id: str, unsigned_tx: bytes,
                                 intent_summary: IntentSummary) -> tuple[bytes, str]:
        # Show intent_summary to the user, sign in your wallet, broadcast,
        # and return (signed_tx_raw, tx_hash). Velarum keeps neither.
        ...
 
    async def sign_jws(self, payload: dict) -> str:
        # Return a compact JWS (header.payload.signature) for abandon /
        # consent / revocation receipts, signed by the user's key.
        ...

Errors

Every method raises a subclass of velarum.VelarumError carrying code, request_id, and details. Notable typed errors: HitlRequiredError (HC_NC_5_HITL_REQUIRED, inspect .triggered_thresholds / .next_action), AutomationConsentMissingError, IdempotencyConflictError, PaymentAbandonNotAllowedError, and the WithdrawalRequest* / SovereignAddress* family. The complete code → exception mapping is in Error codes.

Agent payments — client.payments

MethodPurposeReturnsRaises (notable)
create(req, *, idempotency_key=None)Register a non-custodial payment intent (intent_created)AgentPaymentDtoHitlRequiredError, AutomationConsentMissingError, IdempotencyConflictError
quote(req)Route/fee preview; creates no statePaymentQuoteResponseDataVelarumError
get(payment_id)Authoritative current statusAgentPaymentDtoVelarumError
abandon(payment_id, req)Terminate an unsettled intent with a user-signed receiptAgentPaymentDtoPaymentAbandonNotAllowedError, AbandonReceiptInvalidError
submit_broadcast_hint(payment_id, req)Tell the worker which tx_hash you broadcastAgentPaymentDtoBroadcastHintRejectedError
watch_until_terminal(payment_id, interval=2.0)Async-iterate status until terminal (re-fetches, never caches)AsyncIterator[AgentPaymentDto]VelarumError
from velarum import CreateAgentPaymentRequest, InitiatorType, PaymentIntentDetails
 
dto = await client.payments.create(
    CreateAgentPaymentRequest(
        agent_id="agt_123",
        initiator_type=InitiatorType.HUMAN,
        payment_intent=PaymentIntentDetails(
            amount="5.00", asset_id="base_usdc",
            settlement_chain_id="base", destination="0xRecipient",
        ),
    ),
    idempotency_key="order-9001",
)
async for status in client.payments.watch_until_terminal(dto.id):
    print(status.status)  # intent_created → chain_pending → … → settled

Automation policies (HC-NC-5)

On the client directly: create_automation_policy(req), get_automation_policy(policy_id), revoke_automation_policy(policy_id, req). The request requires all four thresholds — there is no server-side “recommended” default.

from velarum import CreateAutomationPolicyRequest, CumulativePeriod, ValidityWindow
 
policy = await client.create_automation_policy(
    CreateAutomationPolicyRequest(
        agent_id="agt_123",
        single_tx_limit="10.00", cumulative_limit="100.00",
        cumulative_period=CumulativePeriod.ROLLING_24H,
        validity_window=ValidityWindow(start_at="2026-06-01T00:00:00Z",
                                       end_at="2026-12-31T23:59:59Z"),
        merchant_whitelist=["0xMerchant"],
        consent_receipt="<JWS you signed>", idempotency_key="pol-1",
    )
)

Other resource namespaces

All are async and accessed off the client. Names below are the real exported resources and methods.

NamespaceMethods
client.authvalidate_api_key, validate_agent_token, admin_login, admin_verify_2fa
client.organizationscreate, get, update, create_project, list_projects, invite_member, list_members, update_member_role
client.projectscreate, list, get, update
client.agentscreate, get, get_allowance, get_status, freeze, unfreeze
client.authorization_grantscreate, list, get, update, revoke, renew
client.policiescreate, list, get, update, delete, freeze, unfreeze
client.walletscreate, list, get, update, freeze, unfreeze, close, create_address, list_addresses_for_wallet, list_addresses
client.subjectscreate, get, update, submit_verification, verify, freeze, unfreeze, close, invite_member, list_members, accept_member, update_member, remove_member
client.ledgerslist, get, observe
client.accountscreate, list, get, freeze, unfreeze, close
client.auditslist_transactions, dashboard, export, list_admin_logs

Sovereign withdrawals — client.withdrawals (HC-NC-1)

Withdrawals are non-custodial: Velarum returns unsigned calldata for your own Safe (or equivalent) multisig; your wallet signs and broadcasts. Methods: create, list, get, broadcast_hint, abandon, get_unsigned_approve_calldata, get_unsigned_execute_calldata, watch_until_terminal, list_addresses, get_address.

from velarum import CreateWithdrawalRequest, WithdrawalReasonLabel
 
resp = await client.withdrawals.create(
    CreateWithdrawalRequest(
        source_wallet_id="wal_1", source_chain_id="base", source_asset_id="base_usdc",
        amount="25.00", destination_address="0xAllowlisted",
        reason_label=WithdrawalReasonLabel.PARTIAL_WITHDRAW,
    )
)
# resp.unsigned_propose_calldata is UNSIGNED — hand it to your wallet to sign.
print(resp.unsigned_propose_calldata.to, resp.unsigned_propose_calldata.data)