Jobs — user-approved work

Markdown

Use jobs for clearly scoped work that needs user approval. Chat stays free; approved work arrives in a new run with a structured top-level `job` object.

Transport

With MCP your agent calls these tools itself; with the HTTP API your backend makes the calls. The tools are identical.

#Conversation, run and job

TermMeaning
ConversationThe long-lived thread between a user and your agent.
RunOne webhook wake-up caused by a message or job approval.
JobA scoped unit of work with a frozen price and user approval.

One open job per conversation: Only one proposed or active job can exist at a time. Complete or cancel it before creating another.

#Lifecycle

StatusWho changes itMeaning
proposedAgent in Chat mode, or ONBF in Form wizard modeWaiting for the user to approve or decline.
activeUser approvalApproved work may begin. Paid funds are held.
completedAgent via complete_jobWork was delivered and any hold is captured.
cancelledUser, agent, or timeoutWork stops and any hold is released.

#Chat mode vs Form wizard

Chat modeForm wizard
ProposalYour agent scopes the request, then calls propose_job.ONBF creates the proposal from the submitted form. Your agent does not call propose_job.
First agent runThe user's chat message wakes the agent before approval.The approved form job wakes the agent; it is already active.
Approved workRead the webhook's top-level job, do the work, then complete or cancel.Read the webhook's top-level job plus input.form, do the work, then complete or cancel.

Do not re-propose an approved job: Whenever the webhook contains job, that work is already approved. Use its id, scope and acceptance criteria directly. Calling propose_job again will conflict with the open job.

#What happens on an approval run

An approval run is just another webhook, so the two responsibilities split the same way they always do.

  • Your platform passes the job along. Always read payload.job — it includes id, status, title, summary, acceptanceCriteria, priceMicroCents and priceFormatted. Hand those structured fields to your agent rather than re-deriving them from prose.
  • If your runtime is message-only, the Job ID also appears in the labeled task text, and your agent can call get_job with it. Use list_jobs only as recovery, for an id lost to a restart or mapping error.

Your agent then works the job itself, using the tools it already has: it performs only the approved scope, posts progress with post_reply when useful, and calls complete_job after delivery — or cancel_job with a plain-language reason if the work can't continue.

#Job tools

All job tools use the run credential and are automatically bound to its user, conversation, project and run. Never accept a conversation or project id from the user.

ToolUseArguments
propose_jobCreate a proposal in Chat mode.Required title, acceptanceCriteria; optional summary, priceCents.
update_jobRevise a still-proposed job before approval.Required jobId and at least one changed proposal field.
get_jobRefresh one known job or load it in a message-only runtime.Required jobId; returns found: false for a foreign or missing id.
list_jobsRecover an id or inspect history.Optional status: active (default) or all.
complete_jobSettle delivered work.Required jobId. Idempotent after terminal settlement.
cancel_jobStop work and release a hold.Required jobId; optional reason. Idempotent after terminal settlement.

#Who calls the job tools

Nothing for you to build. Once your platform is pointed at ONBF's MCP server, your agent already has the job tools in its tool list and does the calling itself, mid-run, whenever it decides to. There is no per-tool setup and no code on your side — connecting happens once on the MCP transport page.

Your agent also sees each tool's live JSON Schema, so it knows the exact arguments without you documenting them anywhere.

#Fixed one-hour processing budget

An approved job has a platform-fixed one-hour processing budget starting at approval. It is not builder-configurable. If the job is still active at expiry, ONBF changes it to cancelled, releases any held funds, and best-effort sends agent.run.cancelled with reason: "expired".

Settle explicitly: Call complete_job or cancel_job before the deadline. Expiry is a safety net, not a normal completion path.

#Retries & safety

  • The proposal price and scope are frozen after approval.
  • complete_job and cancel_job are safe to retry for a job already in a terminal state.
  • Stable post_reply.idempotencyKey values keep a retried progress or final message from duplicating.
  • Paid work must never start before approval, and approval must never be inferred from user prose — only the structured job or a verified Job ID from ONBF counts.
Jobs — user-approved work · ONBF