---
title: Clone and Template Quotes
slug: clone-and-template-quotes
docTags: 
createdAt: 2026-09-18T12:00:00.000Z
---

# Clone and Template Quotes

Rebuilding a complicated quote line by line is slow and risks silently dropping a negotiated price tag. Three endpoints let you start from a quote that already exists instead: clone any quote, create a new quote from a curated template, or copy a template onto a quote you have already created.

All three carry over line items, applied line price tags and price tiers, and all three are available over REST, through the [Nue Lifecycle Manager MCP](docId\:Cf8cbHyGYpD3OZg7Ym2GY), and as Apex global methods inside the managed package.

## Which one to use

| You have                                                         | You want                                      | Use                            |
| ---------------------------------------------------------------- | --------------------------------------------- | ------------------------------ |
| Any quote — a revision, last year's renewal                      | A new independent copy on the same account    | **Clone Quote**                |
| A curated **template** quote and an opportunity                  | A new quote created and populated in one call | **Create Quote From Template** |
| A curated **template** quote and a quote that **already exists** | The template's lines copied into that quote   | **Copy Quote From Template**   |

## Endpoints

| Endpoint                                       | Method | Description                                            |
| ---------------------------------------------- | ------ | ------------------------------------------------------ |
| `/cpq/quotes/{quoteId}:clone`                  | POST   | Copy any quote into a new Draft quote                  |
| `/cpq/quotes:createFromTemplate`               | POST   | Create a quote on an opportunity from a template quote |
| `/cpq/quotes/{targetQuoteId}:copyFromTemplate` | POST   | Copy a template quote onto an existing quote           |

All three are available on Salesforce-integrated tenants only.

## A template carries the shape of a deal, never a locked price

This is the single most important behaviour on this page, and the one most likely to surprise you.

A **clone** stays on its source's account. Its pricing, negotiated discounts and tax are copied across **unchanged**, because nothing about the customer changed.

A **template** is different. `createFromTemplate` and `copyFromTemplate` always reprice the result against the **target quote's own account**. A discount whose tier depends on the customer — headcount, volume, segment — re-resolves to the target's band, not the template's. Tax is likewise recalculated for the target's own shipping jurisdiction, so a template built for a customer in one city and applied to a customer in another is taxed for the second customer.

Expect the money to differ from the template whenever the two accounts resolve differently. That is the design.

:::hint{type="warning"}
**Tax recalculation is best effort and is not reported back.** A tenant with taxation switched off, or a tax engine that rejects the target's address, still returns `200`. If the tax figure matters, read the quote back and check it.
:::

## Clone Quote

```javascript
POST /cpq/quotes/{quoteId}:clone
```

No request body.

```json
{
  "quote": { "id": "0Q0dh000002cp1xCAA", "name": "Acme Renewal Copy" },
  "lineItemsCopied": 7,
  "priceTagsCopied": 3
}
```

**Key behaviors**

- The source does **not** have to be a template quote. Use this to duplicate any quote.
- The clone is always **Draft** and is never marked primary.
- It is named `<source name/> Copy` and stays on the source's opportunity. The endpoint accepts **no overrides** — to change the name, opportunity, start date or subscription term, follow with [Update Quote](docId\:J9W7UfHJvphiEJ93BX5y9).
- Price tags are always copied. There is no flag to suppress them.
- Cloning a quote with zero line items succeeds and returns an empty Draft quote. That is not an error.
- **Not idempotent.** Each call creates another quote, and a request that times out may still have created one — look for a new Draft quote on the source's opportunity before retrying.

## Create Quote From Template

```javascript
POST /cpq/quotes:createFromTemplate
```

```json
{
  "templateQuoteId": "0Q0dh000002cpBdCAI",
  "opportunityId": "006dh00000ABCDEAA3"
}
```

```json
{
  "quote": { "id": "0Q0dh000002cq44CAA", "name": "Standard Platform Deal" },
  "lineItemsCopied": 4,
  "priceTagsCopied": 2
}
```

**Key behaviors**

- The source **must** be flagged as a template quote (`Ruby__Is_Template_Quote__c = true`). A non-template source is refused with `400` and nothing is created.
- The new quote is **Draft**, never primary, and inherits the template's **price book, subscription term and start date**.
- There is no `priceBookId` parameter: the copy rejects any quote whose price book differs from the template's, so the new quote always takes the template's.
- The endpoint accepts no name, date or term overrides — follow with [Update Quote](docId\:J9W7UfHJvphiEJ93BX5y9).
- **Header custom fields are not inherited.** A template supplies line items, not header values, so set header custom fields yourself on the follow-up update. **Line-level** custom fields *do* copy.
- **Not idempotent.** Each call creates another quote.

## Copy Quote From Template

```javascript
POST /cpq/quotes/{targetQuoteId}:copyFromTemplate
```

```json
{ "templateQuoteId": "0Q0dh000002cpBdCAI" }
```

```json
{
  "targetQuoteId": "0Q0dh000002cq44CAA",
  "skipped": false,
  "lineItemsCopied": 4,
  "priceTagsCopied": 2
}
```

**Key behaviors**

- The target must **already exist**. This endpoint never creates a quote.
- The template and target **price books must match**, and the template must have a price book.
- **Header custom fields are not copied** from the template; line-level custom fields are.

### Always read `skipped`

A repeat call is not always a repeat copy, and the response is `200` either way.

When the target already **records** this same template as the one it was built from, the call is a deliberate no-op: `skipped: true`, `lineItemsCopied: 0`, and nothing is repriced or re-taxed.

That recording is what decides it — not the line items themselves — and **only quotes created by&#x20;**`createFromTemplate`**&#x20;carry it**. A quote you assembled any other way records no template, so a second copy onto it is refused with `409`. Do not treat this endpoint as idempotent for a target you built yourself: read the target first.

## Error responses

| Status | When                                                                                                                                      |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Invalid request; the source is not a template quote; the template has no price book; the template and target price books differ           |
| `404`  | The source, template or target quote was not found                                                                                        |
| `409`  | *(copyFromTemplate only)* The target already has lines and does not record this template. Not retryable without clearing the target first |
| `500`  | Internal server error. The outcome is unknown — look for a newly created Draft quote before retrying, or you may create a second one      |

## Through the Nue Lifecycle Manager MCP

| Tool                      | Endpoint it calls                                                 |
| ------------------------- | ----------------------------------------------------------------- |
| `cloneQuote`              | `/cpq/quotes/{quoteId}:clone`, then `:update` for any override    |
| `createQuoteFromTemplate` | `/cpq/quotes:createFromTemplate`, then `:update` for any override |
| `copyQuoteFromTemplate`   | `/cpq/quotes/{targetQuoteId}:copyFromTemplate`                    |

The tools add conveniences the REST contract does not have. `cloneQuote` and `createQuoteFromTemplate` accept `name`, `startDate` and `subscriptionTerm` — and `createQuoteFromTemplate` also accepts `customFields` — and apply them as a follow-up repricing update. If the quote is created but that update fails, the tool returns `PARTIAL_SUCCESS` and names the quote that *was* created, so it can be corrected rather than orphaned silently.

## Apex global methods

Available inside the managed package for Flows and Apex callers.

```apex
Quote clone = new Ruby.GlobalQuoteService().cloneQuote('0Q04w000002RzITCA0');

Quote fromTemplate = new Ruby.GlobalQuoteService()
    .createQuoteFromTemplate('0Q04w000002RzITCA1', '006dh00000ABCDEAA3');

new Ruby.GlobalQuoteService()
    .copyQuoteFromTemplate('0Q04w000002RzITCA1', '0Q04w000002RzITCA0');
```

:::hint{type="info"}
**One deliberate difference between Apex and REST.** `copyQuoteFromTemplate` is a released global method, so it still accepts an ordinary quote as its source. The REST and MCP surfaces refuse a non-template source, so that they agree with `createFromTemplate`. Existing Apex and Flow callers are unaffected.
:::

## Related

- [Create Quote Overview](docId\:BC-IdxYnwAUS7O9EFYCwq)
- [Update Quote Overview](docId\:J9W7UfHJvphiEJ93BX5y9)
- [Price Tags](docId\:Z5zOtt7ESzhMHAXIFqMKr)
- [Finalize Quotes to Orders](docId:7OBzQa-PnrgWfFKJrk9gS)
