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

# Transfer a website

> Learn how to transfer a Cactal website to another organization: access requirements in both organizations, the billing capacity rule for websites with custom domains enabled, what moves with the transfer, and who can see it afterward.

A transfer moves a website — with its full history, content, and domains — from one organization to another in a single call.

## Prerequisites

* Access in both organizations: `websites.transfer` capability on the source website and website-creation rights in the target — see [Access control](/docs/concepts/access-control)
* The target organization's id, from `GET /v1/organizations`
* For a website with custom domains enabled: active billing and unused paid capacity in the target — see [Plans and limits](/docs/platform/plans-and-limits)

<Note>
  An API key carries access in a single organization, so it cannot pass the target-side check. Run transfers as a person who belongs to both organizations — from the dashboard, or a session on the app host.
</Note>

<Steps>
  <Step title="Check the target's capacity">
    Skip this step for a website with custom domains disabled. A website using only its Cactal platform domain never counts toward a plan, even when published. For a website with custom domains enabled, the target organization must have active billing and unused paid capacity, or the transfer is rejected. Capacity normally matches the sites in use, so the practical path is often to disable custom domains, transfer, and have an owner in the target organization re-enable them, confirming the charge there. After a transfer out, the source organization's freed capacity is released automatically.

    ```bash Check the target's billing state theme={null}
    curl 'https://api.cactal.ai/v1/billing/state?organizationId=oTgt4rS6tU8vW0xY1zA3b' \
      -H "Authorization: Bearer $CACTAL_API_KEY"
    ```

    Confirm `"status": "active"` and compare `billableSiteCapacity` against `billableSiteCount`. Capacity must exceed the count to receive the site.
  </Step>

  <Step title="Transfer the website">
    `POST /v1/websites/{websiteId}/transfer` performs the move atomically.

    <CodeGroup>
      ```bash Transfer theme={null}
      curl -X POST 'https://api.cactal.ai/v1/websites/V1StGXR8_Z5jdHi6B-myT/transfer' \
        -H "Authorization: Bearer $CACTAL_API_KEY" \
        -H 'Content-Type: application/json' \
        -d '{ "targetOrganizationId": "oTgt4rS6tU8vW0xY1zA3b" }'
      ```

      ```json Response (truncated) theme={null}
      {
        "id": "V1StGXR8_Z5jdHi6B-myT",
        "organizationId": "oTgt4rS6tU8vW0xY1zA3b",
        "name": "Acme marketing site",
        "headVersion": 12,
        "publishedVersion": 11
      }
      ```
    </CodeGroup>

    The response is the website with its new `organizationId`. The website id never changes. Transferring to the website's current organization returns `400`.
  </Step>

  <Step title="Understand what moved">
    Everything owned by the website travels with it, because it is keyed by the website id:

    | Moves with the website               | Notes                                                                         |
    | ------------------------------------ | ----------------------------------------------------------------------------- |
    | Source code and full version history | Head and published versions are unchanged.                                    |
    | CMS collections, fields, and items   | Publish states preserved.                                                     |
    | Assets                               | CDN URLs keep working.                                                        |
    | Domains                              | The platform subdomain and custom domains keep serving; no DNS change needed. |
    | Analytics                            | History stays attached to the website.                                        |

    One thing is removed on purpose: every website-scoped access grant on the website is deleted during the transfer.

    <Warning>
      Website-scoped editors lose access at transfer time, and website-scoped API keys lose their grant on this website. A key whose only grant was this website stops authorizing anything. Re-invite editors and re-scope or re-issue keys in the target organization.
    </Warning>
  </Step>

  <Step title="Restore access in the target organization">
    Members of the target organization now see the website through their organization roles; the source organization loses all visibility. Rebuild any per-website access:

    ```bash Re-create a client key in the target organization theme={null}
    curl -X POST 'https://api.cactal.ai/v1/apiKeys' \
      -H "Authorization: Bearer $CACTAL_API_KEY" \
      -H 'Content-Type: application/json' \
      -d '{
        "organizationId": "oTgt4rS6tU8vW0xY1zA3b",
        "name": "acme-content-agent",
        "expiresAt": null,
        "access": { "kind": "websites", "websiteIds": ["V1StGXR8_Z5jdHi6B-myT"], "role": "content_editor" }
      }'
    ```

    Re-invite website editors with `POST /v1/websites/{websiteId}/editors` — see [Invite teammates and clients](/docs/guides/invite-teammates-and-clients). The transfer itself is recorded in the target organization's audit log with the source and target organization ids.
  </Step>

  <Step title="Verify the transfer">
    ```bash Confirm the new owner organization theme={null}
    curl 'https://api.cactal.ai/v1/websites/V1StGXR8_Z5jdHi6B-myT' \
      -H "Authorization: Bearer $CACTAL_API_KEY"
    ```

    <Check>
      You should see `"organizationId": "oTgt4rS6tU8vW0xY1zA3b"`, and the website appears in `GET /v1/websites?organizationId=oTgt4rS6tU8vW0xY1zA3b`. If it was published, its hosts still serve — request the platform subdomain to confirm.
    </Check>
  </Step>
</Steps>

## Troubleshooting

| Symptom                                                                    | Cause                                                                                                        | Fix                                                                                                                             |
| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `400` `Target organization must be different from the source organization` | `targetOrganizationId` is the website's current organization.                                                | Pick a different target.                                                                                                        |
| `403` `forbidden`                                                          | The caller lacks access to the target organization, including any API key scoped to the source organization. | Run the transfer as a principal with `websites.create` access in the target organization.                                       |
| `404` `Organization not found`                                             | `targetOrganizationId` does not match an active organization.                                                | Check the target organization id.                                                                                               |
| `403` `Billing must be active to receive this website`                     | The target has no active subscription.                                                                       | Activate a plan in the target organization.                                                                                     |
| `403` `This organization has reached its website capacity`                 | The target has no unused paid capacity.                                                                      | Disable custom domains on this website and re-enable them after the transfer, or disable them on another website in the target. |
| A client's integration breaks after transfer                               | Its website-scoped key grant was deleted with the transfer.                                                  | Issue a new key in the target organization and update the integration.                                                          |

## Next steps

* [Manage API keys](/docs/guides/manage-api-keys) to rebuild scoped keys in the target organization
* [Invite teammates and clients](/docs/guides/invite-teammates-and-clients) to restore editor access
* [Websites concepts](/docs/concepts/websites) for the full website lifecycle
