> ## 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.

# Preview a project billing change

> Preview a project billing change: previewProjectChange in the TypeScript, Python and Rust clients.

`POST /v1/organizations/{organizationId}/billing/projects/{projectId}/preview`

Previews a change on the selected project's subscription in the supplied category: a fixed-charge quantity change, or a plan change with the held fixed charges it carries over. Prices the change by the billing engine exactly as making it would bill it now: dueNow is that invoice, with taxes and the payer's credits applied. A quantity already paid for this period is not billed again, so a purchase after a release can be free. nextCharge is what is charged in advance at the next period start. For a fixed-charge change, pass quote on with the purchase so it is refused if the price or period no longer hold. This operation does not change billing. An upgrade bills the replaced plan's usage to date and the new plan from now, less the replaced plan's unused time; planChange says when it applies and which held fixed charges the target does not sell: released first for a change that applies now, released with a downgrade when it applies. additions quotes what is added once an upgrade applies, each billed when it is added, in planChange.additions and outside dueNow. Supply both organizationId and projectId; the organization must pay for the project.

<Tabs>
  <Tab title="TypeScript">
    ```typescript TypeScript theme={null}
    photon.organizations.billing.previewProjectChange(input: PreviewProjectChangeInput, options?: RequestOptions): Promise<PreviewProjectChangeOutput>
    ```
  </Tab>

  <Tab title="Python">
    ```python Python theme={null}
    photon.organizations.billing.preview_project_change(input: PreviewProjectChangeInput) -> models.PreviewProjectChangeResponse
    ```
  </Tab>

  <Tab title="Rust">
    ```rust Rust theme={null}
    client.preview_project_change(organization_id: String, project_id: String, body: &types::PreviewProjectChangeRequest).await -> Result<ResponseValue<types::PreviewProjectChangeResponse>, Error<PreviewProjectChangeError>>
    ```
  </Tab>
</Tabs>

<RequestExample>
  ```typescript TypeScript theme={null}
  import { Photon } from "@photon-ai/api";

  const photon = new Photon({
    headers: { Authorization: `Bearer ${process.env.PHOTON_API_TOKEN}` },
  });
  const result = await photon.organizations.billing.previewProjectChange({
    path: {
      organizationId: "organizationId",
      projectId: "projectId",
    },
    body: {
      category: "category",
      fixedCharge: {
        fixedChargeCode: "fixedChargeCode",
        increment: -10000,
      },
    },
  });
  ```

  ```python Python theme={null}
  import os

  from photon_api import Photon
  from photon_api.rpc_generated import PreviewProjectChangeInput

  photon = Photon(headers={"Authorization": f"Bearer {os.environ['PHOTON_API_TOKEN']}"})
  result = photon.organizations.billing.preview_project_change(
      PreviewProjectChangeInput.model_validate({
          "path": {
              "organizationId": "organizationId",
              "projectId": "projectId",
          },
          "body": {
              "category": "category",
              "fixedCharge": {
                  "fixedChargeCode": "fixedChargeCode",
                  "increment": -10000,
              },
          },
      })
  )
  ```

  ```rust Rust theme={null}
  use photon_ai_api::{Credential, PhotonClientBuilder, SecretString};

  let token = std::env::var("PHOTON_API_TOKEN")?;
  let client = PhotonClientBuilder::new()
      .credential("accountServiceKey", Credential::Bearer(SecretString::from(token)))
      .build()?;
  let result = client
      .preview_project_change(
          "organizationId".to_owned(),
          "projectId".to_owned(),
          &serde_json::from_value(serde_json::json!({
              "category": "category",
              "fixedCharge": {
                  "fixedChargeCode": "fixedChargeCode",
                  "increment": -10000,
              },
          }))?,
      )
      .await
      .map_err(|error| format!("{error:?}"))?
      .into_inner();
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "balanceAfterCents": null,
    "dueNow": {
      "creditsCents": -9007199254740991,
      "currency": "string",
      "discountCents": -9007199254740991,
      "feesCents": -9007199254740991,
      "lines": [
        {
          "amountCents": -9007199254740991,
          "code": "string",
          "kind": "fixed_charge",
          "name": "string",
          "periodEnd": null,
          "periodStart": null,
          "units": "string"
        }
      ],
      "taxesCents": -9007199254740991,
      "totalCents": -9007199254740991
    },
    "nextCharge": null,
    "planChange": null,
    "quote": null,
    "timeZone": "string"
  }
  ```
</ResponseExample>

## Parameters

<ParamField path="organizationId" type={"string"} required />

<ParamField path="projectId" type={"string"} required>
  The project being billed.
</ParamField>

## Body

**One of: BillingPreviewFixedChargeChange**

<ParamField body="category" type={"string"} required>
  Category slug, e.g. analytics or messaging.
</ParamField>

<ParamField body="fixedCharge" type={"FixedChargeChange"} required>
  A quantity change made now, as a purchase or release makes it.
</ParamField>

**One of: BillingPreviewPlanChange**

<ParamField body="additions" type={"array of FixedChargeAddition"}>
  Fixed charges added once an upgrade applies, each billed when it is added rather than with the upgrade. Priced as if added now on the target plan and returned in planChange.additions, outside dueNow. Each must be sold by the target plan and named once.
</ParamField>

<ParamField body="category" type={"string"} required>
  Category slug, e.g. analytics or messaging.
</ParamField>

<ParamField body="planCode" type={"string"} required>
  Plan to change to, as a purchase of it changes it.
</ParamField>

## Returns

**`200`** What the change would bill. PreviewProjectChangeResponse

<ResponseField name="balanceAfterCents" type={"integer | null"} required>
  The payer's credit balance once dueNow is charged: the credit the change issues included, less what dueNow uses. In dueNow's currency. Null when the change charges nothing now (a downgrade).
</ResponseField>

<ResponseField name="dueNow" type={"BillingPreviewInvoice"} required>
  Charged when the change is made, priced as the change bills it. No lines when it charges nothing now.
</ResponseField>

<ResponseField name="nextCharge" type={"BillingNextCharge | null"} required>
  What is charged in advance at the next period start for what is held after the change. Usage and a minimum commitment are billed after the period. Null when nothing is, or the subscription ends first.
</ResponseField>

<ResponseField name="planChange" type={"BillingPlanChangeEffect | null"} required>
  Set for a plan change, null otherwise.
</ResponseField>

<ResponseField name="quote" type={"FixedChargeQuote | null"} required>
  Set for a fixed-charge change, null otherwise: the unit price, currency and period the preview was priced on. Pass them on with the purchase so it is refused if they no longer hold.
</ResponseField>

<ResponseField name="timeZone" type={"string"} required>
  The IANA time zone the payer is billed in, e.g. America/Los\_Angeles. Billing periods start and end at its midnights, so read their calendar dates in it.
</ResponseField>


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