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

> ## Agent Instructions
> Use Stable documentation by default. Honor an explicit Beta request or a URL under /docs/beta/. If the requested version conflicts with the installed CLI package or API origin, clarify the target before writing integration code.
> Pages under /docs/beta/ document Beta; other product pages document Stable. Keep the CLI package, commands, API origin, and credentials within the selected version. State the documentation version in your answer.
> For MCP search, always pass version: Stable or version: Beta. Unfiltered search mixes both versions. For filesystem reads, keep Beta queries under /beta/ and exclude /beta/ from Stable queries; discover paths before reading them.
> The public docs base is https://photon.codes/docs. Convert MCP page paths to public URLs under that base, preserving /beta/ when present. Read https://photon.codes/docs/skill.md for version selection and https://photon.codes/docs/llms.txt for the version indexes.

# JSON output and exit codes

> Machine-readable results, the stable error envelope, debug traces, and exit codes.

Every command accepts the global `--json` flag. Successful data goes to stdout
as **one** JSON object; progress, browser instructions, warnings, and errors go
to stderr.

```sh theme={null}
photon --json whoami
photon auth status --json
photon project list --json
```

<Tabs>
  <Tab title="Terminal">
    ```console theme={null}
    $ photon project list
    NAME             ID             SLUG            ACTIVE  UPDATED
    Support line     project_123    support-line    *       2026-08-01T18:00:00.000Z
    Billing alerts   project_456    billing-alerts          2026-08-03T18:00:00.000Z
    ```
  </Tab>

  <Tab title="JSON">
    ```console theme={null}
    $ photon project list --json
    {"projects":[{"projectId":"project_123","organizationId":"org_123","name":"Support line","slug":"support-line","createdAt":"2026-08-01T18:00:00.000Z"}]}
    ```
  </Tab>
</Tabs>

Loading indicators animate in an interactive terminal, fall back to static text
when redirected, and are omitted entirely in JSON mode.

## Envelopes

Collections use a named plural envelope and single resources use the singular
form:

| Kind | Envelope |
| - | - |
| Collections | `projects`, `members`, `invitations`, `assignments`, `resources`, `invoices`, `apiKeys`, `registrations`, `destinations`, `apiVersions`, `brands`, `campaigns`, `serviceKeys`, `platforms` |
| Single resources | `project`, `member`, `invitation`, `agentProfile`, `assignment`, `operation`, `subscription`, `billing`, `platform`, `resource`, `profile`, `registration`, `destination`, `brand`, `campaign`, `account`, `otp` |

Paginated collections add `nextPageToken` when more records remain. Context
results add `source`. Destructive results add an explicit `deleted`,
`disconnected`, `left`, `removed`, `released`, or `revoked` boolean.

Declining a confirmation prints this and exits `0`:

```json theme={null}
{"cancelled":true}
```

Only documented fields are selected into JSON, so a new upstream response field
never silently becomes part of the CLI's contract.

## Errors

Errors are one JSON object on stderr in a stable shape:

```json theme={null}
{"error":{"code":"PROJECTS_UNAVAILABLE","message":"Photon could not reach the project service.","retryable":true,"detail":"The request failed after automatic retries.","remediation":["Check your connection, then run \"photon operation replay op_...\"."],"operationId":"op_...","idempotencyKey":"...","replayable":true}}
```

| Field | Meaning |
| - | - |
| `code` | Stable machine-readable identifier. Branch on this. |
| `message` | Stable human summary. |
| `retryable` | Whether **this error** is worth retrying. |
| `replayable` | Whether the **recorded mutation** has a safe replay contract. |
| `detail`, `issues`, `httpStatus`, `requestId` | Occurrence-specific diagnostics, when Photon supplies them. |
| `remediation` | Concrete next steps, sometimes including a ready-to-run command. |
| `operationId`, `idempotencyKey` | Present when the failure left a recovery record. |

<Warning>
  `retryable` and `replayable` answer different questions and can legitimately
  disagree. A retryable transport error on a non-replayable mutation means
  "try the command again"; a non-retryable error on a replayable mutation means
  "use [`photon operation replay`](/docs/beta/cli/operations#replay)".
</Warning>

A shorter example with HTTP diagnostics:

```json theme={null}
{"error":{"code":"PROJECT_FORBIDDEN","message":"You do not have permission to perform this project operation.","retryable":false,"detail":"Access denied.","httpStatus":403,"requestId":"req_123"}}
```

## Exit codes

| Code | Meaning |
| - | - |
| `0` | Success, or a confirmation you explicitly declined |
| `1` | Authentication, authorization, API, transport, protocol, storage, or rejected billing failure |
| `2` | Invalid input, missing project context, a required non-interactive confirmation, or a [moved command](/docs/beta/cli/organizations#moved-commands) (`COMMAND_MOVED`) |
| `130` | Interrupted command |

Exit code `2` is what you get when a script forgets `--force` on a command that
confirms. It is an argument error, not implicit approval — nothing was changed.

## Debug traces

Global `--debug` buffers sanitized HTTP exchanges and emits one ordered trace
to stderr when the command finishes:

```sh theme={null}
photon --debug project list
photon --debug --json project list 2> trace.json
```

A successful `--json` command keeps its single result object on stdout and
writes a separate `{"debug":{"http":...}}` object to stderr. A failed JSON
command stays one error object, with the exchanges and sanitized cause chain
under `error.debug`. Commands that make no HTTP requests emit no trace.

Each exchange records the method, sanitized endpoint, query-parameter names,
safe headers, JSON bodies when safe, status, request ID, and duration. Long
traces keep the first three and latest 22 exchanges and report how many were
dropped in between.

<Warning>
  Review debug output before sharing it. Photon redacts authorization headers,
  cookies, API-key headers, query values, tokens, device and invitation codes,
  payment capabilities, presigned upload paths and form data, 10DLC profiles
  and campaign input, Call Registry profiles, tax identifiers, phone numbers,
  OTP and PIN values, registration feedback, and personal fields — but debug
  payloads are diagnostic and their shape may change between releases.
</Warning>

## Color

Human output uses compact tables and labeled detail views. ANSI styling is
enabled only for terminal streams, and text labels and symbols always carry the
same meaning without color.

Styling is disabled for JSON, for redirected output, for `TERM=dumb`, for a
non-empty `NO_COLOR` or `NO_COLORS`, and for `--no-color`. `FORCE_COLOR=1`
enables it for a non-terminal stream unless `--no-color` or `--json` disables
it.

## Environment variables

| Variable | Effect |
| - | - |
| `PHOTON_PROJECT_ID` | Selects the active project, below `--project` and above the stored selection. Empty is ignored. |
| `NO_COLOR`, `NO_COLORS` | Any non-empty value disables ANSI styling. |
| `FORCE_COLOR=1` | Enables styling on a non-terminal stream. |
| `TERM=dumb` | Disables styling. |


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