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

# Copy a template across projects

> POST /v1/projects/:id/templates/copy — duplicate a template's layout, manifest, and jsonSchema from one project you own into another.

<Info>**POST** `https://api.craftkit.dev/v1/projects/{id}/templates/copy`</Info>

Duplicate a template — its published **layout/design**, variable **manifest**, and everything
needed to derive its `jsonSchema` — from a **source** project into **this** (target) project,
published as version 1. This is the cross-project path the rest of the public API doesn't cover:
[`GET /v1/templates/:slug`](/api-reference/get-template) never returns a layout, and
[`POST /v1/templates`](/api-reference/create-template) only accepts a manifest — so without this
endpoint, copying a template between projects reproduces its 50+ variables but drops the Canvas
design entirely. The copy renders **byte-for-byte identically** to the source: `contentJson`,
`compiledHtml`, `variablesManifest`, and `pageConfig` are carried over verbatim, not regenerated.

<Warning>
  Both the source and target project must be owned by the **same** authenticated account. This is
  a cross-**project** copy, not a cross-**account** one — an account key can only ever pull a
  template from a project it also owns. See [Account-key auth](/guides/account-api).
</Warning>

## Authorization

<ParamField header="Authorization" type="string" required>
  `Bearer ck_acct_…` — must own **both** the target project (the path `id`) and the source project.
</ParamField>

## Path

<ParamField path="id" type="string" required>The **target** project id (UUID) to copy into.</ParamField>

## Body

<ParamField body="sourceProjectId" type="string">
  UUID of the **source** project. Provide this or `sourceProjectSlug` (not both required — either
  is enough to resolve the project).
</ParamField>

<ParamField body="sourceProjectSlug" type="string">
  Slug of the source project, scoped to the authenticated account. Alternative to `sourceProjectId`.
</ParamField>

<ParamField body="sourceTemplateId" type="string">
  UUID of the template to copy, scoped to the source project. Provide this or `sourceTemplateSlug`.
</ParamField>

<ParamField body="sourceTemplateSlug" type="string">
  Slug of the template to copy, scoped to the source project. Alternative to `sourceTemplateId`.
</ParamField>

<ParamField body="name" type="string">
  Display name for the copy (1–120 chars). Omit to keep the source template's `name` **and** `slug`
  unchanged. When provided, the target slug is derived from `name` instead.
</ParamField>

## Response

`201` when a new template was created, `200` when an idempotent replay returned an existing one
(see [Idempotency](#idempotency) below) — the body shape is identical either way:

<ResponseField name="id" type="string">UUID of the copy (or the pre-existing template, on a replay).</ResponseField>
<ResponseField name="projectId" type="string">Echoes the path `id` — the target project.</ResponseField>
<ResponseField name="slug" type="string">The copy's slug (the source's slug, unless `name` was given).</ResponseField>

<ResponseField name="name" type="string" />

<ResponseField name="currentVersionNumber" type="number">Always `1` for a freshly created copy.</ResponseField>

<ResponseField name="sourceProjectId" type="string" />

<ResponseField name="sourceTemplateId" type="string" />

<ResponseField name="sourceVersionNumber" type="number">The source's version number that was copied (e.g. `6`).</ResponseField>

<ResponseField name="alreadyExisted" type="boolean">
  `true` when a non-deleted template already occupied the target slug and was returned instead of
  creating a duplicate (see [Idempotency](#idempotency)).
</ResponseField>

## Idempotency

If the target project already has a non-deleted template at the resolved slug, this endpoint
returns **that** template (`200`, `alreadyExisted: true`) instead of erroring or creating a
duplicate — so retrying the same copy call (e.g. after a network timeout, or as part of a
re-run onboarding flow) is always safe. If you need strict conflict detection instead, pass a
`name` you know is unused in the target project — a genuine collision then falls through to the
copy logic and is resolved the same way.

## Errors

| Status | code                        | Meaning                                                                                                                                       |
| ------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| 401    | `unauthorized`              | Missing/invalid/wrong-type (`ck_acct_…`) bearer token.                                                                                        |
| 404    | `not_found`                 | No such **target** project owned by this account.                                                                                             |
| 404    | `source_project_not_found`  | No such **source** project owned by this account.                                                                                             |
| 404    | `source_template_not_found` | No template matching `sourceTemplateId`/`sourceTemplateSlug` in the source project.                                                           |
| 409    | `no_published_version`      | The source template exists but has never been published — there is no design to copy.                                                         |
| 422    | `invalid_request`           | Neither `sourceProjectId`/`sourceProjectSlug`, or neither `sourceTemplateId`/`sourceTemplateSlug`, was provided; or `name` failed validation. |

```bash cURL theme={null}
curl -X POST https://api.craftkit.dev/v1/projects/8f2a1c3e-4b5d-4e6f-9a1b-2c3d4e5f6a7b/templates/copy \
  -H "Authorization: Bearer $CRAFTKIT_ACCOUNT_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "sourceProjectSlug": "template-library",
    "sourceTemplateSlug": "e-charterparty",
    "name": "E-Charterparty (Acme)"
  }'
```

```json 201 theme={null}
{
  "id": "3f9b6c2e-1a2b-4c3d-9e8f-7a6b5c4d3e2f",
  "projectId": "8f2a1c3e-4b5d-4e6f-9a1b-2c3d4e5f6a7b",
  "slug": "e-charterparty-acme",
  "name": "E-Charterparty (Acme)",
  "currentVersionNumber": 1,
  "sourceProjectId": "1d2e3f4a-5b6c-7d8e-9f0a-1b2c3d4e5f6a",
  "sourceTemplateId": "9e8f7a6b-5c4d-3e2f-1a2b-3f9b6c2e1a2b",
  "sourceVersionNumber": 6,
  "alreadyExisted": false
}
```

<Tip>
  After copying, fetch [`GET /v1/templates/:slug`](/api-reference/get-template) in the target
  project with a `ck_live_…` key (mint one with
  [`POST /v1/projects/:id/keys`](/api-reference/create-project-key)) to confirm the manifest and
  `jsonSchema` match the source, then render it exactly as you would any other template with
  [`POST /v1/templates/:slug/render`](/api-reference/render-template).
</Tip>
