---
title: Finalize Quotes to Orders
slug: finalize-quotes-to-orders
docTags: 
createdAt: 2026-09-18T12:00:00.000Z
---

# Finalize Quotes to Orders

Finalizing is the quote-to-revenue step: an accepted quote becomes an order, and an activated order provisions the subscriptions, assets and entitlements the customer actually bought. This page covers the three operations that carry a quote across that line and back again — **finalize**, **activate**, and **cancel**.

## Endpoints

| Endpoint                       | Method | Description                                                        |
| ------------------------------ | ------ | ------------------------------------------------------------------ |
| `/cpq/quotes:finalize`         | POST   | Convert one or more quotes into orders, optionally activating them |
| `/orders/{orderId}`            | POST   | Activate a draft order                                             |
| `/cpq/orders/{orderId}:cancel` | POST   | Cancel an activated order                                          |

`/cpq/quotes:finalize` and `/cpq/orders/{orderId}:cancel` are on the CPQ API and are available on Salesforce-integrated tenants only. Activation is served by the Nue order service.

## Draft or activated

Finalizing has two modes, and the difference is what gets provisioned.

|                | `activateOrders: false` *(default)*        | `activateOrders: true`                                      |
| -------------- | ------------------------------------------ | ----------------------------------------------------------- |
| **Creates**    | A **Draft** order                          | A Draft order, then activates it                            |
| **Provisions** | Nothing                                    | Subscriptions, assets and entitlements, by product category |
| **Reversible** | Yes — a Draft order can still be corrected | Not in practice. Undoing it means cancelling the order      |

Leaving the order in Draft and activating it separately is the same shape the Salesforce UI uses, and it is the safer default when an agent or an integration is driving.

## Finalize Quotes

```javascript
POST /cpq/quotes:finalize
```

```json
{
  "quoteIds": ["0Q0dh000002cp1xCAA"],
  "activateOrders": false
}
```

```json
{
  "orders": [
    {
      "orderId": "801dh00000zZQFNAA4",
      "orderNumber": "00000142",
      "status": "Draft",
      "quoteId": "0Q0dh000002cp1xCAA"
    }
  ],
  "warnings": []
}
```

**Key behaviors**

- At most **50** quote ids per call. A larger batch is rejected before any work is done.
- The batch is **atomic**: one unknown id finalizes nothing. A repeated id is finalized once.
- Activation is synchronous but **provisioning is not**. Subscriptions appear shortly afterwards, so read them back rather than reporting them from this response — it does not list them.
- On a **quote-split** tenant one quote can produce several orders. Do not assume one order per quote id.
- A tenant may additionally require the quote be primary, or its status be exactly `Approved` — note that is Approved, not Accepted.

:::hint{type="warning"}
**Finalizing is not idempotent, and it is not blocked.** Finalizing a quote that already has an order creates a **second** order on a normal tenant, and activates the existing draft on a quote-split tenant. Check whether the quote already has an order before finalizing it again. A `500` leaves the outcome unknown — read the quote before retrying.
:::

## Activate Order

```javascript
POST /orders/{orderId}
```

```json
{ "options": { "generateInvoice": false, "activateInvoice": false } }
```

```json
{ "data": { "success": true } }
```

Activation turns a Draft order — new or change — into an Active order, which is what generates subscriptions, assets and entitlements. On Salesforce the work is done by the Order trigger when `Status` becomes `Activated`, which is why the endpoint only has to stamp the field.

The response does **not** list what was provisioned. Read the subscriptions back separately.

Salesforce orgs also expose this as an Apex REST resource:

```javascript
POST /services/apexrest/Ruby/activate-order
{ "id": "801dh00000zZQFNAA4" }
```

which answers `{ "success": true }`, or `{ "success": false, "errorCode": "...", "message": "..." }` with a `400`.

## Cancel Order

```javascript
POST /cpq/orders/{orderId}:cancel
```

No request body, and no response body on success.

Cancelling unwinds the order's subscriptions, assets and entitlements to their previous version and reverses their daily and monthly ARR records.

:::hint{type="danger"}
**This is irreversible.** A cancelled order cannot be reactivated.
:::

**Key behaviors**

- Only **Activated** orders can be cancelled. A Draft order has provisioned nothing — delete it instead.
- The `CancelOrder` permission and the bundle-child dependency check are enforced **server-side**.
- An order with dependent orders, a split order, or one with superseding changes is refused.
- Salesforce-origin orders only.
- A `504` means the outcome could **not be confirmed**, not that the cancel failed. Read the order's status before retrying.

To check eligibility first, call `POST /orders/cancelable` with `{ "orderIds": [...] }`. It reports only that no active invoice blocks the order — it does not check order status, activation state, or dependent orders, all of which are enforced when you cancel.

## Error responses

| Status | When                                                                                                                                                                                                                 |
| ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | More than 50 quote ids, an empty list, an unresolved change-order line, or a tax-calculation error on the quote. For cancel: dependent orders, already cancelled, never activated, or the caller lacks `CancelOrder` |
| `404`  | One or more quotes were not found. Nothing was finalized                                                                                                                                                             |
| `409`  | Cancel via Apex is not enabled for this tenant                                                                                                                                                                       |
| `500`  | Internal server error. The outcome is unknown — read the quote before retrying, or it may be finalized twice                                                                                                         |
| `504`  | The cancel outcome could not be confirmed                                                                                                                                                                            |

## Through the Nue Lifecycle Manager MCP

| Tool                   | Endpoint it calls                                                         |
| ---------------------- | ------------------------------------------------------------------------- |
| `finalizeQuoteToOrder` | `/cpq/quotes:finalize`                                                    |
| `activateOrder`        | `/orders/{orderId}`                                                       |
| `cancelOrder`          | `/cpq/orders/{orderId}:cancel`, plus `/orders/cancelable` for the preview |

All three are gated, because all three are hard to undo. `finalizeQuoteToOrder` defaults to a Draft order and requires `confirm: true` before it will activate; asking to activate without confirming is **not** an error — it returns `mode: "preview"` describing the quotes involved and writes nothing. `activateOrder` and `cancelOrder` write nothing unless `commit` is true, and a cancel preview lists the subscriptions, assets and entitlements the cancel would unwind so the blast radius is visible before anyone agrees to it.

## Apex global methods

```apex
List<Id> quoteIds = new List<Id>{ '0Q04w000002RzITCA0' };
List<Order> orders = new Ruby.GlobalQuoteService().generateOrders(quoteIds, true);

// Split quotes: finalize selected buckets into one order each, a batch at a time.
List<Order> bucketOrders = new Ruby.GlobalQuoteService()
    .generateOrders(quoteId, lineBucketIds, true);

new Ruby.GlobalOrderService().cancelActiveOrders(new List<Id>{ '8014w000008LIDXAA4' });
```

There is **no** global method for activating an order — activation happens when an order's `Status` becomes `Activated`, so a Flow or Apex caller can stamp the field directly, or call the `/services/apexrest/Ruby/activate-order` resource above. Cancelling is the opposite: a direct status write to `Cancelled` is refused, and the dedicated cancel path must be used.

:::hint{type="info"}
**Bucket activation is best effort.** When `generateOrders(quoteId, lineBucketIds, true)` is used and an order's start date has to be recalculated, activation completes asynchronously — a returned order may still be `Draft`. Poll `Order.Status` to confirm.
:::

## Related

- [Create Order Overview](docId\:qDvlx6WS2ASCbu8HwFrZF)
- [Update Order Overview](docId\:Gm0OXVHu351idX6gf9oQa)
- [Clone and Template Quotes](docId\:maBR2n8tAOG87gv-Heykp)
- [Activate Draft Orders](docId\:dy-hR7uB-FcsX3ktpFtTE)
