> ## Documentation Index
> Fetch the complete documentation index at: https://phone-harness.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Session lifecycle

> Understand readiness, deadlines, billing time, and cleanup.

`POST /sessions` accepts a request and returns `202`. Use `GET /sessions/{sid}` to follow it.

A session is a temporary Android phone (the default), your saved Android phone (`profile_id`, see [Android](/docs/guides/android)), or your own iPhone (`platform: "ios"`, see [iPhone](/docs/guides/iphone)). Everything below applies to all of them.

| State | What it means | What to do |
| - | - | - |
| `provisioning` | The request was admitted; the phone is being prepared. | Keep polling. Do not send phone operations yet. |
| `ready` | The phone and API executor are available. | Use the operations listed in `ops`. |
| `error` | Startup failed. | Inspect the public error and create receipt; clean up if still present. |
| `closing` | Closure was requested and cleanup may still be running. | Stop using the phone and wait for cleanup. |

After removal from the active runtime, `GET /sessions/{sid}` returns `404`. Use the durable create receipt or history to inspect the completed request. A `404` alone is not a cleanup receipt.

## Three different clocks

| Clock | Starts | Ends |
| - | - | - |
| **Provisioning time** | Session creation/admission | API state becomes `ready`, or startup fails |
| **Session duration** | Session creation/admission | `expires_at`, or an earlier End/closure |
| **Billable use** | The phone becomes `ready` | Closure is recorded |

`timeout_seconds` defaults to **300 seconds**. Pass a positive integer to select a different finite duration. Extremely large durations that cannot form a supported timestamp are rejected. There is no unlimited mode or session-extension endpoint.

<Warning>
  Provisioning is included in the session duration. For example, if you select 900 seconds and provisioning takes 140 seconds, about 760 seconds remain after readiness. Choose enough time for startup, app installation, and your workflow.
</Warning>

Use the returned `expires_at` Unix timestamp, in seconds, as the session's deadline. Reloading the dashboard, polling the API, sending input, or requesting another viewer does not extend it. Current sessions with a selected deadline do not use a separate inactivity cutoff.

## Readiness is not the first video frame

`ready` means API operations can attach to the phone. Opening the browser viewer also requires obtaining a viewer URL, establishing the connection, and displaying video. Measure first visible frame separately if you are measuring the browser experience.

`age_s` measures session age. `ready_age_s` is zero until ready, then measures elapsed ready time up to closure. Neither is a direct input-latency measurement.

## End and cleanup

Call `DELETE /sessions/{sid}` when your workflow finishes, including when it fails. Closure stops new API operations and records the end of billable use. Cleanup can continue after the request returns.

* **`200`, `released: true`:** cleanup is confirmed.
* **`202`, `cleanup_pending: true`:** closure is accepted; cleanup is pending.
* **`cleanup_status: allocation_uncertain`:** the service is still reconciling an uncertain allocation. Do not assume it is gone.

For sessions created with an idempotency key, poll `GET /sessions/requests/{request_key}` until `cleanup_complete` is `true`. Capacity can remain occupied while cleanup is pending.

Your workflow should also tolerate early closure from exhausted credits, access revocation, service recovery, or startup failure. A selected duration is an upper bound, not a guarantee against interruptions.

## What survives a session

A temporary phone keeps nothing: app installations, logins and files end with the session, so store screenshots or test outputs in your own system before ending it. Your saved Android phone keeps all of that between sessions, and your iPhone is never wiped at all.

## One idempotency key per logical Start

Send an `Idempotency-Key` header with `POST /sessions`. Use 16–128 ASCII letters, digits, underscores, or hyphens. A UUID is a suitable value.

The key belongs to your account. Replaying the same key with the same parsed JSON body returns the existing session identity instead of allocating another phone. JSON key ordering does not matter; changing fields or adding a formerly omitted default does.

```bash theme={null}
curl --fail-with-body -sS https://api.phone-harness.com/sessions \
  -H "Authorization: Bearer $PHONE_HARNESS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $REQUEST_KEY" \
  -d '{"timeout_seconds":900}'
```

Without this header, each accepted POST can create another session.

## Recover an ambiguous create

If your connection drops after sending the request, you may not know whether a phone was created. Look up the key:

```bash theme={null}
curl --fail-with-body -sS \
  "https://api.phone-harness.com/sessions/requests/$REQUEST_KEY" \
  -H "Authorization: Bearer $PHONE_HARNESS_API_KEY"
```

```json Illustrative response theme={null}
{
  "id": "examplephone",
  "request_key": "example-request-key-0001",
  "platform": "android",
  "kind": "emulator",
  "state": "provisioning",
  "cleanup_complete": false,
  "cleanup_pending": false
}
```

Use the returned `id` to continue polling. If no receipt is found, retry the original POST with the **same key and body**. Even if the earlier request arrives late, the key prevents a second allocation for that logical request.

Do not immediately generate another key because a request timed out.

## Operations are different

There is no general idempotency key for taps, text entry, app launches, or APK installation. A network failure does not prove the operation did not happen. Check the phone's current state before repeating an action that could have side effects.

For read-only polling, use bounded retries with backoff. If creation is refused for capacity, wait before retrying; repeatedly issuing new creation keys does not reserve future capacity.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.