← Back to FO2.app
Agent integrations

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=true plus 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.

  1. Upload the portability bundle through the normal agent transfer flow.
  2. Call GET /api/v1/agent/transfers/:id/portability-inspect to validate the staged bundle, read continuity eligibility, and see profile-aware warnings, blockers, and restore hints.
  3. If inspect returns continuityPass.status = "purchase_required", buy the Continuity Pass for that transfer before expecting FO2-hosted receive, handoff, or restore-planning access.
  4. Call GET /api/v1/agent/transfers/:id/continuity-pass when 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.
  5. Call GET /api/v1/agent/transfers/:id/portability-receive when a target machine needs the manifest-first download plan.
  6. Call GET /api/v1/agent/transfers/:id/portability-handoff to get the primary FO2-hosted markdown instructions for another AI or operator.
  7. Call GET /api/v1/agent/transfers/:id/portability-handoff.json to get the companion structured payload derived from the same handoff model.
  8. After the destination restore actually runs, call POST /api/v1/agent/transfers/:id/portability-restore-report to record the real restore outcome plus validator results and any representative continuation-task results, then read the same route or /continuity-pass again to see whether the transfer reached C3, C4, or C5.
  • The markdown handoff is the primary human-readable and agent-readable artifact. The JSON payload is a companion, not the main UX surface.
  • /portability-inspect stays readable with normal authenticated agent access so the agent can see whether a Continuity Pass is required.
  • /continuity-pass is 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_bundle instead 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.json

Concrete 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

RouteWhat it needsWhat it gives youStrongest honest continuity claim
/portability-inspectA staged manifest-first portability bundle.Bundle inspection, profile match, blockers, rebind/path-remap hints, continuity-pass capability summary.C0 to C2
/continuity-passAgent 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.jsonContinuity Pass plus a real portability bundle.FO2-hosted transport and restore guidance, receive plan, and current claim boundary.C0 to C2
/portability-restore-sessionContinuity 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-executionA 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 routesContinuity 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-reportContinuity 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.

GradeMeaning
C0Transport only. FO2 has files, but blockers still prevent bundle-valid restore claims.
C1Bundle valid. Inspection says the bundle is plausible, but required rebinds or path decisions still block restore readiness.
C2Restore ready at the FO2 transport/contract layer. Receiver execution, validator checks, and task-level continuity are still unproven.
C3Restored. 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.
C4Validated. FO2 has a restore report plus submitted post-restore validator results, and all submitted technical checks passed for that transfer/runtime.
C5Continuity 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-handoff and /portability-handoff.json as 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-node proof 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-receive or execution planning after inspection and rebind requirements are clear.
  • If the target runtime is buhdi-node and import blocks on compatibility, use the restore-execution handoff's recoveryHandoffCommand to 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-session as recoveryHandoff so 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.json and config-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.

  1. Read GET /api/v1/agent-pricing and cache the pricing snapshot.
  2. 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.
  3. Use that bearer token for every subsequent authenticated call in the session.
  4. Send the quoted USDC payment on Base, then confirm it with POST /api/v1/agent-payments/:id/confirm.
  5. Check credit state with GET /api/v1/agent-credits/balance.
  6. Bootstrap or resume the working batch with POST /api/v1/agent/transfers/current.
  7. If you are starting a brand-new job, either archive the old batch with POST /api/v1/agent/transfers/:id/archive or clear staged files with DELETE /api/v1/agent/transfers/:id/files before uploading the next set.
  8. Request signed upload targets with POST /api/v1/agent/transfers/:id/upload-urls.
  9. Upload file bytes to each returned signed target, then finalize with POST /api/v1/agent/transfers/:id/confirm-uploads.
  10. Add one or more recipients with repeated POST /api/v1/agent/transfers/:id/recipients calls.
  11. Preview the send with POST /api/v1/agent/transfers/:id/send-preview.
  12. Send the transfer with POST /api/v1/agent/transfers/:id/send.
  13. Refresh state with GET /api/v1/agent/transfers/:id to 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 transfer

There 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-email to scope billing and transfer ownership by email.
  • Use x-agent-user-id instead 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/month standard, $9.99/month secure.
  • Agent Transfer: $1/GB for normal file or bundle movement.
  • Continuity Pass: $49 per migration, includes the first 20GB on 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/GB transfer meter for the overage.
  • Agent billing bootstrap: start with a $25 USDC top-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-pricing

Create 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_topup and continuity_pass
  • Supported asset: usdc
  • Supported chain: base
  • Supported credit products: transfer_usage_credits and continuity_pass_v1
  • Quote TTL is environment-configured, defaulting to 15 minutes.
  • Minimum top-up is environment-configured, defaulting to 2500 cents.
  • Continuity Pass must currently be quoted at 4900 cents and includes 2000 cents 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/files removes specific file ids, or all staged files when called with {"deleteAll":true}.
  • POST /api/v1/agent/transfers/:id/archive expires 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

  • signedUrl is the supported direct upload target for agent clients.
  • path and token are internal compatibility fields returned alongside the signed URL. External agents should usually treat them as opaque.
  • uploadManifest is FO2’s signed proof of what you were allowed to upload. You must send it back unchanged during upload confirmation.
  • relativePath preserves 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 recipientId or recipientEmail.
  • The recipient email contains a signed FO2 download link that expires at the transfer's expiresAt timestamp, which defaults to 7 days 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 returns 409.

Limits and constraints

ConstraintCurrent behavior
/upload-urls batch sizeMinimum 1 file, maximum 250 file descriptors per request.
Recipient message lengthUp to 2000 characters on recipient save, preview, and send payloads.
Transfer expiryDefault transfer link lifetime is 7 days, controlled by TRANSFER_LINK_TTL_DAYS.
Upload manifest lifetimeFO2 upload manifests currently expire after 10 minutes.
Quote lifetimeEnvironment-configured, default 15 minutes.
Minimum top-upEnvironment-configured, default 2500 cents.
Base confirmationsEnvironment-configured, default 2 confirmations.
File namingFO2 sanitizes stored filenames. Preserve your own logical structure with relativePath.
Encryption modeserver_blind is environment-gated for upload prep, and agent send-link delivery is not yet enabled for that mode.
Transfer size ceilingFO2 does not currently publish or enforce a separate hard size cap for funded agent transfers in route code.
Practical large-job guidanceThe 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 railOnly 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 required AGENT_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.