Events
The connection pushes frames you did not ask for. Each one reports a status change, so you can stop polling.
An event frame carries an event name and a data body, and never a msgid:
Code
There is nothing to subscribe to. Open the connection and the events arrive. You cannot turn them off, filter them server-side, or ask for a replay.
Delivery only happens while you are connected. An event that fires while you are away is gone. After a reconnect, read back what you care about instead of waiting for a push that already happened. See Reconnect with a fresh URL.
The three events
| Event | Who receives it | Sent when |
|---|---|---|
run | Every connection whose token can view that phone's slot | A phone run is created, and on every status change after that |
submission | Connections opened by the submission's owner | The submission's status changes |
billing_request | Connections opened by the request's owner | The request's status changes |
run
data is the phone run, the same shape /app/phones/runs/get returns. The first one arrives
as soon as the run is created, in queued.
Code
kind is macro, command or agent. status moves through queued and running to
succeeded, failed or cancelled. Those last three are final, and nothing follows them.
result is filled in on success and its shape depends on kind. error is a readable reason
when the run failed.
submission
data is the submission, the same shape /app/submissions/get returns.
Code
published, drafted, failed and cancelled are final. On failed, failure holds a
step and a message. The status table lists every value.
billing_request
data is the billing request, the same shape /app/billing/requests/get returns.
Code
status is pending, approved, declined, failed or expired. Watch for it instead of
polling after you file a phone count change.
Worked example: start a macro and wait for it
Start a macro, then sit on the event stream until that run reaches a final status. No polling, no sleep loop.
socket.events() yields typed events and stops after timeout of silence, counted in
seconds in Python and in milliseconds in TypeScript. It also yields submission and billing
request events, which is why the loop checks the type before reading event.run: Python
matches on SubmissionEvent and BillingRequestEvent, TypeScript on an Event union
discriminated by type (run, submission or billing_request).
Without an SDK
The same loop in raw frames: send /app/phones/macros, keep the id from the 202 reply,
then read every incoming frame and act on the ones where event is "run" and
data.id matches. See Frames and replies for the envelope and
Connecting for a raw read loop to build on.
Next
- Phones: the funs that create runs.
- Submissions: publishing, and the statuses a submission moves through.
- Billing: filing a phone count change and waiting on its approval.

