# Copy a job

```http
PUT https://secure.yoprint.com/v2/api/store/{team_slug}/v2/job/{hash_id}/clone
```

- API version: v2
- Operation ID: `v2_public_job_clone`
- Section: Production › [Jobs](https://developer.yoprint.com/reference/jobs.md)
- Web page: https://developer.yoprint.com/reference/jobs/v2-job-clone/
- Authentication: `Authorization: Bearer YOUR_API_KEY`

Copies a job into a sales order or quote: the same one or another of your team's. Returns the new job.

By default the copy takes the job's line items, tasks, mockups and files, but not its approvals. Dates keep their distance from the order's issue date: the copy starts and is due as long after the target order's issue date as the original is after its own order's (so within the same order they don't change). A job without both dates uses its tasks' earliest start and latest due date; with neither, the copy is unscheduled.

Lines on phased-out or deleted products, sizes or colors are never copied as they are. If the job has any, the first call is refused with `clone_needs_choices`, which lists them. Call again with an `item_choices` entry for each: leave it out, or replace it with another size or color of the same product that is still sold.

## Permission

Needs permission to view the source order and to edit the target order (team owner, Manage Sales Orders "update all", or "update assigned" on orders you created or are assigned to; for a draft target, also its creator when their role can create sales orders).

## Business rules

- A choice for a line that is on sale is refused with `clone_choice_not_needed`; a choice naming a line that isn't on the job with `clone_choice_unknown_line`.
- A product without sizes or colors can only be left out: `replace` on it is `clone_replacement_invalid`.
- A replacement must be another variant of the same product that is still sold (`clone_replacement_invalid`).
- Nothing is copied when a choice is refused.

## Side effects

- Creates the job, and copies of its lines, tasks and files as chosen.
- Recalculates the target order and may run its auto-invoicing; neither happens when the target order came from Shopify.
- Open Admin Portal pages refresh after the copy is saved.

## Path parameters

- `team_slug` (string, required): Your team slug, shown as Team ID next to your API key in Profile. Example `your-team`.
- `hash_id` (string, required): The job's hash id (`id`). Hash ids only: a unique key or a document number gives 404. Example `WaGvtaWsx7Kcn4`.

## Request body

Content type `application/json`, required.

Fields:

- `target_order` (string, required): The sales order or quote to copy into (hash id or unique key). It can be the job's own order.
- `clone_job_approval` (boolean, optional): Copy the job's approvals. Fails with 500 when an approval includes mockup designs, or includes files while `clone_media` is false. Default `false`.
- `clone_media` (boolean, optional): Copy the mockups and production files. Default `true`.
- `clone_mockup_designs` (boolean, optional): Copy mockup designs. Defaults to `clone_media`.
- `clone_order_item` (boolean, optional): Copy the line items. Ignored (never copied) when either order came from Shopify. Default `true`.
- `clone_work_pipeline` (boolean, optional): Copy the tasks. Default `true`.
- `item_choices` (array of objects, optional): One choice per line that uses a phased-out or deleted product, size or color. Only for those lines.
  - Each item:
    - `action` (string, required): One of: `"leave_out"`, `"replace"`.
    - `id` (string, required): The line item on the source job. Each line at most once.
    - `replacement_id` (string, optional): Required for `replace`: another size or color (variant) of the same product that is still on sale.

### Example request

```bash
curl -X PUT 'https://secure.yoprint.com/v2/api/store/your-team/v2/job/WaGvtaWsx7Kcn4/clone' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_order": "sFbALVSYD55eXe"
  }'
```

Another example: Copy with choices for unsellable lines:

```bash
curl -X PUT 'https://secure.yoprint.com/v2/api/store/your-team/v2/job/WaGvtaWsx7Kcn4/clone' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'X-Yoprint-Decimal-Format: exact-v1' \
  -H 'Content-Type: application/json' \
  -d '{
    "clone_job_approval": false,
    "item_choices": [
      {
        "action": "leave_out",
        "id": "ZJTJ2uF5dMiArL"
      },
      {
        "action": "replace",
        "id": "Py196Qo19s55qk",
        "replacement_id": "Tl6eTHKHoKqrMw"
      }
    ],
    "target_order": "yYkdcUrFwnbqE2"
  }'
```

## Response

`200` Successful response.

Fields:

- `data` (object, required): A job.
  - `approval_ready_mockup_design_count` (integer, optional)
  - `assigned_users` (array of objects, optional): People on the job.
    - Each item:
      - `avatar_url` (string | null, optional)
      - `id` (string, optional): The user.
      - `name` (string, optional)
  - `auto_saved_for` (string | null, optional): Set only on autosave copies.
  - `customer` (object, optional)
    - `id` (string | null, optional): The customer.
    - `name` (string | null, optional)
  - `due_date` (string (date-time) | null, optional): Production due date. ISO 8601 with offset, or null.
  - `from_legacy` (boolean, optional)
  - `has_service` (boolean, optional): Kept for older clients; not a count of services.
  - `id` (string, optional): The job.
  - `impression_count` (number | null, optional)
  - `inventory_status` (string | null, optional): The stock status of the job's lines.
  - `is_rush` (boolean, optional)
  - `item_count` (number | null, optional): Units on the job.
  - `location_count` (number | null, optional)
  - `mockup` (array of objects, optional): Mockup files.
    - Each item:
      - `id` (string, optional): The file.
      - `name` (string | null, optional)
      - `url` (string | null, optional): Download link with its own access token.
  - `mockup_design_count` (integer, optional)
  - `mockup_summary` (object, optional)
    - `count` (integer, optional)
    - `has_mockups` (boolean, optional)
  - `name` (string | null, optional)
  - `need_cust_provide_product` (boolean | integer | null, optional): Blanks are provided by the customer.
  - `note` (string | null, optional)
  - `order_customer_po_number` (string | null, optional)
  - `order_id` (string, optional): The order.
  - `order_scoped_id` (string, optional): The order number.
  - `pdf` (array of objects, optional): PDF links.
    - Each item:
      - `label` (string, optional)
      - `url` (string (uri), optional): A download link that carries its own long-lived access token: don't publish it.
  - `pending_approval` (any, optional): The open approval request, if any.
  - `pipelines` (array of objects, optional): The job's tasks.
    - Each item:
      - `assigned_users` (array of objects, optional): Who is assigned. Same fields as `data.assigned_users` above.
      - `created_at` (string (date-time), optional)
      - `due_date` (string (date-time) | null, optional): Task due date. ISO 8601 with offset, or null.
      - `id` (string, optional): The task.
      - `job_id` (string, optional): The job.
      - `job_is_rush` (boolean | integer | null, optional)
      - `job_item_count` (number | null, optional)
      - `job_name` (string | null, optional)
      - `job_scoped_id` (string, optional): The job number with its prefix.
      - `job_status_id` (string | null, optional): The job's current status.
      - `name` (string, optional)
      - `note` (string | null, optional)
      - `order_id` (string, optional): The order.
      - `order_name` (string | null, optional)
      - `order_scoped_id` (string | null, optional): The order number.
      - `pipeline_id` (string, optional): The task preset it comes from.
      - `position` (integer, optional): Order among tasks; lower comes first. 0 when unset.
      - `sales_order` (object, optional): A summary of the job's order.
        - `created_by` (string | null, optional): Who created the order.
        - `cust_due_date` (string (date-time) | null, optional): Order customer due (in-hand) date. ISO 8601 with offset, or null.
        - `customer_id` (string | null, optional): The customer.
        - `customer_name` (string | null, optional)
        - `due_date` (string (date-time) | null, optional): Order production due date. ISO 8601 with offset, or null.
        - `external_type` (string | null, optional): `shopify` when the order came from Shopify, else null.
        - `is_quote` (boolean, optional)
        - `issue_date` (string (date-time) | null, optional): Order issue date. ISO 8601 with offset, or null.
        - `object_type` (string | null, optional): `sales_order` or `quote`.
        - `ship_due_date` (string (date-time) | null, optional): Order ship-by date. ISO 8601 with offset, or null.
        - `shopify_deleted_externally` (boolean, optional)
        - `shopify_deleted_externally_at` (string (date-time) | null, optional): When it was deleted in Shopify. ISO 8601 with offset, or null.
        - `start_date` (string (date-time) | null, optional): Order production start. ISO 8601 with offset, or null.
        - `status_id` (string | null, optional): The order status.
      - `start_date` (string (date-time) | null, optional): Task start. ISO 8601 with offset, or null.
      - `status` (object, optional): A status: label, kind and place in the list.
        - `color` (string | null, optional): Hex color.
        - `customer_label` (string, optional)
        - `id` (string, optional): The status.
        - `label` (string, optional)
        - `pipeline_id` (string, optional): Only on the statuses of a task preset (`role` `pipeline`): the task preset. Absent on other statuses.
        - `position` (integer, optional): Its place in the team's list: sort by it to find the next status.
        - `role` (string, optional): Which list it belongs to: `job` for job statuses, `pipeline` for the statuses of a task preset. One of: `"order"`, `"quote"`, `"job"`, `"purchase_order"`, `"shipment"`, `"customer_message"`, `"pipeline"`.
        - `trashed` (boolean, optional): The status was deleted.
        - `type` (string, optional): The kind of status: `start` (Pending), `pre_production` and `in_progress` (In Progress), `end` (Completed). One of: `"start"`, `"pre_production"`, `"in_progress"`, `"end"`.
      - `status_completion_date` (string (date-time) | null, optional): When it reached a Completed status. ISO 8601 with offset, or null.
      - `status_id` (string, optional): The task's status (a status of its task preset).
      - `status_last_change_date` (string (date-time) | null, optional): When the status last changed. ISO 8601 with offset, or null.
      - `updated_at` (string (date-time), optional)
  - `position` (integer | null, optional)
  - `pricing_original_total` (decimal pair, optional): The job's price before pricing rules, at the stored scale. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `pricing_rule_applied` (object | null, optional): The pricing rule that set the job's decoration price, or null. These answers usually carry only `type`, the two ids and the two totals. List the jobs of a sales order or quote and Get one job of a sales order or quote answer with the full summary (the rule's current name and position, the prices before and after).
    - `after` (string | null, optional): Full summary only: the price after the rule, as a decimal string.
    - `after_total` (decimal pair, optional): The decoration price after the rule. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
    - `base` (string | null, optional): Full summary only: the decoration price the rule started from, as a decimal string.
    - `base_type` (string | null, optional): Full summary only: `service_price`.
    - `normal_price` (string | null, optional): Full summary only: the price with no rule (the same as `base` for a job).
    - `original_total` (decimal pair, optional): The decoration price before the rule. A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
    - `pricing_group_id` (string | null, optional): The pricing group.
    - `pricing_group_name` (string | null, optional): Full summary only: the group's name now.
    - `pricing_group_removed` (boolean | null, optional): Full summary only: true when the group was removed since. The job keeps its price.
    - `pricing_rule_id` (string | null, optional): The pricing rule.
    - `rule` (object | null, optional): Full summary only: the rule as the group holds it now (`id`, `label`, `position`, where 1 is the first rule checked), or null when it no longer exists.
    - `type` (string | null, optional): `service` on a job.
  - `pricing_total` (decimal pair, optional): The job's price, at the stored scale (usually 6 decimal places: `{ "value": 600000000, "dp": 6 }` is 600.00). A decimal pair: the amount is `value` ÷ 10^`dp` (`{ "value": 1250, "dp": 2 }` is 12.50). Read `dp` every time: it can differ between fields and records. See https://developer.yoprint.com/concepts/money-and-quantities.md.
  - `production_file` (array of objects, optional): Production files. Same fields as `data.mockup` above.
  - `qr_code_url` (string | null, optional): A QR code image for the job.
  - `sales_order` (object, optional): A summary of the job's order.
    - `created_by` (string | null, optional): Who created the order.
    - `cust_due_date` (string (date-time) | null, optional): Order customer due (in-hand) date. ISO 8601 with offset, or null.
    - `due_date` (string (date-time) | null, optional): Order production due date. ISO 8601 with offset, or null.
    - `external_type` (string | null, optional): `shopify` when the order came from Shopify, else null.
    - `is_quote` (boolean, optional)
    - `is_rush` (boolean, optional)
    - `issue_date` (string (date-time) | null, optional): Order issue date. ISO 8601 with offset, or null.
    - `name` (string | null, optional): Order name.
    - `object_type` (string | null, optional): `sales_order` or `quote`.
    - `ship_due_date` (string (date-time) | null, optional): Order ship-by date. ISO 8601 with offset, or null.
    - `shopify_deleted_externally` (boolean, optional)
    - `shopify_deleted_externally_at` (string (date-time) | null, optional): When it was deleted in Shopify. ISO 8601 with offset, or null.
    - `start_date` (string (date-time) | null, optional): Order production start. ISO 8601 with offset, or null.
    - `status_id` (string | null, optional): The order status.
  - `scoped_id` (string, optional): The job number as shown, with its `JOB-` prefix. Change a job number takes only the digits.
  - `services` (array of objects, optional): The services (decorations).
    - Each item:
      - `customer_note` (string | null, optional): Customer notes.
      - `id` (string, optional): The job service.
      - `location` (object, optional)
      - `note` (string | null, optional): Internal notes.
      - `option` (any, optional): The service option.
      - `position` (integer | null, optional)
      - `pricing_total` (any, optional): A decimal pair, or null.
      - `service_id` (string, optional): The service.
      - `service_name` (string, optional)
  - `services_still_valid` (boolean, optional)
  - `start_date` (string (date-time) | null, optional): Production start. ISO 8601 with offset, or null.
  - `status` (object, optional): A status: label, kind and place in the list. Same fields as `data.pipelines[].status` above.
  - `status_completion_date` (string (date-time) | null, optional): When it reached a Completed status. ISO 8601 with offset, or null.
  - `status_id` (string, optional): The job's current status: its production stage. Look it up in List job statuses, or read `status`.
  - `status_last_change_date` (string (date-time) | null, optional): When the status last changed. ISO 8601 with offset, or null.
  - `total_qty_need_ordering` (string | null, optional): Quantity that still needs ordering, with 8 decimal places (`"12.00000000"`). A decimal string, such as `"0.00000000"`; parse it as a decimal.
  - `unique_key` (string | null, optional): The job's unique key.
  - `version` (integer, optional)
- `status` (integer, required)
- `success` (boolean, required)

Example response:

```json
{
  "data": {
    "assigned_users": [],
    "customer": {
      "id": "ifAHmADekGZ6id",
      "name": "Northwind Apparel"
    },
    "due_date": "2026-11-06T17:00:00+00:00",
    "id": "6E8Ybl4wnA5yBW",
    "is_rush": true,
    "item_count": 48,
    "mockup": [],
    "name": "Front print",
    "note": null,
    "order_id": "sFbALVSYD55eXe",
    "order_scoped_id": "SO1001",
    "pdf": [
      {
        "description": "Job details without mockup",
        "label": "Job Details",
        "url": "https://example.com/pdf/job-details.pdf"
      }
    ],
    "pipelines": [
      {
        "assigned_users": [
          {
            "avatar_url": null,
            "id": "4mdDkMg3dgQOa3",
            "name": "Alex Rivera"
          }
        ],
        "created_at": "2026-11-03T15:00:00+00:00",
        "due_date": "2026-11-05T17:00:00+00:00",
        "id": "iv0dBo17IwYGzb",
        "job_id": "6E8Ybl4wnA5yBW",
        "job_is_rush": 1,
        "job_item_count": 48,
        "job_name": "Front print",
        "job_scoped_id": "JOB-2420",
        "job_status_id": "scaWHQnPLtqMPT",
        "name": "Burn screens",
        "note": null,
        "order_id": "sFbALVSYD55eXe",
        "order_name": "Team shirts",
        "order_scoped_id": "SO1001",
        "pipeline_id": "SE3SfXjt6dPfnH",
        "position": 1,
        "sales_order": {
          "customer_id": "ifAHmADekGZ6id",
          "customer_name": "Northwind Apparel",
          "is_quote": false,
          "object_type": "sales_order",
          "status_id": "wgbOT87gGZvhgy"
        },
        "start_date": "2026-11-04T09:00:00+00:00",
        "status": {
          "color": "#2196f3",
          "customer_label": "In Progress",
          "id": "hTFITko5Q0R83y",
          "label": "Burning",
          "pipeline_id": "SE3SfXjt6dPfnH",
          "position": 2,
          "role": "pipeline",
          "trashed": false,
          "type": "in_progress"
        },
        "status_completion_date": null,
        "status_id": "hTFITko5Q0R83y",
        "status_last_change_date": "2026-11-03T15:05:00+00:00",
        "updated_at": "2026-11-03T15:05:00+00:00"
      }
    ],
    "position": 1,
    "pricing_original_total": {
      "dp": 6,
      "value": 600000000
    },
    "pricing_rule_applied": null,
    "pricing_total": {
      "dp": 6,
      "value": 600000000
    },
    "production_file": [],
    "sales_order": {
      "is_quote": false,
      "is_rush": false,
      "name": "Team shirts",
      "object_type": "sales_order",
      "status_id": "wgbOT87gGZvhgy"
    },
    "scoped_id": "JOB-2420",
    "services": [
      {
        "id": "tCBpDSBjXaPhHN",
        "option": "2",
        "position": 1,
        "service_id": "5Sf6XRMN2Vuxvh",
        "service_name": "Screen print"
      }
    ],
    "start_date": "2026-11-04T09:00:00+00:00",
    "status": {
      "color": "#2196f3",
      "customer_label": "In Progress",
      "id": "scaWHQnPLtqMPT",
      "label": "Printing",
      "position": 3,
      "role": "job",
      "trashed": false,
      "type": "in_progress"
    },
    "status_completion_date": null,
    "status_id": "scaWHQnPLtqMPT",
    "status_last_change_date": "2026-11-03T15:05:00+00:00",
    "total_qty_need_ordering": "0.00000000",
    "unique_key": "job_EXAMPLEuniqueKe2"
  },
  "status": 200,
  "success": true
}
```

## Errors

- `401` The API key is missing or wrong (`unauthenticated`).
- `403` You are not allowed to do this. `api_access_not_in_plan`: your team's plan doesn't include API access (Pro and above, with an active subscription or trial), so every request answers this until the team upgrades. When your role lacks the permission the operation needs, the body is only `{"message": "Insufficient Permission"}`. Other refusals use the error envelope, with `unauthorized` or `user_is_not_authorized`.
  - When: You can't view the source order or edit the target order. Fix: Ask the team owner to adjust your role or assign you to the orders.
- `404` Not found. `page_not_found`: the team slug is unknown, you are not a member of the team, or an id in the path is not one of this team's. `code: null`, with a message that starts "No query results for": there is no record with that id.
  - When: The job or `target_order` isn't your team's. Fix: Check both ids.
- `422` The request was refused and changed nothing. `validation_failed` lists what to fix in `error.fields`; other codes name a business rule, explained in `error.message`. A few checks answer with only `{"message": "..."}`. An operation that calls another service (a payment provider, a carrier, an accounting app) can answer `connect_timeout` ("Please retry again") when that service does not answer in time: the change may or may not have happened there, so read the record back before you retry.
  - When: Lines need choices: `clone_needs_choices`, with the lines in `error.lines` and their jobs in `error.jobs` (directly under `error`, not in `error.data`). Each line has `id`, `job_id`, `product_id`, `product_name`, `product_type`, `quantity` (a decimal string), `reason` (`variant_phased_out`, `variant_deleted`, `product_phased_out` or `product_deleted`) and, for variant products, `product_size`, `product_color`, `size_id` and `color_id`. Each job has `id`, `scoped_id` (`JOB-126`), `name` and `keeps_products`. Fix: Send one `item_choices` entry for each listed line, then call again.
  - When: A choice is refused: `clone_choice_not_needed` (same `error.lines` and `error.jobs`, listing the lines that are on sale again), `clone_choice_unknown_line` (the unknown line ids in `error.ids`) or `clone_replacement_invalid` (the line in `error.line`). Fix: Correct `item_choices` as the rules say, then call again.
- `500` A business rule refused the request (see the operation's errors), or something failed inside YoPrint. Refusals use the error envelope with a code such as `action_not_allowed`; an unexpected failure answers `{"message": "Server Error"}`. Repeating a refusal gets the same answer; after an unexpected failure, read the record back before you retry a change.
  - When: With `clone_job_approval`: an approval includes mockup designs, or it includes files and `clone_media` is false. The body is `{ "message": "Server Error" }` and nothing is copied. Fix: Copy without approvals, or keep `clone_media` on.

## Related

- [Get one job of an order](https://developer.yoprint.com/reference/jobs/v2-job-show.md): `GET /v2/api/store/{team_slug}/v2/sales_order/{hash_id}/job/{job_id}`
- [Update a job](https://developer.yoprint.com/reference/jobs/v2-job-update.md): `PUT /v2/api/store/{team_slug}/v2/job/{hash_id}`
- [Delete a job](https://developer.yoprint.com/reference/jobs/v2-job-destroy.md): `DELETE /v2/api/store/{team_slug}/v2/job/{hash_id}`
