openapi: 3.2.0 info: contact: email: support@assetfare.dev name: AssetFare support url: https://assetfare.dev/security/ description: 'Agent-native, non-custodial direct-protocol quotes and caller-signed unsigned actions on 54 active routes; 44 measured routes remain inactive. Forty-four active routes publish a verified best-from amount and ten routes are availability-only with no cheapest-price claim. Solana native USDC to Base native USDC is the canonical example. Agents confirm every fresh quote before choosing. Every quote includes direct_route_summary: an intent-bound ordered provider path with normalized chain:asset endpoints, exact base-unit bounds and the AssetFare fee step. All current public routes are direct_protocol_only; external_intent remains a compatibility enum with zero current routes. route_aggregator_used=false is scoped to AssetFare''s engine and does not rule out provider-internal liquidity sourcing. The server never signs or submits. Contract 2.6.0 uses a language-neutral continuation_v3 quote-payload hash projection and server-enforced quote binding. Legacy prepare/session remain legacy_advisory; caller_approved:true alone is not proof of human approval. ActionSafetyReceiptV1 and caller-owned session capability tokens remain required.' license: name: Proprietary public API terms url: https://assetfare.dev/terms/ termsOfService: https://assetfare.dev/terms/ title: AssetFare Active-route Agent Session API version: 2.6.0 servers: - url: https://api.assetfare.dev security: [] tags: - name: Session paths: /v2/session: post: description: approval_v3 selects session and is enforced before workflow/action creation. The same quote cannot also select one_shot. A fresh quote must remain inside caller bounds with the same path/providers. Without approval_v3 this is legacy_advisory; caller_approved alone is not proof of human approval. operationId: createMultichainSession parameters: - description: Caller-generated opaque capability token (>=256-bit CSPRNG, urlsafe-base64). The caller supplies it on the FIRST POST /v2/session (create) and on every read/observe/refresh. The server stores only its sha256 hash and never returns the raw token. The calling network identity is NOT the authorization secret, so a rotated egress IP that presents the correct token keeps access; a retry with the same token+idempotency_key recovers a session whose create response was lost. in: header name: X-AssetFare-Session-Token required: true schema: maxLength: 128 minLength: 43 type: string requestBody: content: application/json: schema: additionalProperties: false allOf: - if: properties: from_chain: const: solana to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - if: properties: from_chain: const: solana to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - solana - base required: - event_signer_public - if: properties: from_chain: const: solana to_chain: const: arbitrum required: - from_chain - to_chain then: properties: wallets: required: - solana - arbitrum required: - event_signer_public - if: properties: from_chain: const: solana to_chain: const: robinhood required: - from_chain - to_chain then: properties: wallets: required: - solana - robinhood - if: properties: from_chain: const: base to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - base - if: properties: from_chain: const: base to_chain: const: robinhood required: - from_chain - to_chain then: properties: wallets: required: - solana - base - robinhood - if: properties: from_chain: const: arbitrum to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - arbitrum - if: properties: from_chain: const: arbitrum to_chain: const: arbitrum required: - from_chain - to_chain then: properties: wallets: required: - arbitrum - if: properties: from_chain: const: arbitrum to_chain: const: robinhood required: - from_chain - to_chain then: properties: wallets: required: - solana - arbitrum - robinhood - if: properties: from_chain: const: robinhood to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - robinhood - if: properties: from_chain: const: robinhood to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - solana - base - robinhood required: - event_signer_public - if: properties: from_chain: const: robinhood to_chain: const: arbitrum required: - from_chain - to_chain then: properties: wallets: required: - solana - arbitrum - robinhood required: - event_signer_public - if: properties: from_chain: const: robinhood to_chain: const: robinhood required: - from_chain - to_chain then: properties: wallets: required: - robinhood - if: properties: from_chain: const: optimism to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - optimism - if: properties: from_chain: const: ethereum to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - ethereum - if: properties: from_chain: const: hyperevm to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - hyperevm - if: properties: from_chain: const: xlayer to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - xlayer - if: properties: from_chain: const: xlayer to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - xlayer - if: properties: from_chain: const: sei to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - sei - if: properties: from_chain: const: sei to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - sei - if: properties: from_chain: const: sonic to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - sonic - if: properties: from_chain: const: sonic to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - sonic - if: properties: from_chain: const: monad to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - monad - if: properties: from_chain: const: monad to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - monad - if: properties: from_chain: const: avalanche to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - avalanche - if: properties: from_chain: const: avalanche to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - avalanche - if: properties: from_chain: const: cronos to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - cronos - if: properties: from_chain: const: cronos to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - cronos - if: properties: from_chain: const: injective to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - injective - if: properties: from_chain: const: injective to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - injective - if: properties: from_chain: const: linea to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - linea - if: properties: from_chain: const: linea to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - linea - if: properties: from_chain: const: aptos to_chain: const: solana required: - from_chain - to_chain then: properties: wallets: required: - solana - aptos - if: properties: from_chain: const: aptos to_chain: const: base required: - from_chain - to_chain then: properties: wallets: required: - base - aptos properties: amount_usd: description: No business maximum. USD 1 is supported for reachability smoke testing only. Prefer AssetFare only when the returned economic_guidance.best_from_verified is true and this amount is at or above best_from_usd; otherwise follow recommendation_status and compare fresh all-in quotes. minimum: 1 type: number approval_v3: additionalProperties: false properties: direct_route_summary_sha256: pattern: ^[0-9a-f]{64}$ type: string idempotency_key: pattern: ^[A-Za-z0-9._:-]{8,128}$ type: string maximum_input_base: pattern: ^[1-9][0-9]*$ type: string minimum_output_base: pattern: ^[1-9][0-9]*$ type: string quote_fingerprint: pattern: ^[0-9a-f]{64}$ type: string quote_id: format: uuid type: string selected_mode: const: session selection_status: const: selected version: const: assetfare-quote-bound-approval-v3 required: - version - quote_id - quote_fingerprint - selection_status - selected_mode - maximum_input_base - minimum_output_base - direct_route_summary_sha256 - idempotency_key type: object caller_approved: const: true description: Legacy caller assertion retained for compatibility. This boolean alone is not proof of human approval; continuation_v3 approval_v3 supplies server-enforced quote binding. type: boolean event_signer_public: description: Caller-generated, base58-encoded 32-byte Solana Ed25519 public key that is on curve and can co-sign the CCTP event-account transaction. Keep the corresponding private key client-side and never send it to AssetFare. Required only when a route includes a Solana CCTP burn. maxLength: 44 minLength: 32 pattern: ^[1-9A-HJ-NP-Za-km-z]+$ type: string from_chain: enum: - solana - base - arbitrum - robinhood - optimism - ethereum - hyperevm - xlayer - sei - sonic - monad - avalanche - cronos - injective - linea - aptos type: string from_token: enum: - SOL - ETH - USDC - USDG type: string idempotency_key: pattern: ^[A-Za-z0-9._:-]{8,128}$ type: string to_chain: enum: - solana - base - arbitrum - robinhood type: string to_token: enum: - SOL - ETH - USDC - USDG type: string wallets: additionalProperties: false properties: aptos: description: Caller-controlled Aptos account address. pattern: ^0x[0-9a-fA-F]{64}$ type: string arbitrum: description: Caller-controlled arbitrum EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string avalanche: description: Caller-controlled avalanche EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string base: description: Caller-controlled base EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string cronos: description: Caller-controlled cronos EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string ethereum: description: Caller-controlled ethereum EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string hyperevm: description: Caller-controlled hyperevm EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string injective: description: Caller-controlled injective EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string linea: description: Caller-controlled linea EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string monad: description: Caller-controlled monad EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string optimism: description: Caller-controlled optimism EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string polygon: description: Caller-controlled polygon EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string robinhood: description: Caller-controlled robinhood EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string sei: description: Caller-controlled sei EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string solana: description: Caller-controlled, base58-encoded 32-byte Solana Ed25519 public key that is on curve and can sign. Program-derived/off-curve addresses are rejected. maxLength: 44 minLength: 32 pattern: ^[1-9A-HJ-NP-Za-km-z]+$ type: string sonic: description: Caller-controlled sonic EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string xlayer: description: Caller-controlled xlayer EVM address. pattern: ^0x[0-9a-fA-F]{40}$ type: string required: [] type: object required: - from_chain - from_token - to_chain - to_token - amount_usd - caller_approved - wallets - idempotency_key type: object required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/SessionResponse' description: Idempotent replay of an existing session '201': content: application/json: schema: $ref: '#/components/schemas/SessionResponse' description: First unsigned action with ActionSafetyReceiptV1 (caller supplies X-AssetFare-Session-Token; raw token never returned) '400': description: Invalid request, caller approval missing, session token missing/malformed, or capacity reached '409': content: application/json: schema: $ref: '#/components/schemas/QuoteBoundReapprovalRequiredV3' description: Quote binding expired, changed, replayed, or outside caller bounds; no session/action was created and reapproval is required '429': description: Rate limited '503': description: Capacity busy summary: Create a quote-bound idempotent receipt-driven session tags: - Session /v2/session/{session_id}: get: operationId: getMultichainSession parameters: - in: path name: session_id required: true schema: format: uuid type: string - description: Caller-generated opaque capability token (>=256-bit CSPRNG, urlsafe-base64). The caller supplies it on the FIRST POST /v2/session (create) and on every read/observe/refresh. The server stores only its sha256 hash and never returns the raw token. The calling network identity is NOT the authorization secret, so a rotated egress IP that presents the correct token keeps access; a retry with the same token+idempotency_key recovers a session whose create response was lost. in: header name: X-AssetFare-Session-Token required: true schema: maxLength: 128 minLength: 43 type: string responses: '200': content: application/json: schema: $ref: '#/components/schemas/SessionResponse' description: Current state '404': description: Session not found or capability token missing/incorrect '409': content: application/json: schema: $ref: '#/components/schemas/QuoteBoundReapprovalRequiredV3' description: Durable quote-bound session path or minimum changed. No new action was created; do not repeat confirmed steps or start another session. There is no replacement-approval attachment endpoint. Keep this session stopped and retry its refresh operation only if a fresh read-only quote indicates the original approved path and bounds are again satisfiable; the new quote itself is not action authority. summary: Read workflow state after recomputing the stored action receipt and digests tags: - Session /v2/session/{session_id}/observe-output: post: operationId: observeOutputReceipt parameters: - in: path name: session_id required: true schema: format: uuid type: string - description: Caller-generated opaque capability token (>=256-bit CSPRNG, urlsafe-base64). The caller supplies it on the FIRST POST /v2/session (create) and on every read/observe/refresh. The server stores only its sha256 hash and never returns the raw token. The calling network identity is NOT the authorization secret, so a rotated egress IP that presents the correct token keeps access; a retry with the same token+idempotency_key recovers a session whose create response was lost. in: header name: X-AssetFare-Session-Token required: true schema: maxLength: 128 minLength: 43 type: string requestBody: content: application/json: schema: additionalProperties: false properties: idempotency_key: pattern: ^[A-Za-z0-9._:-]{8,128}$ type: string transaction_hash: pattern: ^0x[0-9a-fA-F]{64}$ type: string required: - idempotency_key type: object required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/SessionResponse' description: Verified output and, when available, the next receipt-bound action '400': description: Invalid observation or capability token missing/incorrect '409': content: application/json: schema: $ref: '#/components/schemas/QuoteBoundReapprovalRequiredV3' description: Durable quote-bound session path or minimum changed. No new action was created; do not repeat confirmed steps or start another session. There is no replacement-approval attachment endpoint. Keep this session stopped and retry its refresh operation only if a fresh read-only quote indicates the original approved path and bounds are again satisfiable; the new quote itself is not action authority. '429': description: Rate limited summary: Derive bridge output from provider and chain receipts tags: - Session /v2/session/{session_id}/observe-source: post: operationId: observeSourceReceipt parameters: - in: path name: session_id required: true schema: format: uuid type: string - description: Caller-generated opaque capability token (>=256-bit CSPRNG, urlsafe-base64). The caller supplies it on the FIRST POST /v2/session (create) and on every read/observe/refresh. The server stores only its sha256 hash and never returns the raw token. The calling network identity is NOT the authorization secret, so a rotated egress IP that presents the correct token keeps access; a retry with the same token+idempotency_key recovers a session whose create response was lost. in: header name: X-AssetFare-Session-Token required: true schema: maxLength: 128 minLength: 43 type: string requestBody: content: application/json: schema: additionalProperties: false properties: idempotency_key: pattern: ^[A-Za-z0-9._:-]{8,128}$ type: string transaction_hashes: items: type: string maxItems: 4 minItems: 1 type: array uniqueItems: true required: - idempotency_key - transaction_hashes type: object required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/SessionResponse' description: Verified source state and, when available, the next receipt-bound action '400': description: Invalid observation or capability token missing/incorrect '409': content: application/json: schema: $ref: '#/components/schemas/QuoteBoundReapprovalRequiredV3' description: Durable quote-bound session path or minimum changed. No new action was created; do not repeat confirmed steps or start another session. There is no replacement-approval attachment endpoint. Keep this session stopped and retry its refresh operation only if a fresh read-only quote indicates the original approved path and bounds are again satisfiable; the new quote itself is not action authority. '429': description: Rate limited summary: Derive source observation from caller-submitted transaction hashes tags: - Session /v2/session/{session_id}/refresh-action: post: operationId: refreshMultichainAction parameters: - in: path name: session_id required: true schema: format: uuid type: string - description: Caller-generated opaque capability token (>=256-bit CSPRNG, urlsafe-base64). The caller supplies it on the FIRST POST /v2/session (create) and on every read/observe/refresh. The server stores only its sha256 hash and never returns the raw token. The calling network identity is NOT the authorization secret, so a rotated egress IP that presents the correct token keeps access; a retry with the same token+idempotency_key recovers a session whose create response was lost. in: header name: X-AssetFare-Session-Token required: true schema: maxLength: 128 minLength: 43 type: string requestBody: content: application/json: schema: additionalProperties: false properties: idempotency_key: pattern: ^[A-Za-z0-9._:-]{8,128}$ type: string transaction_hash: pattern: ^0x[0-9a-fA-F]{64}$ type: string required: - idempotency_key type: object required: true responses: '200': content: application/json: schema: $ref: '#/components/schemas/SessionResponse' description: Fresh current action with ActionSafetyReceiptV1 '400': description: Action is not refreshable or capability token missing/incorrect '409': content: application/json: schema: $ref: '#/components/schemas/QuoteBoundReapprovalRequiredV3' description: Durable quote-bound session path or minimum changed. No new action was created; do not repeat confirmed steps or start another session. There is no replacement-approval attachment endpoint. Keep this session stopped and retry its refresh operation only if a fresh read-only quote indicates the original approved path and bounds are again satisfiable; the new quote itself is not action authority. '429': description: Rate limited summary: Replace an expired unsubmitted action with a freshly validated receipt-bound action tags: - Session components: schemas: SessionResponse: additionalProperties: true properties: action_available: type: boolean current_action: oneOf: - $ref: '#/components/schemas/UnsignedActionBundle' - type: 'null' quote_binding: oneOf: - additionalProperties: false properties: quote_fingerprint: pattern: ^[0-9a-f]{64}$ type: string quote_id: format: uuid type: string selected_mode: const: session server_signing: const: false server_submission: const: false version: const: assetfare-quote-bound-session-constraints-v1 whole_session_path_and_bounds_enforced: const: true required: - version - quote_id - quote_fingerprint - selected_mode - whole_session_path_and_bounds_enforced - server_signing - server_submission type: object - additionalProperties: false properties: server_signing: const: false server_submission: const: false version: const: legacy_advisory whole_session_path_and_bounds_enforced: const: false required: - version - whole_session_path_and_bounds_enforced - server_signing - server_submission type: object server_signing: const: false server_submission: const: false session_id: format: uuid type: string signed: const: false status: type: string submitted: const: false required: - session_id - status - action_available - current_action - server_signing - server_submission - signed - submitted - quote_binding type: object ActionSafetyReceiptV1: additionalProperties: false description: Deterministically recomputed from decoded/raw unsigned action data. This is a consistency receipt, not a signature or a subjective safe=true claim. properties: action: additionalProperties: false properties: builder_version: type: - string - 'null' kind: enum: - evm_dex - solana_dex - evm_cctp - evm_cctp_receive - solana_cctp - aptos_cctp - evm_layerzero - solana_layerzero - evm_across provider: type: string required: - kind - provider - builder_version type: object approval: additionalProperties: false properties: exact_allowance_base: pattern: ^[0-9]+$ type: - string - 'null' required: type: boolean target: type: - string - 'null' token: type: - string - 'null' transaction_index: minimum: 0 type: integer required: - required - token - target - exact_allowance_base type: object assetfare_service_fee: additionalProperties: false properties: amount_known_before_execution: type: boolean basis: additionalProperties: false properties: amount_base: pattern: ^[0-9]+$ type: - string - 'null' kind: type: string required: - kind - amount_base type: object bps: maximum: 1 minimum: 0 type: integer exact_amount_base: pattern: ^[0-9]+$ type: - string - 'null' formula: const: floor(fee_basis_base * bps / 10000) recipient: type: - string - 'null' required: - basis - bps - formula - exact_amount_base - amount_known_before_execution - recipient type: object custody: additionalProperties: false properties: server_signing: const: false server_submission: const: false required: - server_signing - server_submission type: object destination: additionalProperties: false properties: domain: type: - integer - string - 'null' recipient: type: - string - 'null' required: - recipient - domain type: object generation: const: decoded_built_action_only native_value_cap: additionalProperties: false properties: maximum_base: pattern: ^[0-9]+$ type: string per_transaction_base: items: pattern: ^[0-9]+$ type: string type: array unit: enum: - wei - lamports - octas required: - maximum_base - unit - per_transaction_base type: object network: additionalProperties: false properties: destination_chain: type: string source_chain: type: string source_chain_id: type: - integer - string required: - source_chain - source_chain_id - destination_chain type: object parties: additionalProperties: false properties: caller: type: string fee_payer: type: string required: - caller - fee_payer type: object payload_binding: additionalProperties: false properties: action_sha256: pattern: ^[0-9a-f]{64}$ type: string canonicalization: const: UTF-8 JSON sorted keys compact separators; omit safety_receipt raw_payloads: items: additionalProperties: false properties: chain_id: minimum: 1 type: integer data_prefix_hex: pattern: ^[0-9a-f]*$ type: string data_sha256: pattern: ^[0-9a-f]{64}$ type: string index: minimum: 0 type: integer instruction_type: type: - string - 'null' kind: enum: - evm_transaction - solana_instruction - aptos_raw_transaction native_value_base: pattern: ^[0-9]+$ type: string program_id: type: string raw_sha256: pattern: ^[0-9a-f]{64}$ type: string selector: type: string target: type: string required: - index - kind - raw_sha256 - data_sha256 type: object minItems: 1 type: array required: - canonicalization - action_sha256 - raw_payloads type: object receive: additionalProperties: false properties: expected_amount_base: pattern: ^[0-9]+$ type: - string - 'null' minimum_amount_base: pattern: ^[0-9]+$ type: string token: additionalProperties: false properties: address_or_mint: type: string symbol: type: string required: - symbol - address_or_mint type: object required: - token - expected_amount_base - minimum_amount_base type: object risks: additionalProperties: false properties: caller_verification_required: const: true provider: items: type: string minItems: 1 type: array unpriced: items: type: string minItems: 1 type: array required: - unpriced - provider - caller_verification_required type: object schema: const: https://assetfare.dev/schemas/action-safety-receipt-v1 schema_version: const: 1 selector_or_instruction_allowlist: items: type: string minItems: 1 type: array uniqueItems: true spend: additionalProperties: false properties: exact_amount_base: pattern: ^[0-9]+$ type: string maximum_amount_base: pattern: ^[0-9]+$ type: string token: additionalProperties: false properties: address_or_mint: type: string symbol: type: string required: - symbol - address_or_mint type: object required: - token - exact_amount_base - maximum_amount_base type: object target_or_program_allowlist: items: type: string minItems: 1 type: array uniqueItems: true timing: additionalProperties: false properties: action_deadline_unix: type: - integer - 'null' bundle_expires_at: format: date-time type: - string - 'null' quote_expiry_unix: type: - integer - 'null' recent_blockhash: type: - string - 'null' required: - action_deadline_unix - quote_expiry_unix - recent_blockhash - bundle_expires_at type: object required: - schema - schema_version - generation - network - action - parties - spend - approval - target_or_program_allowlist - selector_or_instruction_allowlist - native_value_cap - assetfare_service_fee - receive - destination - timing - payload_binding - custody - risks type: object UnsignedAction: additionalProperties: true properties: safety_receipt: $ref: '#/components/schemas/ActionSafetyReceiptV1' serverSigning: const: false serverSubmission: const: false signed: const: false submitted: const: false required: - safety_receipt - signed - submitted type: object QuoteBoundReapprovalRequiredV3: additionalProperties: false properties: action_created: const: false automatic_selection_forbidden: const: true caller_approved_boolean_is_not_human_proof: const: true do_not_repeat_confirmed_steps: type: boolean do_not_start_new_session: type: boolean error: enum: - reapproval_required - approval_v3_invalid event_signer_public_required: type: boolean fresh_read_only_quote_required: type: boolean new_quote_is_not_action_authority: const: true reason: minLength: 1 type: string recover_current_session: type: boolean recovery_operation: enum: - obtain_new_quote_and_make_new_selection - refresh_current_session_only_when_original_approved_bounds_are_satisfied replacement_approval_endpoint_available: type: boolean required_wallet_chains: items: enum: - aptos - arbitrum - avalanche - base - cronos - ethereum - hyperevm - injective - linea - monad - optimism - polygon - robinhood - sei - solana - sonic - xlayer maxItems: 17 minItems: 1 type: array uniqueItems: true selection_status: const: unranked_candidate server_signing: const: false server_submission: const: false required: - error - reason - action_created - fresh_read_only_quote_required - new_quote_is_not_action_authority - do_not_repeat_confirmed_steps - do_not_start_new_session - recover_current_session - recovery_operation - replacement_approval_endpoint_available - selection_status - automatic_selection_forbidden - caller_approved_boolean_is_not_human_proof - required_wallet_chains - event_signer_public_required - server_signing - server_submission type: object UnsignedActionBundle: additionalProperties: true properties: action_id: format: uuid type: string expires_at: format: date-time type: string payload_sha256: pattern: ^[0-9a-f]{64}$ type: string server_signing: const: false server_submission: const: false signed: const: false step_index: minimum: 0 type: integer submitted: const: false unsigned_action: $ref: '#/components/schemas/UnsignedAction' workflow_id: format: uuid type: string required: - workflow_id - action_id - step_index - expires_at - unsigned_action - payload_sha256 - server_signing - server_submission - signed - submitted type: object x-assetfare-evaluation-guidance: always_compare_fresh_at_intended_amount: true documentation_example_scope: example_only_not_route_guidance_or_minimum documentation_example_usd: 1000 historical_observation: evidence_as_of: '2026-09-29' not_generalizable: true observed_competitive_bucket_usd: 500 route: solana:USDC->base:USDC not_a_minimum: true not_guaranteed_best: true reachability_smoke_scope: connectivity_only_not_economic_evaluation reachability_smoke_usd: 1 route_minimum_usd: 1 route_specific_guidance: availability_only_routes: 10 catalog_routes: 98 controls_recommendation_only_when_verified: true nullable_when_unverified: true public_active_routes: 54 public_inactive_routes: 44 required_on_every_quote: true url: https://assetfare.dev/route-economics.json values_change_with_market: true verified_best_from_only: true version: assetfare-route-economic-guidance-v3 schema_version: 4 sol_input_caveat: SOL-input routes add a source swap, so compare their full fee-inclusive route economics separately. x-assetfare-safety: action_bundle_ttl_seconds: 180 action_refresh_policy: expired_unsubmitted_only action_safety_receipt_schema: ActionSafetyReceiptV1 amount_policy: no_business_maximum amount_usd_maximum: null amount_usd_minimum: 1 api_contract_version: 2.6.0 caller_approved_boolean_is_not_human_proof: true caller_approved_required_for_prepare_and_session: true caller_owned_agent_execution: a2a_remote_skill: false assetfare_server_key_access: false assetfare_server_signing: false assetfare_server_submission: false command: assetfare-agent-runner key_location: caller_wallet_adapter_only minimum_package_version: 1.15.2 package: assetfare-mcp policy_schema: https://assetfare.dev/schemas/caller-owned-execution-policy-v2.json remote_mcp_tool: false scope: caller_process_only supported: true version: assetfare-caller-owned-agent-execution-v2 wallet_adapter_contract_version: assetfare-caller-wallet-adapter-v2 caller_verifies_signs_and_submits: true continuation_v3_enforcement: server_enforced_quote_binding direct_route_summary: assetfare_fee_step_bound: true base_unit_amounts_are_decimal_strings: true classification_values: - direct_protocol_only - external_intent economic_eligibility_is_route_and_amount_conditioned: true external_coverage_only_route_count: 0 external_intent: No public route uses an external intent protocol; provider-internal liquidity sourcing or aggregation remains possible normalized_chain_asset_endpoints: true ordered_provider_path: true primary_direct_route_count: 54 product_classification_values: - primary_direct - external_coverage_only required_on_every_quote: true route_aggregator_used_scope: assetfare_engine_only route_count: 54 server_signing: false server_submission: false step_count: 95 version: assetfare-direct-route-summary-v1 external_intent_protocol: none legacy_prepare_and_session_enforcement: legacy_advisory onchain_deadline_seconds: 240 provider_internal_dex_aggregation_possible: true public: true quote_cache: bounded_thread_safe_process_local_ttl_restart_fails_closed quote_ttl_seconds: 60 receipt_generated_from: decoded_built_action_only receipt_recomputed_before_storage_return_reload_refresh: true relay_used: false release_mode: noncustodial_public_agent_release server_signing: false server_submission: false session_ownership: caller-generated high-entropy opaque capability token (X-AssetFare-Session-Token, >=256-bit CSPRNG) supplied on session create and every read/observe/refresh; the server stores only its sha256 hash, never returns or logs the raw token, and compares in constant time. Idempotency binds to (token_hash, idempotency_key), so a retry recovers a session whose create response was lost and survives an egress-IP change; the network identity is NOT the ownership secret and is used only for capacity/telemetry; sessions created under the retired network-identity model are denied (fail-closed) subjective_safe_boolean_present: false wallet_ready_minimum_remaining_seconds: 120