---
title: Approve, Reject, and Reassign
slug: approve-reject-and-reassign
docTags: 
createdAt: 2026-09-04T04:17:01.816Z
---

These three endpoints act on approvals that are already pending. All of them take `approvalIds` — the IDs of individual approval records, which you get from `GET /cpq/approvals` or `GET /cpq/approval-requests`, not the ID of the quote itself.

All three return the same action payload: the resulting `status`, whether the record `isLocked`, and the affected approval rows.

## Approve

```javascript
POST /cpq/approvals:approve
```

| Field         | Type            | Description                                        |
| ------------- | --------------- | -------------------------------------------------- |
| `approvalIds` | array of string | **Required.** Approval record IDs to approve       |
| `message`     | string          | Approver comment recorded on the approval          |
| `actor`       | string          | Salesforce User ID to act on behalf of             |
| `source`      | string          | `UI`, `Email`, `API`, or `Flow`. Defaults to `API` |

```bash
curl -X POST 'https://api.nue.io/cpq/approvals:approve' \
  -H 'nue-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "approvalIds": ["a0UKb000000WxDeMAK"],
    "message": "Margin is within policy for this segment",
    "source": "API"
  }'
```

```json
{
  "status": "SUCCESS",
  "data": {
    "requestId": "a0TKb000000VwCdMAK",
    "recordId": "0Q0Kb000000XyZaKAK",
    "status": "Approved",
    "isLocked": false,
    "approvers": [],
    "approvals": [
      {
        "approvalId": "a0UKb000000WxDeMAK",
        "stepName": "Deal Desk",
        "stepStatus": "Approved",
        "approverUserId": "005Kb000000WxYzIAK",
        "originalApproverUserId": null,
        "reassignedFromApprovalId": null,
        "rejectionMessage": null
      }
    ]
  },
  "warnings": [],
  "errors": []
}
```

Approving the last outstanding approver on a step resolves that step and advances the process. When the final step resolves, the record's overall `status` becomes `Approved` and `isLocked` returns to `false`.

You can pass several `approvalIds` in one call — useful for a bulk-approve action in an approver's queue.

## Reject

```javascript
POST /cpq/approvals:reject
```

| Field         | Type            | Description                                        |
| ------------- | --------------- | -------------------------------------------------- |
| `approvalIds` | array of string | **Required.** Approval record IDs to reject        |
| `reason`      | string          | Rejection reason recorded on the approval          |
| `actor`       | string          | Salesforce User ID to act on behalf of             |
| `source`      | string          | `UI`, `Email`, `API`, or `Flow`. Defaults to `API` |

```bash
curl -X POST 'https://api.nue.io/cpq/approvals:reject' \
  -H 'nue-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "approvalIds": ["a0UKb000000WxDeMAK"],
    "reason": "Discount exceeds policy for a first-year deal. Reduce to 15% or add a two-year term."
  }'
```

:::hint{type="info"}
**Always send a reason**

The reason is what the submitter sees and what an auditor reads later. It is surfaced as `rejectionMessage` on the approval row and on the record's overall status. A rejection without one tells the rep their quote was refused but not what to change.
:::

## Reassign

Routes pending approvals to a different approver.

```javascript
POST /cpq/approvals:reassign
```

| Field           | Type            | Description                                             |
| --------------- | --------------- | ------------------------------------------------------- |
| `approvalIds`   | array of string | **Required.** Approval record IDs to reassign           |
| `newApproverId` | string          | **Required.** ID of the new approver, a user or a group |
| `approverType`  | string          | Type of the target identified by `newApproverId`        |
| `message`       | string          | Comment recorded on the reassignment                    |
| `actor`         | string          | Salesforce User ID to act on behalf of                  |
| `source`        | string          | `UI`, `Email`, `API`, or `Flow`. Defaults to `API`      |

```bash
curl -X POST 'https://api.nue.io/cpq/approvals:reassign' \
  -H 'nue-api-key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "approvalIds": ["a0UKb000000WxDeMAK"],
    "newApproverId": "005Kb000000AbCdEAK",
    "approverType": "User",
    "message": "Dana is out this week"
  }'
```

Reassignment keeps a trail. The resulting row carries:

| Field                      | Meaning                                    |
| -------------------------- | ------------------------------------------ |
| `approverUserId`           | The new approver                           |
| `originalApproverUserId`   | The previous approver's Salesforce User ID |
| `reassignedFromApprovalId` | The approval this row was rerouted from    |

Both trail fields are `null` on rows that were never reassigned, so a delegation chain is explicit rather than something you have to infer from timestamps.

## Common Errors

| Code                | Cause                                                                       |
| ------------------- | --------------------------------------------------------------------------- |
| `MISSING_PARAMETER` | A required field such as `approvalIds` or `newApproverId` was absent        |
| `INVALID_PARAMETER` | A value was malformed, or `source` was outside `UI`, `Email`, `API`, `Flow` |
| `RECORD_NOT_FOUND`  | An approval ID does not exist or is not visible to the acting user          |

All of these return the standard envelope with `status` of `FAILURE`, so you can handle them with the same parsing path as a success.
