Agent tasks and approvals

How to bill a fixed-price coding task with AI assistance while keeping human approval

Agree the price with your client, then run the work as a Billr task against a pricing policy a person approved. The agent can create, start, heartbeat, pause and complete the task and report provider costs, but it cannot approve pricing or invoice anything. Completion freezes the record, get_task_billing_preview shows the amounts, and a person reviews it in the app before the invoice is sent.

Written and reviewed by the Billr team. Published , last reviewed .

The short version

A frozen task record showing active time and reported provider cost under an approved rate, with pricing and the invoice decision still in human hands.

What you need first

  • A pricing policy for the client, approved in the app by an owner or admin: currency, hourly rate, usage multiplier and a per-task budget.
  • A key with the Read and draft profile, which is the profile that covers every task tool.
  • Provider cost details you can actually cite, such as the provider, model, request reference and amount.

Cautions

  • Agents cannot create or approve pricing policies. A person sets the rate before any task exists.
  • Reported usage is what the agent says the provider charged. Billr records the claim and its source, it does not independently verify the invoice from your model provider.
  • Completing a task cannot be undone. Log every usage row before you complete it.

Who this is for: Developers who quote agent-assisted work at a fixed price and need an auditable record of what the work actually took.

list_billing_policiescreate_taskstart_taskheartbeat_taskpause_taskrecord_task_usagecomplete_taskget_task_billing_previewupdate_draft_invoicesend_invoice

Keep the two numbers separate

A fixed-price job has two numbers, and both are billing figures. The first is the flat price you agreed with the client. The second is what the approved policy computes from the task: measured active time at the policy rate, plus reported provider usage multiplied by the usage multiplier.

Billr produces the second number. It is not a statement of your actual costs, your margin, or your profit: the rate and the multiplier are whatever a person approved, and the usage rows are reported provider charges. What the record gives you is an auditable comparison, so the next fixed-price quote can be based on something other than memory.

The permission boundary is what makes this usable with an agent in the loop. An agent can run the work and report usage. It cannot set the rate, approve the pricing for this task, or release it for invoicing. Ordinary invoices are a different matter: with a Read and draft key an agent can create and edit drafts, including the one this approval produces, and with the Billing profile it can also send invoices and record payments.

Who decides what on a fixed-price task

Human boundary

  • Approve the pricing policy: rate, usage multiplier, currency, budget.
  • Review the completed task and approve it for invoicing.
  • Decide whether that draft goes out as it stands.

Agent boundary

  • Read the approved policies, then create a paused task with a title.
  • Start, heartbeat every 30 to 60 seconds, pause while waiting.
  • Report each provider charge with a reference and a source.
  • Complete the task and read the billing preview.

Nothing crosses the middle line on its own. Setting the rate and approving the task for invoicing are human steps; the agent runs the work, reports usage, and can adjust the draft while a person decides what leaves the building.

The agent can run the work and report what it cost. Approving the rate and approving the invoice are human steps, and completion freezes the record for that review.

Have a person approve the rate before any work runs

Task billing starts in the app. An owner or admin creates a policy for a client with a currency, an hourly rate, a usage multiplier and a budget per task. Existing tasks keep the policy they were created under, so changing a rate later does not rewrite history; it creates a new rate for new work.

The agent then reads the policies and picks the right one. Pricing it cannot see is pricing it cannot bill against, which is the whole point of the boundary. The scope what an agent may do guide explains which key profile covers which tools.

list_billing_policies

Example
list_billing_policies  { "business_id": "b1f2c3d4-5e67-4a89-9b01-2c3d4e5f6a7b" }

The response returns the policies a person approved plus a review_url pointing at the app page where pricing is managed.

A budget is per task

The budget on the policy applies to each task, not to the key or the business as a whole. If the agent can create tasks, review how many it may open for one piece of client work.

Run the task with an honest clock

create_task opens the task in a paused state with a title and a policy id. Nothing is charged until start_task. From then on the clock is server-measured: heartbeats record elapsed time, and only the first 120 seconds after a heartbeat count. A session that goes quiet is not quietly billing.

That cap shapes the operating rule. Send heartbeat_task every 30 to 60 seconds while the agent is actively working, and pause_task whenever it is waiting on you, on a review, or on a slow external resource. If you are waiting for a person, the task should be paused. The run a billable task guide lists the whole lifecycle in order.

create_task, then start and heartbeat

Example
create_task  {
  "request_id": "f3d7a1b9-5c28-4e64-8a09-2b6d4f1c7e35",
  "policy_id": "0d9c2b64-7f13-4e88-9a25-1c6b3e8d4f70",
  "title": "Accessibility remediation pass"
}
start_task  {
  "request_id": "2e8b5c41-6a07-4d38-8f52-3e7a1c9b0d64",
  "task_id": "c8a3f0d5-4b17-4e92-9c60-2d5b8f1a7e39"
}
heartbeat_task  {
  "request_id": "9b2e5c41-6a07-4d38-8f52-3e7a1c9b0d64",
  "task_id": "c8a3f0d5-4b17-4e92-9c60-2d5b8f1a7e39"
}

Each call carries its own request_id, and retrying with the same id and input is the safe retry path.

Report provider costs with something to check them against

record_task_usage records one provider charge: the provider, a provider request reference, the cost in the policy currency and a source string explaining where the figure came from. Model name, input tokens and output tokens are optional but worth including, because they are what let you reconcile the row against your provider dashboard later.

Usage rows are unique per business, provider and provider reference. Recording the same provider charge twice is rejected rather than quietly doubling the amount, and a retry with the same request_id returns the original.

record_task_usage

Example
record_task_usage  {
  "request_id": "3c7d1a86-5e29-4b04-9f71-6a2d8c4b1e95",
  "task_id": "c8a3f0d5-4b17-4e92-9c60-2d5b8f1a7e39",
  "provider": "anthropic",
  "model": "claude-sonnet-4",
  "provider_request_id": "req_01HZY8QK4M",
  "cost": 216,
  "currency": "USD",
  "input_tokens": 184500,
  "output_tokens": 26400,
  "source": "provider usage export, weekly download"
}

The cost is the provider charge, not an estimate of what it might have been. The source field is what makes the row auditable.

Do not invent a price

Task usage is reported cost. If the number is not known yet, wait for the provider export rather than filling in a plausible figure, and log it before you complete the task. Completion is final.

Complete, then read the amounts before you decide anything

complete_task stops the clock and freezes the work for review. Then get_task_billing_preview reports the two components and the total: active time at the policy rate, reported usage multiplied by the approved multiplier, both rounded per component, plus requires_human_review and a review_url.

The worked example below is a job agreed at a flat 1,500.00 where the computed amount came in lower. That is the useful case: the record shows how the approved policy priced the work, while the agreement still decides what the client pays.

Fictional account, field names as returned by the server

You say

Complete the accessibility task and show me what it came to against the fixed price.

Your agent calls

  • complete_task
    {
      "request_id": "d7a4f0b8-3c61-4e29-8b05-9f2d6a1c4e73",
      "task_id": "c8a3f0d5-4b17-4e92-9c60-2d5b8f1a7e39"
    }
  • get_task_billing_preview
    {
      "task_id": "c8a3f0d5-4b17-4e92-9c60-2d5b8f1a7e39"
    }

Arithmetic you can check

  • 36900 s at $120.00/hour$1,230.00
  • $216.00 provider cost x 1.1 multiplier$237.60
  • $1,230.00 + $237.60$1,467.60 computed by the policy, $32.40 below the $1,500.00 agreed flat price

What came back

The billing preview for the completed task. requires_human_review stays true until a person approves it in the app.

time_amount
1230
usage_amount
237.6
total
1467.6
currency
USD
requires_human_review
yes
review_url
/tasks
Show the raw response
{
  "time_amount": 1230,
  "usage_amount": 237.6,
  "total": 1467.6,
  "currency": "USD",
  "requires_human_review": true,
  "review_url": "/tasks"
}

Approve in the app, then make the draft match the agreement

  1. A person opens the review url, reads the active time and each usage row, and approves the pricing.
  2. Approval writes a draft invoice from the policy: one line for active time and one for multiplied usage, with the task linked to that invoice. Deleting the draft releases the task back to completed so it can be approved again rather than leaving billing in a half state.
  3. Check the draft against the agreement. If the job was agreed at a flat price, replace the lines with that single agreed amount using update_draft_invoice, which only works while the invoice is still a draft.
  4. Do not also draft a second invoice for the same work. One task maps to one invoice, so a duplicate is the thing to avoid. If you have already created one by mistake, delete the unsent draft in the app rather than sending either.
  5. Send when the draft is right: send_invoice, or the Send action in the app.

update_draft_invoice, aligning the draft with the flat price

Example
update_draft_invoice  {
  "invoice_id": "6b1f0c62-6a0f-4f38-9d0e-3a1f9a2b7c11",
  "line_items": [
    {
      "description": "Accessibility remediation pass, agreed flat price",
      "quantity": 1,
      "rate": 1500
    }
  ],
  "notes": "Agreed flat price. Task record shows $1,467.60 computed under the approved policy ($1,230.00 time and $237.60 usage)."
}

Replacing the line items changes the total to the agreed price. Invoice notes are visible to the client. Include the computed figure only if you intend to share it; otherwise keep that comparison in the task record.

Who can do what
ActionWho
Approve a pricing policyOwner or admin, in the app
Create, start, pause, heartbeat, complete a taskAgent, with a Read and draft key
Report provider usageAgent, with a reference and a source
Approve the task for invoicingOwner or admin, in the app
Edit the draft that approval producedAgent with a Read and draft key, while it is still a draft
Send the reviewed invoicePerson in the app, or an agent with the Billing profile

What to do when the numbers drift

  • Under the agreed price: invoice the agreed price, and keep the preview as the record of what the approved policy computed. Nothing about the client conversation changes.
  • Over the agreed price: you now have dated evidence of what took the time, including the usage rows. That is material for a scope conversation rather than a surprise invoice.
  • Work that stalls: pause instead of letting a task idle. Time only counts in the measured windows after a heartbeat.
  • A repeated provider charge: the uniqueness on business, provider and request reference rejects the duplicate. If the provider genuinely charged twice, record it with the second reference.

A fixed price is your agreement, not a Billr field

Billr computes active time and reported usage; it does not hold a "fixed price" value, and the policy rate is not a statement of your real costs. If the contract says a flat amount, invoice that amount and keep the task record as the explanation behind it.

Two operational limits are worth planning around. The pricing policy is per client and per task history, so a rate change means a new policy for new work rather than an edit that rewrites old amounts. And task work is tracked separately from logged time: a task measures its own active seconds instead of the hours you would log with log_time, so bill one or the other for the same work, never both. The bill a month of logged work guide covers the logged-time path.

Frequently asked questions

Can an agent approve its own rate or release a task invoice?
No. Agents cannot create or approve pricing policies, and approving a completed task for invoicing happens in the app with an owner or admin. The agent can run the task, report usage and read the preview. Ordinary invoices are separate: with the Billing profile a key can draft, send and record payments on them.
How is time measured for a task?
From server timestamps. Each heartbeat or pause records at most 120 seconds since the previous heartbeat, so a session that goes quiet stops accumulating time instead of billing a long idle window.
What if the same provider charge is reported twice?
Usage rows are unique per business, provider and provider reference, so the duplicate is rejected. A retry of the same call with the same request id returns the original operation.
Can I bill a fixed price even if the computed total differs?
Yes. The computed total is what the approved policy charges for measured time and reported usage, not your real cost or margin. If the agreement is a flat price, replace the draft lines with that price using update_draft_invoice and keep the task preview as the explanation behind it.
Can I undo a completed task?
Completion cannot be undone. If a task is completed with something missing, the correction path is human review: a person can adjust the draft invoice before it is sent, and the task history stays intact.

Guides and docs for the individual steps

Each article covers a whole job. These are the narrower references behind it.

Sources

Reviewed 2026-09-20 against Billr's own tool implementations and the pages linked here.

A task runs against an approved policy, and the key profile decides what the agent may touch at all. Start with the key scope and the rate.

Scope the agent first

Every example on this page uses a fictional account. Field names come from Billr's own tool implementations, and the arithmetic is recomputed by the same helpers the server uses. All articles.