---
title: "Duplicate a project (or save it as a template)"
method: POST
path: "/projects/{id}/duplicate"
tags: ["Projects"]
---

# Duplicate a project (or save it as a template)

`POST /projects/{id}/duplicate`

## Request

- Base URL: `https://tasg.app/api/v1`
- URL: `https://tasg.app/api/v1/projects/{id}/duplicate`
- Auth: HTTP bearer

Copies the project's structure: sections, tasks, subtasks, priorities, categories and tags. Progress, assignees, dates, comments and attachments are reset, and members are not carried over; the caller becomes the copy's owner. With `as_template: true` the copy gets status `template`, which is what the new-project template picker lists; create a project FROM a template by duplicating the template without that flag. Any project member; guests cannot duplicate.

## Path parameters

- `id` string, uuid, required

## Request body

- object
  - `name` string: Defaults to the source name plus " (copy)" or " (template)".
  - `as_template` boolean: Create the copy with status `template`.
  - `include_tasks` boolean: Default true. False copies only the project and its sections.

## Response `201`

Created.

- object
  - `project` Project, required
    - `id` string, uuid, required
    - `workspace_id` string, uuid, required
    - `name` string, required
    - `description` string, nullable
    - `owner_id` string, uuid, required
    - `color` string, required
    - `status` 'active' | 'archived' | 'template', required
    - `default_view` 'list' | 'board' | 'timeline' | 'calendar', required
    - `task_count` integer: Present on list and get; absent on create/update responses.
    - `members` ProjectMember[], required
      - `user_id` string, uuid, required
      - `role` 'owner' | 'editor' | 'viewer', required: Project role. Owners manage settings/members; editors change tasks; viewers read and comment.
      - `joined_at` string, date-time, required
      - `user` object, nullable
        - `id` string, uuid, required
        - `full_name` string, required
        - `avatar_url` string, uri, nullable
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required

## Other responses

- `400`: Validation failed: the body or query did not match the schema, or a business rule was violated. `error` says which.
- `401`: Missing, invalid, expired or revoked credentials.
- `403`: Authenticated, but not allowed: not a member, insufficient project role or the API token lacks the required scope.
- `404`: Not found, or not visible to the caller (tenancy is enforced: foreign resources look identical to missing ones).
- `429`: Rate limited. Back off and retry after the window in the `RateLimit-*` headers.

---

[API](https://skmtc.dev/tasg/apis/tasg-api.md) · [All operations](https://skmtc.dev/tasg/apis/tasg-api/llms.txt) · [OpenAPI document](https://skmtc.dev/tasg/apis/tasg-api/revisions/cd2a1dbaf5cf?raw)
