
State the movement, not the mechanism
Money journeys and cross-domain intent routing
Say where the money should end up and the service plans and runs every leg.
Name one movement, one asset and two endpoints, and the planner returns a single ordered plan whose legs run in Paxeer or in LayerX. Your wallet, a LayerX account, an agent or an agent budget can sit at either end. Planning reads no clock and no randomness, so the same intent over the same state gives byte-identical plan bytes. A balance the service cannot observe refuses the plan instead of counting as zero.
What it does
A person states one intent, to move an amount of one asset from one endpoint to another, and human/crates/layerx-human-service turns it into a single ordered plan whose legs run in Paxeer or in LayerX. The four endpoint kinds are the bound Paxeer wallet, a LayerX account, an agent and an agent budget, and either end may be in either domain. Planning is pure: it reads no clock, no randomness and does no input or output, so the same intent over the same observed state produces byte-identical canonical plan bytes and the same plan digest. Execution happens as a durable journey (deposit, move, withdraw or exit) where each leg runs to its own verified receipt before the next leg starts.
Key capabilities
- Plans across both domains from one stated movement, with the bound wallet or a LayerX account at either end.
- Picks the mechanism itself: the user's only verb is "Move money", never a bridge or transfer type.
- Orders candidates by fewest legs, lowest total fee, mechanism labels, then canonical plan bytes.
- Emits a top-up leg only where a user-signed allowance already covers that exact leg.
- Binds every leg to the plan digest, so a signed plan cannot be re-routed.
- Returns typed refusals instead of a partial plan: no route, deadline passed, shortfall, unquoted fee.
Who it's for
People and organisations moving funds between their Paxeer wallet, their LayerX account and their agents.
Why it matters
You say where the money should end up and the service works out the steps, including funding an agent budget that does not exist yet. The plan you sign is the plan that runs: submission re-plans server-side and refuses when the recomputed digest no longer matches, so a plan signed against a state that has since changed never executes. A balance the service cannot observe refuses the plan rather than being planned on as zero.
Differentiators
Three properties are enforced in code rather than asserted. Candidate selection is total and deterministic: three documented ranking rules plus a residual tie-break on canonical plan bytes, and an advisor may displace only that last rule (human/crates/layerx-human-service/src/journeys/router.rs:11-22). Allowance scope match is equality, so an allowance never stretches to a neighbouring endpoint, asset or mechanism and is never widened or synthesised (human/crates/layerx-human-service/src/journeys/router.rs:24-31, :228-229). The settlement wait shown for a withdrawal is computed solely from declared configuration, and zero values and arithmetic overflow are refused rather than defaulted (human/crates/layerx-human-service/src/journeys/withdraw.rs:73-94).
Outcomes
- Move money with one verb instead of choosing a mechanism.
- Get the same plan bytes from the same intent and state.
- Receive a typed refusal rather than a partial plan.
Technical notes
- Bounds in the planner: at most 12 legs per plan, 32 candidates, 8 notes of 256 bytes each. The TypeScript and Python clients decode at most 16 legs.
- Leg mechanisms are
paxeer-custody-depositandpaxeer-withdraw-finaliseon the Paxeer side, andbudget-create,budget-fund,budget-defund,send,receive-under-payer-grant,bridge-deposit-creditandbridge-withdraw-requestin LayerX. - Inside LayerX a movement is fund, allocate, return or transfer. Deposit, withdrawal and exit name crossings of the Paxeer custody boundary exclusively, and inherit that bridge's nullifiers, challenge window and withdrawal delay.
POST /v1/intents/planchanges no state and creates no journey;POST /v1/intents/submitrequires anIdempotency-Keyand repeating it returns the original journey. Progress is read throughGET /v1/journeys/{journey_id}.- The withdrawal settlement estimate needs
CHECKPOINT_INTERVAL_SECONDS,PAXEER_BLOCK_SECONDSandPAXEER_CONFIRMATIONS, each at least 1. Beta owner policy sets them to 60, 2 and 12 (platform/hosted/human/beta-owner-policy.json:14-19). - Not yet implemented:
intent.submitre-plans and compares the declared digest, then returns the plan. It creates no journey and checks neither the signed digest nor the per-leg bindings the contract declares. Separately, the precompile-event intent router inhuman/crates/layerx-intents/src/precompile.rshas no caller outside its own tests, so an intent arising from a chain event is not resolved end to end (docs/wiki/UnifiedNetwork.md:204). - Runtime floors: Node.js 22 or newer, Python 3.11 or newer.
import { HumanIntentClient, protocolAmount } from "layerx-sdk";
const client = new HumanIntentClient({ endpoint: process.env.LAYERX_HUMAN_SERVICE_URL! });
const plan = await client.planIntent({
source: { kind: "paxeer-wallet", account: null },
destination: { kind: "agent", account: "agent:did:layerx:bob:main" },
assetId: process.env.PAX_ASSET_ID!,
money: { amount: protocolAmount("500000"), currency: "PAX" },
constraints: { deadline: "2026-10-01T09:35:00Z", maxFee: { amount: protocolAmount("250"), currency: "PAX" }, allowTopUp: false },
});More in Interface
Build on Paxeer.
Give your agent a wallet, set its policy, and settle your first call on LayerX.