---
title: "Confirm an SMS purchase quote"
description: "Buy what the quote binds, with its single-use confirm_token. `POST /api/v1/sms/quotes/{id}/confirm`"
url: https://www.unitpost.com/docs/sms/post-sms-quotes-id-confirm
section: "SMS API reference"
updated: 2026-10-13
---
# Confirm an SMS purchase quote

`POST /api/v1/sms/quotes/{id}/confirm`

Buy what the quote binds, with its single-use `confirm_token`. It charges the workspace: confirm only after the person agreed to the quoted price. Requires an `Idempotency-Key`: a retry with the same key answers what the first call answered and never charges twice. The order runs exactly as in the dashboard: the plan's purchase rules, the one-time fee taken from the plan's allowance before carriers are called and refunded if they refuse the submission, the monthly fee from when the number leases or carriers approve the campaign. Every API purchase counts against the monthly API spend cap the workspace owner sets in the dashboard (the fee due now plus the first month, per UTC month); with no cap set, API purchases are refused. The owner gets an email per purchase, and the activity log names the key. Refusals charge nothing: a changed price (`price_changed`), an expired quote (`quote_expired`), a brand or messaging program edited since (`quote_stale`), a quote already used (`quote_already_used`), an order for the same purchase still in flight (`conflict`, naming its quote). These refusals are answered before the shared hourly limits are spent. A confirm that recorded nothing leaves the quote open (and frees its `Idempotency-Key`); one that reached carriers spends it: `failed` when carriers refused it (quote again), `pending` when their answer is unknown (follow the quote; don't buy the same thing meanwhile). Requires the `sms:purchase` capability.

## Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes | — |
| `Idempotency-Key` | header | string | yes | Any unique string (1–256 characters, e.g. a UUID). Retrying with the same key and the same body within 24 hours returns the first result instead of sending again; the same key with a different body is `409 invalid_idempotent_request`, and one still in flight is `409 concurrent_idempotent_requests`. |

## Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `confirm_token` | string | yes | The quote's confirm token (`sqct_…`), from its create response. Single use. |

## Response fields (200)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `object` | string | yes | — |
| `id` | string | yes | The quote (`squote_…`). |
| `product` | enum | yes | One of: `ten_dlc_brand_registration`, `ten_dlc_brand_vetting`, `ten_dlc_campaign`, `ten_dlc_number`, `long_code_number`, `toll_free_number`. |
| `status` | enum | yes | `open` until confirmed or `expires_at`; `ordered` once placed; `pending` while the outcome is unknown (the carrier didn't answer: a fee due now is held, monthly fees are committed, and it settles to `ordered` or `failed` from the billing ledger, so read it again; don't buy the same thing meanwhile); `failed` when the confirm bought nothing (nothing was charged; quote again). One of: `open`, `expired`, `confirming`, `ordered`, `pending`, `failed`. |
| `label` | string | yes | What is bought, as the dashboard names it. |
| `brand_id` | string \| null | yes | — |
| `registration_id` | string \| null | yes | The registration the order is for (a brand registration, or the campaign whose number it is). |
| `messaging_program_id` | string \| null | yes | — |
| `country` | string \| null | yes | — |
| `due_now_cents` | integer | yes | Charged at the confirm, from the plan's allowance (one-time fees). |
| `monthly_cents` | integer | yes | Charged when the number leases or carriers approve the campaign, then every month. |
| `currency` | string | yes | — |
| `line_items` | object[] | yes | — |
| `covered_by_unitpost` | boolean | yes | This workspace's carrier fees are covered by Unitpost: nothing is charged. |
| `expires_at` | string | yes | Confirm before this (quotes last 10 minutes). |
| `created_at` | string | yes | — |
| `confirmed_at` | string \| null | yes | — |
| `order` | object \| null | yes | What the confirmed order made; null until then. |
| `confirm_token` | string | no | Only in the create response, once: pass it to the confirm. Stored only as a hash; a lost token means a new quote. |

## Responses

| Status | Description |
| --- | --- |
| 200 | The quote, `ordered`, with what the order made. |
| 400 | The body isn't JSON, or the `Idempotency-Key` header is missing (`idempotency_key_required`) or not 1–256 characters. |
| 401 | Missing or invalid API key. |
| 402 | The plan's allowance can't cover the fee now (`sms_allowance_short`, `insufficient_wallet`, `sms_plan_too_small`). |
| 403 | Insufficient scope (`sms:purchase`), no monthly API spend cap set (`api_spend_cap_not_set`), over it (`api_spend_cap_exceeded`), the account under review, the card failing, or SMS not on the plan. |
| 404 | Quote not found, or SMS is not enabled. |
| 409 | `price_changed`, `quote_expired`, `quote_stale`, `quote_already_used`, an order for the same purchase still in flight (`conflict`), a pending plan change, a carrier-side state that refuses the order, or the `Idempotency-Key` in use by another body or a request still running. |
| 422 | The confirm token doesn't match the quote, an unknown key, or values carriers would reject (named in `details`; nothing was charged). |
| 429 | Too many confirms or carrier submissions; honor Retry-After. |
| 500 | Something failed before the purchase was recorded (`internal_error`): nothing was charged, the quote is still open and the `Idempotency-Key` is free, so the same call can be made again. |
| 502 | The carrier didn't answer, so the outcome is unknown (`engine_unavailable`): the quote is `pending` and counted. Follow it until it reads `ordered` or `failed`; don't confirm again or buy the same thing meanwhile. |

## Example request

```bash
curl -X POST 'https://www.unitpost.com/api/v1/sms/quotes/123/confirm' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'User-Agent: my-app/1.0' \
  -H 'Content-Type: application/json' \
  -d '{ }'
```

## Example response (200)

```json
{
  "object": "sms_quote",
  "id": "id_123",
  "product": "ten_dlc_brand_registration",
  "status": "open",
  "label": "string",
  "brand_id": "brand_123",
  "registration_id": "registration_123",
  "messaging_program_id": "messaging_program_123",
  "country": "string",
  "due_now_cents": 0,
  "monthly_cents": 0,
  "currency": "usd",
  "line_items": [
    {
      "fee_code": "string",
      "label": "string",
      "amount_cents": 0,
      "cadence": "one_time",
      "starts_when": "on_confirmation"
    }
  ],
  "covered_by_unitpost": false,
  "expires_at": "2026-01-01T00:00:00.000Z",
  "created_at": "2026-01-01T00:00:00.000Z",
  "confirmed_at": "2026-01-01T00:00:00.000Z",
  "order": {
    "registration_id": "registration_123",
    "number_id": "number_123",
    "status": "queued"
  },
  "confirm_token": "string"
}
```

## Links

- HTML reference (with Node.js, Python, Ruby, PHP, Laravel, Go, Java, Rust, .NET and CLI examples): https://www.unitpost.com/docs/sms/post-sms-quotes-id-confirm
- OpenAPI 3.1 (full schemas): https://www.unitpost.com/api/v1/openapi.json
