FO2.app agent integration and AI portability guide
Use FO2.app to price, fund, upload, and send secure deliveries from an agent workflow, and to target the early FO2 portability standard for moving portable AI bundles between machines. This page documents the real v1 route contract, the portability positioning, and the parts that are intentionally still environment-gated.
Current v1 status
- The rail is live for self-serve agent use today: pricing is public, and the first unauthenticated quote call can mint a bearer token for the rest of the session.
- The environment still needs
ENABLE_AGENT_PAYMENTS_INTERNAL=trueplus the agent-payment tables and RPCs applied, because those power the live credit rail. - Human subscription billing uses Stripe. Agent usage funding is a separate prepaid credit rail using USDC on Base.
- Agent billing does not inherit human plan tiers. A funded agent is constrained by prepaid credits plus the current machine contract, not by a Free-vs-Pro human subscription distinction.
- Public agent tokens are isolated under FO2-managed sender aliases so a new agent can fund and deliver immediately without needing an existing owner session.
- Public bearer tokens are intentionally time-bounded, and public transfer routes require a funded prepaid balance before FO2 will issue transfer-state or upload capabilities.
- FO2 currently exposes a stable v1 machine contract for pricing, prepaid credits, signed uploads, recipient management, send preview, send, and transfer-state reads.
- FO2 is now also publishing early public guidance for AI portability and FO2-compatible portable agent bundles through the open spec repo at https://github.com/KrizPB/fo2-portability-spec.
- The portability handoff surfaces now also publish an honest continuity grade, so agents can distinguish transport-only truth from bundle-valid or restore-ready truth instead of overstating what FO2 has proven.
AI portability and FO2-compatible bundles
FO2 is not just a narrow send API. We are intentionally positioning it as an early transport rail for AI portability, agent continuity, and moving useful machine state to a new computer without blindly cloning an entire machine.
The open portability spec gives users and harness authors a public target for what to export, what to exclude, and how a restore should validate portable state before import. That public repo lives at https://github.com/KrizPB/fo2-portability-spec.
- Use the spec when you want to package a portable AI bundle, agent memory export, workflow snapshot, or restore-ready machine migration artifact.
- Use the transfer API on this page when you want to price, fund, upload, preview, and deliver those bundles through FO2.
- The public repo documents include/exclude guidance, restore contract expectations, and the line between the open portability target and FO2's hosted trust, verification, and delivery product.
Concrete portability example
For a real agent portability bundle, package a manifest-first tree such as manifest.json, memory/, workspace/, and any runtime-specific state directories, then request upload URLs with relativePath populated for every file so FO2 preserves the folder structure instead of flattening it.
What FO2 hosts, and what still runs on the destination machine
FO2 is the transfer rail, inspection surface, and handoff publisher. It is not yet the runtime that rehydrates your agent locally.
FO2-hosted responsibilities
- Pricing, funding, bearer bootstrap, and transfer state.
- Bundle upload, delivery, portability inspection, and machine receive planning.
- AI handoff documents, restore-session storage, restore-execution payloads, and continuity reporting.
- Transport-layer truth such as
C0,C1,C2, and post-restore proof recording when the destination reports back.
Destination-machine responsibilities
- Download the bundle, apply rebind values and path remaps, and run the actual runtime import or restore consumer.
- Handle runtime-version compatibility, local secrets, and any matching-runtime repair path.
- Run validator checks and representative continuation tasks against the restored runtime.
- Report the real restore outcome and any cached recovery artifact back to FO2 after local execution.
For buhdi-node, FO2 can hand another agent the exact download and recovery path, but the destination machine still runs commands like portability execution-fetch, portability recovery-handoff, and portability import locally.
Portability inspection and AI handoff routes
Normal FO2 file delivery stays the default path. When you stage a manifest-first portability bundle, FO2 now also exposes profile-aware inspection, machine receive planning, and an AI handoff document pair on top of the same transfer.
- Upload the portability bundle through the normal agent transfer flow.
- Call
GET /api/v1/agent/transfers/:id/portability-inspectto validate the staged bundle, read continuity eligibility, and see profile-aware warnings, blockers, and restore hints. - If inspect returns
continuityPass.status = "purchase_required", buy the Continuity Pass for that transfer before expecting FO2-hosted receive, handoff, or restore-planning access. - Call
GET /api/v1/agent/transfers/:id/continuity-passwhen you want a dedicated machine-readable answer for what continuity surfaces are unlocked on that exact transfer, how many bytes the pass currently covers, and whether any standard-transfer overage is due without parsing the full inspection payload. - Call
GET /api/v1/agent/transfers/:id/portability-receivewhen a target machine needs the manifest-first download plan. - Call
GET /api/v1/agent/transfers/:id/portability-handoffto get the primary FO2-hosted markdown instructions for another AI or operator. - Call
GET /api/v1/agent/transfers/:id/portability-handoff.jsonto get the companion structured payload derived from the same handoff model. - After the destination restore actually runs, call
POST /api/v1/agent/transfers/:id/portability-restore-reportto record the real restore outcome plus validator results and any representative continuation-task results, then read the same route or/continuity-passagain to see whether the transfer reachedC3,C4, orC5.
- The markdown handoff is the primary human-readable and agent-readable artifact. The JSON payload is a companion, not the main UX surface.
/portability-inspectstays readable with normal authenticated agent access so the agent can see whether a Continuity Pass is required./continuity-passis the clean dedicated read route for transfer-scoped continuity capability, allowed routes, included transfer bytes, current staged-byte coverage, overage cents, and now the latest recorded post-restore continuity proof when one exists./portability-handoff,/portability-handoff.json,/portability-receive, and the restore-planning routes require an active transfer-scoped Continuity Pass entitlement.- If the staged transfer does not currently look like a portability bundle, FO2 returns
409 not_portability_bundleinstead of pretending restore guidance exists. - Profile matching can use either the uploaded manifest or profile heuristics. A seeded profile match should be treated as guidance that still needs destination-side proof.
Inspect the staged portability bundle
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-inspect -H 'Authorization: Bearer <agent-access-token>'Read continuity capability for one transfer
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/continuity-pass -H 'Authorization: Bearer <agent-access-token>'Read the machine receive plan
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-receive -H 'Authorization: Bearer <agent-access-token>'Read the primary FO2 AI handoff markdown
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-handoff -H 'Authorization: Bearer <agent-access-token>'Read the companion FO2 AI handoff JSON payload
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-handoff.json -H 'Authorization: Bearer <agent-access-token>'Record the real restore outcome after destination execution
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-restore-report -H 'Authorization: Bearer <agent-access-token>' -H 'Content-Type: application/json' -d '{
"restoreReport": {
"bundleId": "bundle_123",
"status": "restored_successfully",
"restoredFiles": 42,
"bytesRestored": 180000000,
"validationSteps": ["manifest_parses", "memory_db_opens", "runtime_smoke_passes"]
},
"validatorResults": [
{ "key": "memory_db_opens", "status": "passed" },
{ "key": "runtime_smoke_passes", "status": "passed" }
],
"continuationTaskResults": [
{ "key": "resume_workflow_v1", "status": "passed", "detail": "Representative next task completed on the restored runtime." }
]
}'Literal end-to-end portability restore example
This is the honest sequence today: FO2 hosts inspection, handoff, and reporting, while the destination machine still performs the actual restore/import work locally.
# Source machine: upload a manifest-first portability bundle into FO2
curl -s "$FO2_BASE/api/v1/agent/transfers/<transfer-id>/portability-inspect" -H "Authorization: Bearer $FO2_AGENT_TOKEN"
# If continuityPass.status says purchase_required, buy it for this exact transfer first.
curl -s "$FO2_BASE/api/v1/agent/transfers/<transfer-id>/portability-handoff" -H "Authorization: Bearer $FO2_AGENT_TOKEN"
# Destination machine: materialize the bundle and inspect local recovery truth.
buhdi-node portability execution-fetch restore-execution.json --write-json restore-plan.json
buhdi-node portability recovery-handoff <bundle-dir> --write-json portability-recovery-handoff.json
# Report the destination-derived recovery artifact back to FO2 for retry/routing truth.
curl -s "$FO2_BASE/api/v1/agent/transfers/<transfer-id>/portability-restore-session" -X POST -H "Authorization: Bearer $FO2_AGENT_TOKEN" -H "Content-Type: application/json" -d @restore-session-update.json
# After local import and validation actually run, record the real restore outcome.
curl -s "$FO2_BASE/api/v1/agent/transfers/<transfer-id>/portability-restore-report" -X POST -H "Authorization: Bearer $FO2_AGENT_TOKEN" -H "Content-Type: application/json" -d @restore-report.jsonConcrete restore-session request
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-restore-session -X POST -H 'Authorization: Bearer <agent-access-token>' -H 'Content-Type: application/json' -d '{
"bundleDir": "/Users/example/restore-target/portable-bundle",
"rebindValues": {
"openai_api_key": "<destination-secret>",
"sync_api_token": "<destination-secret>"
},
"pathRemaps": {
"workspaceDir": "/Users/example/restore-target/workspace",
"vaultDir": "/Users/example/restore-target/vault"
}
}'Representative restore-execution payload
{
"ok": true,
"portabilityRestoreExecution": {
"targetRuntime": "buhdi-node",
"readyForImport": false,
"bundleId": "bundle_123",
"bundleDir": "/Users/example/restore-target/portable-bundle",
"restorePacket": {
"version": 1,
"transferId": "transfer_123",
"bundleId": "bundle_123",
"targetRuntime": "buhdi-node",
"bundleDir": "/Users/example/restore-target/portable-bundle",
"rebindValues": {
"openai_api_key": "<destination-secret>",
"sync_api_token": "<destination-secret>"
},
"pathRemaps": {
"workspaceDir": "/Users/example/restore-target/workspace",
"vaultDir": "/Users/example/restore-target/vault"
}
},
"commandPreview": [
"buhdi-node portability validate /Users/example/restore-target/portable-bundle",
"buhdi-node portability recovery-handoff /Users/example/restore-target/portable-bundle --write-json portability-recovery-handoff.json",
"buhdi-node portability plan /Users/example/restore-target/portable-bundle",
"buhdi-node portability import /Users/example/restore-target/portable-bundle"
],
"recoveryHandoffCommand": "buhdi-node portability recovery-handoff /Users/example/restore-target/portable-bundle --write-json portability-recovery-handoff.json"
}
}Destination-reported recovery handoff reportback
If the destination runtime cannot import cleanly, post the stable buhdi_node_portability_recovery_handoff artifact back into the restore session. FO2 will cache it and re-emit it on later restore-execution reads for retry or routing.
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-restore-session -X POST -H 'Authorization: Bearer <agent-access-token>' -H 'Content-Type: application/json' -d '{
"bundleDir": "/Users/example/restore-target/portable-bundle",
"recoveryHandoff": {
"version": 1,
"kind": "buhdi_node_portability_recovery_handoff",
"bundleDir": "/Users/example/restore-target/portable-bundle",
"bundleId": "bundle_123",
"currentRuntimeVersion": "0.3.0",
"currentValidation": {
"ok": false,
"errors": [
"Pre-1.0 compatibility mismatch: bundle 0.4.0 expects buhdi-node 0.4.x, current runtime is 0.3.0."
]
},
"repairPlan": {
"strategy": "upgrade_destination_then_reexport",
"canImportOnCurrentRuntime": false
}
}
}'Concrete restore-report payload
This is the step that turns FO2 continuity claims from restore-ready theory into recorded proof. In practice, restore success alone earns the restored tier, validator results are what let FO2 claim validated behavior, and representative continuation-task results are what push it to real C5 workflow proof.
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/portability-restore-report -X POST -H 'Authorization: Bearer <agent-access-token>' -H 'Content-Type: application/json' -d '{
"restoreReport": {
"targetRuntime": "buhdi-node",
"bundleId": "bundle_123",
"restoredAt": "2026-04-21T22:35:00.000Z",
"restoredFiles": 412,
"bytesRestored": 89423312,
"status": "restored_successfully",
"warnings": [],
"validationSteps": [
"manifest validated",
"required rebinds applied",
"import completed"
],
"appliedRebindKeys": ["openai_api_key", "sync_api_token"],
"unresolvedRebinds": []
},
"validatorResults": [
{
"key": "buhdi_node_post_restore_validator_pack_v1",
"label": "Buhdi node post-restore validator pack",
"status": "passed",
"detail": "Config, vault access, and runtime startup checks passed."
}
],
"continuationTaskResults": [
{
"key": "round_trip_export",
"label": "Round-trip export proof",
"status": "passed",
"detail": "Restored runtime exported a normalized next-hop bundle."
}
]
}'Common portability failure cases
409 not_portability_bundle
FO2 did not find a real manifest-first portability bundle in the staged transfer. Restage the files with a real portability manifest and preserved relativePath structure before calling inspect, handoff, receive, or restore routes again.
402 continuity_pass_required
You reached a FO2-hosted continuity surface, but that exact transfer does not have an active Continuity Pass yet. Quote and confirm the pass for the same transferId, then retry the route.
409 restore_session_required
/portability-restore-execution needs stored destination context first. Post bundleDir plus any rebind values or path remaps to POST /api/v1/agent/transfers/:id/portability-restore-session before asking FO2 for the execution handoff.
400 invalid_restore_session_request
The restore-session payload did not pass schema validation. The most common current cause is an oversized recoveryHandoff artifact, which FO2 caps at 64KB. Trim the payload to the stable handoff JSON only and retry.
402 continuity_overage_payment_required
The transfer is larger than the Continuity Pass included 20GB. Add normal transfer credits for the overage before downloading bundle files from the portability receive surface.
409 continuity_transfer_changed_after_billing
The staged transfer grew after an overage debit was already applied. Archive or restage the bundle as a new transfer instead of assuming the old billed state is still valid.
503 portability_restore_session_unavailable or 503 portability_restore_report_unavailable
The storage surface for restore-session or restore-report data is unavailable, usually because the required migration is not applied in that environment yet. On self-hosted or preview environments, apply the matching portability migration before treating the restore/report flow as ready.
400 invalid_billing_scope
Your agent auth path did not resolve to a valid owner email. Fix the scoped bearer token or internal user header first, because FO2 stores restore-session and restore-report state per transfer plus owner identity.
Portability route matrix
| Route | What it needs | What it gives you | Strongest honest continuity claim |
|---|---|---|---|
/portability-inspect | A staged manifest-first portability bundle. | Bundle inspection, profile match, blockers, rebind/path-remap hints, continuity-pass capability summary. | C0 to C2 |
/continuity-pass | Agent auth for the transfer. No restore proof required. | Whether the pass is active, what it unlocks, and staged-byte overage state. | No grade change, capability only |
/portability-handoff and /portability-handoff.json | Continuity Pass plus a real portability bundle. | FO2-hosted transport and restore guidance, receive plan, and current claim boundary. | C0 to C2 |
/portability-restore-session | Continuity Pass plus destination-local values like bundleDir, rebinds, path remaps, or reported recovery handoff JSON. | Encrypted-at-rest restore packet summary and recomputed restore draft. | C2 at best, still pre-import |
/portability-restore-execution | A stored restore session plus Continuity Pass. | Machine-ready restore packet, receive sequence, command preview, and any cached destination recovery handoff. | C2 at best, still pre-import |
/portability-receive and file download routes | Continuity Pass, and extra prepaid credits if the bundle exceeds the included 20GB. | Manifest-first download sequence and short-lived signed file URLs. | No grade change, transport only |
/portability-restore-report | Continuity Pass plus real destination restore results, validator results, and optional continuation-task proof. | The recorded restore outcome that can move the transfer from restored to validated to workflow-proven. | C3, C4, or C5 depending on proof submitted |
Continuity truth model for agents
FO2 uses a simple continuity grade so agents do not confuse file movement with actual restored readiness.
| Grade | Meaning |
|---|---|
C0 | Transport only. FO2 has files, but blockers still prevent bundle-valid restore claims. |
C1 | Bundle valid. Inspection says the bundle is plausible, but required rebinds or path decisions still block restore readiness. |
C2 | Restore ready at the FO2 transport/contract layer. Receiver execution, validator checks, and task-level continuity are still unproven. |
C3 | Restored. A destination restore ran without fatal errors and FO2 has a machine-readable restore report for that transfer, but validator-backed technical proof is still incomplete. |
C4 | Validated. FO2 has a restore report plus submitted post-restore validator results, and all submitted technical checks passed for that transfer/runtime. |
C5 | Continuity proven for one supported workflow. FO2 has a restore report, passed technical validators, and at least one submitted representative continuation-task result for that exact runtime and transfer. |
- Treat the continuity grade in
/portability-handoffand/portability-handoff.jsonas the current claim boundary. - If the handoff says
C0, clear blockers before planning restore. - If the handoff says
C1, finish the required rebind or remap work before calling the bundle restore-ready. - If the handoff says
C2, you still owe receiver execution plus validation. Do not tell the human continuity is proven yet. - If the transfer reaches
C3, restore execution happened, but FO2 still has not earned validated continuity unless the submitted technical checks also pass. - If the transfer reaches
C4, FO2 has both a restore outcome and passed submitted validator results for that runtime/transfer pair. - If the transfer reaches
C5, FO2 also has at least one representative continuation-task result for that runtime/transfer pair. Treat that as workflow-specific proof, not a blanket guarantee for every possible job. - Current local
buhdi-nodeproof now covers seventeen representative continuation workflows, including resumed workspace/vault/media state, local chat/sync/bootstrap state, restored SQLite reopen-and-mutate lanes, real memory/run-store helper lanes, four-layer memory plus graph-context lanes, round-trip export and rebind-hygiene proofs, next-hop restore proof, compatibility/version gating, recovery-guidance and recovery-execution proofs, machine-readable repair planning, and matching-runtime recovery back onto the current runtime.
Portability handoff by harness type
The human web UI stays focused on traditional file sharing. If a user tells you they uploaded a portability bundle through FO2, start here, not in the dashboard.
OpenClaw or other tool-using assistants
- Read this page first, then inspect the staged transfer with
GET /api/v1/agent/transfers/:id/portability-inspect. - Prefer the FO2 markdown handoff route as the primary restore brief for another agent or operator.
- Use the JSON handoff only as a companion when your toolchain wants structured fields.
Read https://www.fo2.app/for-agents first, then read the FO2 portability handoff markdown for this transfer. Use the handoff's continuity grade, warnings, blockers, required rebinds, and path decisions to guide restore work. Ask me only for required rebinds or path choices, and do not claim continuity until restore plus validation pass.Codex, Claude Code, Cursor, or terminal harnesses
- Treat FO2 as the transfer rail, not the restore executor.
- Download or read
/portability-handoff, then pass that markdown into the harness as the restore brief. - Only move to
/portability-receiveor execution planning after inspection and rebind requirements are clear. - If the target runtime is
buhdi-nodeand import blocks on compatibility, use the restore-execution handoff'srecoveryHandoffCommandto write a stable local JSON repair artifact before switching runtimes or re-exporting. - After the bundle is downloaded locally, report that versioned recovery JSON back to
POST /api/v1/agent/transfers/:id/portability-restore-sessionasrecoveryHandoffso FO2 can cache the destination-derived repair state for retry/routing.
Read this FO2 portability handoff and restore the selected runtime safely. Follow the continuity truth, warnings, blockers, and required rebind/path decisions. Use the receive plan only if you need to materialize the bundle locally. Do not claim success until the runtime-specific restore and validation steps pass.Generic chat AI with no direct API tooling
- Have the human share this page plus the FO2 handoff markdown output.
- Do not ask the human to reason from raw bundle clues like
manifest.jsonandconfig-root/alone. - The goal is for the AI to read the FO2-hosted instructions, not for the human to reverse engineer the portability bundle structure.
Quick start example
If you want the shortest path to a first successful integration, follow this exact order.
- Read
GET /api/v1/agent-pricingand cache the pricing snapshot. - Create a top-up quote with
POST /api/v1/agent-payments/quote. If you do not already have a bearer token, FO2 returns one in the quote response. - Use that bearer token for every subsequent authenticated call in the session.
- Send the quoted USDC payment on Base, then confirm it with
POST /api/v1/agent-payments/:id/confirm. - Check credit state with
GET /api/v1/agent-credits/balance. - Bootstrap or resume the working batch with
POST /api/v1/agent/transfers/current. - If you are starting a brand-new job, either archive the old batch with
POST /api/v1/agent/transfers/:id/archiveor clear staged files withDELETE /api/v1/agent/transfers/:id/filesbefore uploading the next set. - Request signed upload targets with
POST /api/v1/agent/transfers/:id/upload-urls. - Upload file bytes to each returned signed target, then finalize with
POST /api/v1/agent/transfers/:id/confirm-uploads. - Add one or more recipients with repeated
POST /api/v1/agent/transfers/:id/recipientscalls. - Preview the send with
POST /api/v1/agent/transfers/:id/send-preview. - Send the transfer with
POST /api/v1/agent/transfers/:id/send. - Refresh state with
GET /api/v1/agent/transfers/:idto inspect recipients, delivery summary, and remaining credits.
export FO2_BASE="https://www.fo2.app"
# 1. Read pricing (public)
curl -s "$FO2_BASE/api/v1/agent-pricing"
# 2. Create a USDC top-up quote. If you do not send auth yet,
# FO2 will mint a bearer token in response.auth.token.
QUOTE=$(curl -s "$FO2_BASE/api/v1/agent-payments/quote" -X POST -H "content-type: application/json" -d '{"amountUsdCents":2500}')
export FO2_AGENT_TOKEN="<token-from-response.auth.token>"
# 3. Pay the quote on Base, then confirm with txHash
# 4. Bootstrap the active transfer workspace
curl -s "$FO2_BASE/api/v1/agent/transfers/current" -X POST -H "Authorization: Bearer $FO2_AGENT_TOKEN"
# 5. Request upload URLs, upload file bytes, then confirm uploads
# 6. Save recipients, preview the send, then send the transferThere is no separate “create transfer” body in v1. FO2 creates or resumes the current active transfer workspace with POST /api/v1/agent/transfers/current. That means the latest non-expired batch is reused until you explicitly archive it or delete its files.
Authentication modes
1. Public bearer token
The first unauthenticated quote call can mint a bearer token for a new agent session. Reuse that token across confirm, balance, transfer, upload, preview, and send calls. FO2 expects you to fund credits before using the transfer surface.
Authorization: Bearer <agent-access-token>2. Owner session
For browser-attached tooling or same-session automation, a normal FO2 owner session can authorize agent routes once the agent rail is enabled in that environment.
3. Internal secret headers
For controlled server-to-server automation, FO2 still supports the internal secret plus a user scope header.
- Use
x-agent-user-emailto scope billing and transfer ownership by email. - Use
x-agent-user-idinstead when you already have the FO2 user UUID. - If both are absent on the internal path, FO2 rejects the request with
400 invalid_billing_scope. - Do not ship the internal secret to browsers or untrusted clients.
Funding and payment flow
Pricing and when to use each path
- Human web UI:
$4.99/monthstandard,$9.99/monthsecure. - Agent Transfer:
$1/GBfor normal file or bundle movement. - Continuity Pass:
$49per migration, includes the first20GBon that exact transfer, and is the right path when the goal is to move an agent or runtime to a new machine with portability plus continuity workflow. If the staged transfer is larger, FO2 falls back to the normal$1/GBtransfer meter for the overage. - Agent billing bootstrap: start with a
$25 USDCtop-up before agent billing can run.
On this page, agents should present these as clear options, not as a hard upsell. If the human only needs file movement, use the normal transfer rail. If the human wants the same agent to continue on the new machine, point them to Continuity Pass.
Recommended response pattern for agents
I can use FO2 in three ways: human web plans are $4.99/month standard or $9.99/month secure, agent transfer is $1/GB for general file or bundle movement, and FO2 Continuity Pass is $49 per migration with 20GB included when the goal is to move an agent or runtime to a new machine. Agent billing starts with a $25 USDC top-up. Tell me which path you want.FO2 v1 agent billing is prepaid and volume-based. Usage is currently billed in 10,000,000-byte blocks at 1 cent per block, which is roughly $1 / GB.
Read pricing
curl -s https://www.fo2.app/api/v1/agent-pricingCreate a quote
curl -s https://www.fo2.app/api/v1/agent-payments/quote -X POST -H 'content-type: application/json' -d '{
"amountUsdCents": 2500
}'{
"ok": true,
"paymentIntent": {
"id": "8c7d...",
"purpose": "usage_topup",
"creditProduct": "transfer_usage_credits",
"status": "pending",
"asset": "usdc",
"chain": "base",
"amountUsdCents": 2500,
"creditGrantCents": 2500,
"includedTransferBytes": 0,
"amountTokenMinor": "25000000",
"tokenDecimals": 6,
"reference": "fo2_ref_...",
"expiresAt": "2026-04-18T05:30:00.000Z",
"quotedAt": "2026-04-18T05:15:00.000Z",
"paidAt": null,
"productContext": {
"transferId": null,
"runtimeProfile": null
}
},
"instructions": {
"recipientAddress": "0x6d908d52C838dF89B5461cEFA55269b853B813ee",
"chainId": 8453,
"asset": "usdc",
"tokenContract": "0x833589fCD6EDB6E08f4c7C32D4f71b54bDa02913",
"amountTokenMinor": "25000000",
"reference": "fo2_ref_...",
"memo": null
},
"auth": {
"mode": "bearer",
"token": "fo2agt_...",
"header": "Authorization: Bearer fo2agt_...",
"ownerAlias": "agent+8c7d...@fo2.app"
}
}If you called quote without auth, persist response.auth.token and send it back as a bearer token on every later request. That token is time-bounded, so refresh your session by creating a new quote if it expires.
Create a Continuity Pass quote for one transfer
curl -s https://www.fo2.app/api/v1/agent-payments/quote -X POST -H 'content-type: application/json' -H 'Authorization: Bearer <agent-access-token>' -d '{
"purpose": "continuity_pass",
"creditProduct": "continuity_pass_v1",
"amountUsdCents": 4900,
"metadata": {
"transferId": "<transfer-id>",
"runtimeProfile": "openclaw"
}
}'Continuity Pass is transfer-scoped. Quote it with the exact transferId you want FO2 to unlock, then confirm payment on the normal Base USDC rail. After confirm, inspect, /api/v1/agent/transfers/:id/continuity-pass, and payment-status responses expose the active continuity capability block for that transfer. FO2 measures the first 20GB against that transfer itself, then uses normal transfer credits only for any overage.
Confirm the payment
curl -s https://www.fo2.app/api/v1/agent-payments/<payment-intent-id>/confirm -X POST -H 'content-type: application/json' -H 'Authorization: Bearer <agent-access-token>' -d '{
"txHash": "0xabc123..."
}'The confirm route verifies the Base USDC transfer, records a payment receipt, and credits the scoped account when the observed transaction matches the quote.
In practice that means FO2 checks the quoted payment on Base, waits for the required confirmations, rejects duplicate tx hashes, and treats mismatched recipient, amount, asset/reference, or other receipt details as a failed match instead of crediting optimistically.
Read remaining credit balance
{
"ok": true,
"creditAccount": {
"id": "credit_acct_...",
"currency": "usd_credits",
"balanceCents": 2400,
"lifetimeCreditedCents": 2500,
"lifetimeDebitedCents": 100,
"status": "active",
"lastActivityAt": "2026-04-18T05:21:00.000Z"
}
}- Supported purposes:
usage_topupandcontinuity_pass - Supported asset:
usdc - Supported chain:
base - Supported credit products:
transfer_usage_creditsandcontinuity_pass_v1 - Quote TTL is environment-configured, defaulting to
15minutes. - Minimum top-up is environment-configured, defaulting to
2500cents. - Continuity Pass must currently be quoted at
4900cents and includes2000cents of transfer credits plus transfer-scoped continuity entitlement. - Verification currently expects Base USDC transfer details to match the quoted recipient, token contract, amount, and confirmation state.
Transfer bootstrap and state reads
Create or resume the active transfer workspace
curl -s https://www.fo2.app/api/v1/agent/transfers/current -X POST -H 'Authorization: Bearer <agent-access-token>'{
"ok": true,
"transfer": {
"id": "transfer_...",
"title": "File Transfer",
"status": "draft",
"ownerEmail": "agent+8c7d...@fo2.app",
"recipientEmail": null,
"expiresAt": "2026-04-25T05:15:00.000Z",
"encryptionMode": "none",
"cryptoState": "none",
"fileCount": 0,
"totalBytes": 0,
"deliverySummary": {
"totalRecipients": 0,
"sentRecipients": 0,
"openedRecipients": 0,
"downloadedRecipients": 0,
"totalOpens": 0,
"totalDownloads": 0,
"lastOpenedAt": null,
"lastDownloadedAt": null
},
"files": [],
"recipients": [],
"agentBilling": {
"mode": "prepaid_credits",
"meterKey": "bytes_transferred",
"unit": "bytes",
"pricedCents": 0,
"creditAccount": {
"id": "credit_acct_...",
"currency": "usd_credits",
"balanceCents": 2500,
"lifetimeCreditedCents": 2500,
"lifetimeDebitedCents": 0,
"status": "active",
"lastActivityAt": "2026-04-18T05:20:00.000Z"
}
}
}
}POST /api/v1/agent/transfers/current returns a transfer snapshot with files, recipients, delivery summary, and the current priced send quote for that batch.
Batch cleanup and rollover
DELETE /api/v1/agent/transfers/:id/filesremoves specific file ids, or all staged files when called with{"deleteAll":true}.POST /api/v1/agent/transfers/:id/archiveexpires the current batch and immediately returns a fresh active transfer snapshot for the next job.- These cleanup routes intentionally use authenticated agent access, not funded-only access, so agents can recover from stale batches even when credits are low.
Upload flow and mechanics
Prepare signed upload targets
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/upload-urls \
-X POST \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <agent-access-token>' \
-d '{
"files": [
{
"name": "records/batch-001.pdf",
"relativePath": "records/batch-001.pdf",
"type": "application/pdf",
"size": 4812390
}
],
"encryptionMode": "none"
}'{
"ok": true,
"items": [
{
"originalFileName": "records/batch-001.pdf",
"fileName": "records-batch-001.pdf",
"relativePath": "records/batch-001.pdf",
"contentType": "application/pdf",
"sizeBytes": 4812390,
"storagePath": "<transfer-id>/records-batch-001.pdf",
"token": "...",
"signedUrl": "https://...",
"path": "<transfer-id>/records-batch-001.pdf",
"uploadManifest": "eyJ..."
}
]
}What the upload response fields mean
signedUrlis the supported direct upload target for agent clients.pathandtokenare internal compatibility fields returned alongside the signed URL. External agents should usually treat them as opaque.uploadManifestis FO2’s signed proof of what you were allowed to upload. You must send it back unchanged during upload confirmation.relativePathpreserves your logical folder structure even though FO2 sanitizes the stored file name.
Upload the raw file bytes
FO2 v1 exposes signed direct uploads. Agent clients should upload the file body with a PUT to the returned signedUrl.
curl -X PUT '<signedUrl-from-upload-urls>' \
-H 'content-type: application/pdf' \
--data-binary '@/local/path/records/batch-001.pdf'Confirm uploaded files
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/confirm-uploads \
-X POST \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <agent-access-token>' \
-d '{
"files": [
{
"uploadManifest": "<upload-manifest-from-upload-urls>"
}
]
}'You must send back the exact uploadManifest value returned from the upload-preparation step. FO2 uses that signed manifest to bind the file metadata to the transfer before recording usage.
- If an upload token or manifest expires, request fresh upload URLs and re-upload that file.
- FO2 currently exposes signed direct uploads, not a dedicated resumable TUS contract.
- For larger jobs, split work into smaller transfers or smaller upload batches until FO2 exposes resumable agent uploads.
Recipient and delivery flow
Save one recipient
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/recipients \
-X POST \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <agent-access-token>' \
-d '{
"email": "recipient@example.com",
"message": "Your secure file delivery is ready."
}'To add multiple recipients, call the same route once per recipient email. Use GET /api/v1/agent/transfers/:id/recipients to list the current saved set, and DELETE to remove one by recipientId.
{
"ok": true,
"recipients": [
{
"id": "recipient_...",
"email": "recipient@example.com",
"status": "pending",
"message": "Your secure file delivery is ready.",
"sentAt": null,
"openedAt": null,
"downloadedAt": null,
"openCount": 0,
"downloadCount": 0
}
]
}Preview the send
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/send-preview \
-X POST \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <agent-access-token>' \
-d '{
"recipientEmail": "recipient@example.com"
}'{
"ok": true,
"preview": {
"transferId": "transfer_...",
"recipientEmail": "recipient@example.com",
"message": "",
"fileCount": 1,
"totalBytes": 4812390,
"expiresAt": "2026-04-25T05:15:00.000Z",
"policy": {
"allowed": true,
"requiresApproval": false,
"policyMode": "manual_send",
"reasons": []
},
"agentBilling": {
"mode": "prepaid_credits",
"meterKey": "bytes_transferred",
"unit": "bytes",
"requiredCents": 1,
"creditAccount": {
"id": "credit_acct_...",
"currency": "usd_credits",
"balanceCents": 2500,
"lifetimeCreditedCents": 2500,
"lifetimeDebitedCents": 0,
"status": "active",
"lastActivityAt": "2026-04-18T05:20:00.000Z"
}
}
}
}The preview route resolves the actual recipient it would use, shows the transfer expiry timestamp, and returns the billed-delivery preview before a live send is attempted.
Send the transfer
curl -s https://www.fo2.app/api/v1/agent/transfers/<transfer-id>/send \
-X POST \
-H 'content-type: application/json' \
-H 'Authorization: Bearer <agent-access-token>' \
-d '{
"recipientEmail": "recipient@example.com"
}'{
"ok": true,
"agentBilling": {
"mode": "prepaid_credits",
"quote": {
"meterKey": "bytes_transferred",
"unit": "bytes",
"totalBytes": 4812390,
"pricedCents": 1
},
"debit": {
"usageEventId": "usage_evt_...",
"status": "applied",
"creditAccountId": "credit_acct_...",
"ledgerEntryId": "ledger_...",
"debitedCents": 1,
"balanceCents": 2499
},
"creditAccount": {
"id": "credit_acct_...",
"currency": "usd_credits",
"balanceCents": 2499,
"lifetimeCreditedCents": 2500,
"lifetimeDebitedCents": 1,
"status": "active",
"lastActivityAt": "2026-04-18T05:31:00.000Z"
}
},
"resolvedRecipient": {
"email": "recipient@example.com",
"message": ""
}
}A successful send debits prepaid credits, emails the recipient a signed download link, and returns the resolved recipient plus updated billing state. Public agent-token sends use an FO2-managed sender identity instead of a caller-supplied email address.
- If you provide
recipientEmail, FO2 uses it directly. - If you provide
recipientId, FO2 resolves the saved recipient on that transfer. - If exactly one saved recipient exists, send-preview and send can omit both fields.
- If more than one saved recipient exists, the caller must specify
recipientIdorrecipientEmail. - The recipient email contains a signed FO2 download link that expires at the transfer's
expiresAttimestamp, which defaults to7days unless the environment overrides it. - Recipients get a simple signed download page. No FO2 login is required on the recipient side for the normal v1 flow.
- Transfer snapshots expose recipient status, open/download timestamps, per-recipient counters, and aggregate delivery-summary counts.
- There is no public v1 webhook or callback contract for recipient opens, downloads, payment settlement, or send completion yet.
- Agent send currently supports plaintext delivery only. If the transfer uses
server_blind, the send route returns409.
Limits and constraints
| Constraint | Current behavior |
|---|---|
/upload-urls batch size | Minimum 1 file, maximum 250 file descriptors per request. |
| Recipient message length | Up to 2000 characters on recipient save, preview, and send payloads. |
| Transfer expiry | Default transfer link lifetime is 7 days, controlled by TRANSFER_LINK_TTL_DAYS. |
| Upload manifest lifetime | FO2 upload manifests currently expire after 10 minutes. |
| Quote lifetime | Environment-configured, default 15 minutes. |
| Minimum top-up | Environment-configured, default 2500 cents. |
| Base confirmations | Environment-configured, default 2 confirmations. |
| File naming | FO2 sanitizes stored filenames. Preserve your own logical structure with relativePath. |
| Encryption mode | server_blind is environment-gated for upload prep, and agent send-link delivery is not yet enabled for that mode. |
| Transfer size ceiling | FO2 does not currently publish or enforce a separate hard size cap for funded agent transfers in route code. |
| Practical large-job guidance | The real constraints today are prepaid credit balance, 250 files per /upload-urls call, the 10-minute upload-manifest window, and the fact that agent uploads are direct signed uploads without resumable TUS support yet. For very large jobs, batch the work and request fresh upload URLs if needed. |
| Payment rail | Only Base USDC top-ups are supported for v1 agent credits. |
Error handling and retry guidance
Most v1 agent endpoints return JSON shaped like { error: { code, message } }. A few send and preview failure paths inherited from the human route can still return a plain string error. Treat the envelope as mixed for now.
{
"error": {
"code": "amount_below_minimum",
"message": "Top-up amount is below the minimum allowed value."
}
}{
"error": "Upload files before sending the access link."
}404 agent_payments_disabled
The environment has not enabled the agent rail. Turn on the feature flag before retrying.
401 unauthorized
Your owner session is missing, expired, or the internal secret is wrong.
400 invalid_billing_scope
Your request did not include a valid scoped user email or user ID.
400 amount_below_minimum or unsupported_*
Fix the request and retry. These are caller-shape errors, not transient platform failures.
409 intent_expired or intent_not_pending
Discard the old quote and create a new payment intent.
409 payment_mismatch or duplicate_receipt
Do not blindly retry with the same quote. Inspect the transaction, amount, recipient, and token details first.
409 no_active_credit_account or insufficient_credits
Top up credits before retrying the billed send flow.
Send failed after billing preview or debit
Treat send as non-idempotent until you inspect the returned transfer state. FO2 currently includes a compensating refund safeguard when a billed send fails after debit but before the transfer is actually committed as sent, so do not immediately assume credits were permanently burned.
409 missing_recipient, recipient_selection_required, or recipient_not_found
Either create a recipient first or specify exactly which saved recipient should receive the send.
403 encryption_disabled
You requested server_blind uploads in an environment where encrypted agent uploads are not enabled.
Missing tables or RPCs
If quote, confirm, balance, or debit calls return messages about unavailable agent-payment tables or RPCs, apply the required internal agent-payment database migrations before treating that environment as integration-ready.
Testing and sandbox guidance
- There is no public mock or sandbox USDC rail documented for v1 right now.
- For safe testing, use a non-production FO2 environment, enable
ENABLE_AGENT_PAYMENTS_INTERNAL, configure the requiredAGENT_PAYMENTS_*values, and apply the agent-payment database migrations first. - Use small top-up quotes and small files for your first integration pass, then scale up after you verify funding, upload, confirm, preview, and send behavior.
- For very large jobs, do not assume resumability yet. Batch the upload work so expired upload windows or network interruptions only force you to retry a smaller slice.
What is public, and what is still internal-only
- Documented v1 contract: pricing, quote, confirm, balance, transfer bootstrap, transfer-state reads, upload preparation, upload confirmation, recipient management, send preview, and send.
- Environment-gated: the whole agent-payment and agent-transfer rail currently requires the target environment to enable it explicitly.
- Internal-only auth path: the secret-header flow is for trusted server-to-server automation and should be treated as private infrastructure.
- Separate human path: dashboard subscriptions, plan upgrades, and Stripe customer-portal flows are human billing flows, not part of agent prepaid credits.
Integration help
If you want a pinned server-to-server contract, rollout help for the agent-payment migrations, or a language-specific example for your stack, email support@fo2.app.