Phonebox / Docs
Guides

Keep costs down

Park phones explicitly, keep idle timeouts short, cap sessions, mind the one-minute minimum, send heartbeats only while someone watches, and set a spending limit.

View as Markdown

You pay only while a phone is creating, starting or ready, at $0.001 a second, so keeping costs down means keeping that time short. A parked phone costs nothing and keeps everything on it, so parking is never a loss. Pricing and credits has the details.

Park as soon as the work is done

Billing stops the moment you ask to park, so park explicitly instead of waiting for the idle timer. Do it even when the task failed, for example in a finally block:

const night = new Date().toISOString().slice(0, 10); // one phone a night: a rerun the same night gets it back
const phone = await pb.phones.create({ name: "nightly-check", idempotencyKey: `nightly-check-${night}` });
try {
  if (phone.status !== "ready") await phone.waitUntil("ready"); // create() returns the phone even if its wait is cut short
  await runChecks(phone);
} finally {
  await phone.park();
}

From the CLI, end every session with phonebox park. Over MCP, call park_phone. The agent skill tells coding agents to do this.

Use short idle timeouts

A phone parks itself its idle_timeout after its last_active_at, the last time a request addressed it: 300 seconds by default, and anything from 60 to 3600. last_active_at moves at most once every 15 seconds, so the park can come up to about 15 seconds sooner than idle_timeout after your last request. Every one of those seconds after the last request is billed, so an agent that forgets to park costs up to the whole timeout.

Choose the shortest timeout that covers the longest pause in your agent's work, plus about 15 seconds for that margin. An agent that acts every few seconds does well with 120 seconds. For a long pause, such as waiting for a build, park the phone and start it again when the build is done: parking stops billing and keeps everything on the phone. Or raise idle_timeout for that phone, when you create it or with a start, which gives a running phone's session the timers you send.

POST /v1/phones
{
  "name": "form-filler",
  "idle_timeout": 120,
  "max_duration": 900
}

Cap each session with max_duration

max_duration parks a phone however busy it is, at 900 seconds by default and at most 10800. It is your ceiling for one session: a loop that never stops can't run past it. It also sets the reservation, so a shorter max_duration holds less credit and lets more phones start from the same balance. When a long task needs more time, start the phone again to renew it.

Mind the one-minute minimum

Every session is billed for at least 60 seconds, and from the moment its create or start is admitted, so the time the phone takes to become ready is billed too. In our measurements, a create took 38 to 63 seconds to become ready, and a resume 40 to 103. So ten tasks of ten seconds each, each in its own resumed session, cost from $0.60 to $1.13: every session bills its start-up and at least the one-minute minimum. In one shared session, they cost one start-up and 100 seconds, $0.14 to $0.20. When tasks come in quick succession, keep the phone running between them and park it after the last one, or let a short idle timeout park it.

Send heartbeats only while someone watches

POST /v1/phones/{id}/heartbeat restarts the idle timer. Send heartbeats only while a person is actually watching the phone, for example in your own interface, and stop when they leave, so that the idle timer can park the phone. A heartbeat loop that runs unattended keeps the phone billing until its max_duration.

The console's phone viewer sends heartbeats while it is open and visible, and an open live view page keeps the phone awake too. Close them when you're done.

Set a monthly spending limit

Each project has a monthly spending limit, $100 by default, which you change in the console's Settings. A new session that would take the month past it is refused with 402 spend_limit_reached, and sessions already running finish as they would. Set it to what you expect to spend, so that a runaway agent stops at the limit instead of at your balance. A limit of $0 stops every new session.

Watch what you spend

  • GET /v1/account shows your balance, what running phones have reserved, and what you spent this month.
  • GET /v1/phones/{id}/sessions shows what each session of a phone cost.
  • The console's Usage page shows your usage by day.

Failures cost nothing

A phone that fails before it is ever ready costs nothing, so a failed create doesn't need a refund. A retried create does cost money if it creates a second phone, so send the same Idempotency-Key when you retry one. Only after its phone has failed, or was deleted, do you create the next one with a new key.

On this page