Skip to main content
A project is the unit that owns your Photon resources — phone numbers, email domains, API keys, webhooks, and billing. Every project belongs to an organization, which also holds its members. photon project manages projects and everything inside them.

Command overview

Every node generates its own help and shell completion. Run any of them with --help, such as photon project create --help.

Platform resources

Phone numbers, email domains, and voice routing.

API keys

Issue and revoke project API keys.

Webhooks

Subscribe endpoints to project events.

Agent profile

The name and avatar your project presents.

Billing

Plans, subscriptions, and invoices.

Projects in an organization

project list, project count, and project create work in the selected organization: --org, PHOTON_ORGANIZATION_ID, the organization of the folder’s project, the one org use saved, or your only organization.
Project resources include organizationId. The project membership role is gone, and so is the --role filter: access comes from your organization role.
photon project member, project invitation, and project leave moved to photon org. The old names only print the replacement and exit with code 2.

Select an active project

Most commands are project-scoped. They resolve the project in this order:
  1. The global --project <project-id> flag, for that invocation only.
  2. A non-empty PHOTON_PROJECT_ID environment variable.
  3. The .photon/project.json that photon link saved in the current folder, or in the nearest folder above it.
There is no account-wide default. Each folder remembers its own project, so running photon link in one repository never changes the project another repository acts on.
Agents and scripts should pass --project <project-id> or set PHOTON_PROJECT_ID on every command, rather than rely on the folder they happen to run in.
With none of the three, a command that needs a project fails with PROJECT_NOT_SELECTED and exits 2, as a missing required flag does. If you aren’t signed in at all, the sign-in error comes first. photon project current reports the source as flag, environment, or folder, with the file’s path for folder. An empty PHOTON_PROJECT_ID is ignored rather than treated as a selection. Before a command changes anything in a project you didn’t name with --project, it names the project and where it came from on stderr, for example Project: pho_prj_01k9yq3m7d2c8x4v6b5n0p1r2v, from PHOTON_PROJECT_ID or Project: pho_prj_01k9yq3m7d2c8x4v6b5n0p1r2s, from /srv/support-bot/.photon/project.json. When the command has already read the project, the line also gives the project’s name and organization. A command that asks for confirmation prints the line before its question, --project included. The line costs no extra request, and with --json it is still text on stderr, so stdout does not change.

The folder’s project file

photon link takes the project as --project, as vercel link and railway link do; PHOTON_PROJECT_ID doesn’t stand in for it. It fetches the project before saving it, so you can never save an inaccessible ID. It writes .photon/project.json in the current folder, with the project and its organization:
It also adds .photon/ to the .gitignore in that folder, creating the file if there is none, and never adds the line twice, as vercel link does with .vercel. The file is your checkout’s choice, not the repository’s. The organization it saves is always the project’s own. If you also pass --org and it names another organization, photon link refuses: it exits 2 with ORGANIZATION_MISMATCH, names the project and both organizations, and writes nothing. Its next gives the same command without --org, and with the project’s organization. Only --org is checked: PHOTON_ORGANIZATION_ID and the organization photon org use stored don’t stop a link.
project create --link saves the project it just created the same way. Replaying a project create --link with photon operation replay saves no file, because the operation journal doesn’t record the folder; it prints the photon link step to run instead. photon unlink removes the nearest file, and warns you if PHOTON_PROJECT_ID or a file in a folder above still selects a project. Deleting a project removes the file only when it names that project. The file records the API it was saved against. A CLI that talks to another API passes over it and uses the nearest file above that it saved itself, so the production and staging builds keep separate projects. photon link replaces whatever file is in the current folder, including one the other build saved. With --json, photon link and project create --link add the file’s path, source: "folder", and gitignore: added, listed (the .gitignore already had it), or failed (with a warning on stderr). When project create --link can’t save the file, because of a folder error or a project or organization ID that isn’t canonical, it prints {"project": {...}} alone, with a warning on stderr; the project is created either way. photon unlink returns cleared, the removed file’s path, and, when PHOTON_PROJECT_ID or a file above still selects a project, active with that project’s projectId, source, and path for a file.

Pagination and filters

Paginated list commands default to --limit 30. The CLI follows page tokens for you until it has that many records or reaches the last page, preserving the order the API returned. When more records remain, JSON output includes nextPageToken. billing invoice list keeps the API’s nextCursor instead, which is null on the last page. Pass the token back with --page-token to continue:
Text output ends with one stderr line that is the same command with the organization or project written in and the next page’s token, for example More results: photon project list --search support --org pho_org_01k9yq3m7d2c8x4v6b5n0p1r2s --page-token <token>. Timestamps accept RFC 3339 input and are normalized to UTC; --created-after cannot be later than --created-before.
api-key list is not paginated — the SDK returns every live key, newest first.

JSON contracts

Global --json prints one object on stdout, the result or the error, and sends warnings to stderr. Collections use a named envelope:
A single item is the bare object, as Photon sends it. The documents the CLI defines, listed under Envelopes on the JSON output page, put the API’s object under a name of their own, such as project or operation, or add keys beside the API’s response, such as provisioning. project list, count, create, view, edit and delete, the project agent-profile, project api-key and project webhook commands, and the project platform commands that read one response (list, operation cancel, email dns, imessage view, imessage assignment create, list, view and release, telegram view, and whatsapp-business number list and number view) and the ones that start an operation and wait for it (sms purchase and delete, imessage purchase, email connect and disconnect, telegram connect and disconnect, and whatsapp-business number add --waba-id and number delete), and billing invoice list, billing operation view and billing plan purchase print the API’s response as Photon sends it: a list keeps the API’s envelope, such as {"projects": [...], "nextPageToken": "..."} or {"platforms": [...]}, a single item is the bare object, a deletion or revocation prints the API’s response with no deleted or revoked boolean, and a field the API adds shows up in the JSON. project delete prints the project with deletedAt set. See API keys, Agent profile, Webhooks, Platform resources and Billing, which also describes the documents billing overview, billing subscription cancel and resume print. The email, imessage, sms and telegram list commands print Photon’s page of resources as it sends it, with the provisioning operations still in progress under provisioning. whatsapp-business account list prints the project’s one account as Photon sends it in {"accounts": [...]}, which is empty when none is connected, and whatsapp-business number add with --source byon or --new-waba prints a handoff to the dashboard. project current, project create --link and photon link print a document of their own that holds the API’s project, {"project": {...}, "source": "..."}. For project current, source is flag, environment or folder, with the file’s path for folder. project create --link and photon link add the file they saved:
See JSON output and exit codes for the error envelope and --debug traces.

Confirmations

Destructive commands ask before they act. Interactive input accepts y or yes; anything else cancels, prints Cancelled. on stderr and exits 1. project delete takes the ID of the project it deletes, and at a terminal it asks you to type the project’s name. Without a terminal, pass --yes (-y): without it the command exits 2 with CONFIRMATION_REQUIRED and changes nothing. See Prompts and confirmation.
  • Project deletion
  • API key revocation
  • Agent avatar reset
  • iMessage assignment release
  • Subscription cancellation
  • Plan purchase
  • Email-domain disconnection
  • Telegram connection when the project already has a bot
  • SMS-number purchase
  • SMS-number deletion
  • Voice profile inbound changes that affect assigned lines
  • Voice profile outbound credential rotation and revocation
  • Voice profile assignment changes
  • Voice profile deletion
  • Webhook destination deletion
  • Webhook signing-secret rotation

Recovery

Most state-changing calls are protected by a local write-ahead journal, so a mutation whose result you never saw can be replayed rather than guessed at. See Recoverable operations. Project creation requires a unique --slug: 3–63 lowercase letters or digits, with single hyphens between words.