> This page is for version v2 (default).
> For other versions, use one of these documentation indexes:
> - v2 (default): https://docs.paid.ai/v-2/llms.txt
> - v1: https://docs.paid.ai/v-1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.paid.ai/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paid.ai/_mcp/server.

# Provisioning from a CRM

> Create or update a customer and their order in Paid from a closed deal in your CRM, in one API call.

When a deal closes in your CRM, one call to `POST /api/v2/provision` sets up everything Paid needs to bill it: the customer, an optional contact, the products on the deal, and the order with its lines. Each record carries the ID it has in your CRM, so you can send the same deal again when it changes, and Paid updates the records it already has instead of creating new ones.

Use this endpoint when you build your own integration between a CRM or quoting tool and Paid.

## Prerequisites

* A Paid API key with the `write:billing` scope. Full-access keys have it.
* A stable ID in your CRM for the account, the deal, each line item, and each product. Paid calls these `externalId`.

## How Paid matches records

Paid never asks for its own IDs in a provision request. It finds each record by the `externalId` you send, and creates the record if none exists.

| In the request          | Matched by            | Result                                               |
| ----------------------- | --------------------- | ---------------------------------------------------- |
| `customer`              | `customer.externalId` | The customer is created or updated.                  |
| `contact`               | `contact.externalId`  | The contact is created or updated. Optional.         |
| `order.lines[].product` | `product.externalId`  | The product is created or updated.                   |
| Product `attributes`    | `name`                | Each attribute is created or updated on its product. |
| `order`                 | `order.externalId`    | The order is created, or a draft order is updated.   |

An order line is one product on the order, with its prices. An attribute is one priced part of a product, such as a monthly platform fee or a usage charge.

## Make a provision request

This request provisions a customer with one order line: a platform fee of \$499.00 per month, billed in advance.

```bash
curl https://api.agentpaid.io/api/v2/provision \
  -H "Authorization: Bearer $PAID_API_KEY" \
  -H "Content-Type: application/json" \
  -H "idempotency-key: deal_88213-2026-09-30T14:05:00Z" \
  -d '{
    "externalReference": {
      "source": "standard",
      "metadata": { "crmDealId": "deal_88213" }
    },
    "customer": {
      "externalId": "acct_4410",
      "name": "Northwind Logistics",
      "email": "billing@northwind.example"
    },
    "order": {
      "externalId": "deal_88213",
      "currency": "USD",
      "startDate": "2026-10-01T00:00:00.000Z",
      "lines": [
        {
          "externalId": "deal_88213_platform",
          "startDate": "2026-10-01T00:00:00.000Z",
          "product": {
            "externalId": "growth_plan",
            "name": "Growth plan",
            "attributes": [
              {
                "name": "Platform fee",
                "pricing": {
                  "chargeType": "recurring",
                  "pricingModel": "perUnit",
                  "billingFrequency": "monthly",
                  "billingType": "advance",
                  "pricePoints": { "currency": "USD", "unitPrice": 49900 }
                }
              }
            ]
          }
        }
      ]
    }
  }'
```

The response gives the Paid ID of each record and says whether it was created or updated:

```json
{
  "status": "created",
  "createdAt": "2026-09-30T14:05:12.000Z",
  "customer": {
    "id": "cus_7Qm2Lx9Pa",
    "provisionStatus": "created",
    "createdAt": "2026-09-30T14:05:12.000Z"
  },
  "order": {
    "id": "ord_3Vk8Ht1Zc",
    "provisionStatus": "created",
    "createdAt": "2026-09-30T14:05:12.000Z",
    "lines": [
      {
        "id": "ordl_5Nw4Rb2Ye",
        "externalId": "deal_88213_platform",
        "provisionStatus": "created",
        "createdAt": "2026-09-30T14:05:12.000Z",
        "lineType": "STANDARD"
      }
    ]
  }
}
```

The order in this request is active, because `order.status` defaults to `active`, so Paid activates it and starts billing. To stage an order without billing it, set `order.status` to `draft`. When the deal closes, provision the order again with `status` set to `active`, or with `status` left out.

## Create, update, or conflict

The status code tells you what happened to the order:

| Order with this `externalId` | Response                           | What Paid does                                                    |
| ---------------------------- | ---------------------------------- | ----------------------------------------------------------------- |
| None                         | `201 Created`, `status: "created"` | Creates the order.                                                |
| A draft order                | `200 OK`, `status: "updated"`      | Updates the order and replaces its lines with the lines you send. |
| An active order              | `409`, code `ORDER_ALREADY_ACTIVE` | Changes nothing.                                                  |

Treat both `200` and `201` as success. The customer, contact, and products are created or updated in both cases.

Paid does not change an active order through a provision request, because the order may already be invoiced. To change an active order, for example to add a product or change a quantity, use the [amendments API](/api-reference/api-reference/amendments/execute-order-amendment). [Amending orders](/documentation/billing/amending-orders) explains what an amendment can change.

## Retries and idempotency keys

Every request needs an `idempotency-key` header. The key lets you retry a request safely after a timeout or a network error: Paid runs a request once for each key. Build the key from the deal and its version, such as the deal ID plus its last-modified time. When the deal changes, the key changes too.

| When you send a key that                                                          | Paid returns                                                         |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| Is new                                                                            | The result of the request.                                           |
| Was used before with the same body                                                | The stored response, with the header `Idempotent-Replayed: true`.    |
| Was used before with a different body                                             | `409`, code `IDEMPOTENCY_KEY_CONFLICT`.                              |
| Belongs to a request that is still running                                        | `409`, code `IDEMPOTENCY_KEY_IN_PROGRESS`. Retry after a short wait. |
| Belongs to a request that saved its changes, but whose result Paid did not record | `409`, code `PROVISION_OUTCOME_UNKNOWN`.                             |

After `PROVISION_OUTCOME_UNKNOWN`, look up the order with [List orders](/api-reference/api-reference/orders/list-orders), filtered by its `externalId`, before you send the deal again with a new key.

If a request fails before Paid saves anything, for example with a `400` validation error, Paid does not store a response for its key. Fix the request and send it again with the same key.

Keys are scoped to your organization.

## Amounts and discounts

All prices are whole numbers in minor currency units, such as cents for USD. `49900` is \$499.00. Paid rejects decimal and negative prices.

To give a discount or a credit, add a separate order line with `lineType` set to `CREDIT`, and send its prices as positive amounts. Paid subtracts a credit line from the invoice. For example, a credit line with a monthly price of `10000` takes \$100.00 off each monthly invoice until the line's `endDate`.

Credit amounts in `creditBenefits` are numbers of credits, not money.

## Choose a source

`externalReference.source` names the system that sends the provision request. Use `standard` for an integration you build yourself.

| Source        | Behaviour                                                                                                                                                                                              |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `standard`    | No source-specific behaviour. Prices are per billing period.                                                                                                                                           |
| `hubspot`     | Same as `standard`.                                                                                                                                                                                    |
| `salesbricks` | Same as `standard`.                                                                                                                                                                                    |
| `salesforce`  | A recurring or seat-based line price is the value for the whole line term. Paid bills it once, as one billing period that spans the line. You cannot combine it with `order.billingFrequencyOverride`. |

## Where the external reference is stored

Paid stores the whole `externalReference` object on the order's `metadata`, under the `externalReference` key. Put the IDs you need to trace an order back to your CRM in `externalReference.metadata`. For the request above, the order's `metadata` contains:

```json
{
  "externalReference": {
    "source": "standard",
    "metadata": { "crmDealId": "deal_88213" }
  }
}
```

Each provision request for the order replaces `externalReference`. Other keys in the order's `metadata` stay as they are. You can read the metadata with [Get order](/api-reference/api-reference/orders/get-order-by-id).

## Related

* [Upsert an order API reference](/api-reference/api-reference/orders/upsert-order-by-external-id)
* [Amend an order](/api-reference/api-reference/amendments/execute-order-amendment)
* [Amending orders](/documentation/billing/amending-orders)
* [List orders](/api-reference/api-reference/orders/list-orders)