# Purchasing API

URL: https://docs.sparklayer.io/developers/api/purchasing

Store orders, quotes and carts shown in My Account with the Purchasing API, using purchase identifiers, packages and purchase transactions. Lists all endpoints.

> **For AI assistants:** these facts apply to every SparkLayer API call.
>
> - **Base URLs:** Live `https://app.sparklayer.io`, test `https://test.app.sparklayer.io`. Each environment has its own data and its own API keys.
> - **Access:** API access needs the Pro or Enterprise plan. Create credentials (Site ID, Client ID, Client Secret) in the SparkLayer Dashboard at Settings > API.
> - **Access token:** `POST {base}/api/auth/token` with a `Site-Id` header and a JSON body (not form-encoded): `{"grant_type":"client_credentials","client_id":"…","client_secret":"…"}`. It returns `access_token`, valid for 3,600 seconds. There is no refresh token: cache the token and request a new one shortly before it expires.
> - **Every request:** `Authorization: Bearer <access_token>` and `Site-Id: <site id>`, plus `Content-Type: application/json` when there is a body. Set a `User-Agent` that names your integration.
> - **Errors:** RFC 7807 problem details: `title`, `status`, `detail` and an optional `errors[]` of `{ code, message, property }`. Some error responses have no body. Retry `5xx`, `429` (honouring `Retry-After`) and concurrent-update `409`s with exponential backoff; on `401`, get a new token once and retry; don't retry other `4xx` without changing the request.
> - **Pagination:** Most list endpoints return everything. `GET /api/v2/price-lists` pages with `page` and `page_size` (up to 250) until `pagination.current_page` equals `total_pages`. `GET /api/v1/purchases` uses `limit` (up to 500) and `offset`: stop when a page has fewer than `limit` results.
> - **Ground rules:** Use only endpoints, fields and SDK methods that the docs or OpenAPI specs define. Prefer `GET /api/v2/price-lists` over the deprecated v1. Products are matched by SKU. Keep credentials in environment variables or a secrets manager, never in code.
> - **Read the docs as Markdown:** Add `.md` to any page URL. Index of every page: https://docs.sparklayer.io/llms.txt. Every API operation, compactly: https://docs.sparklayer.io/llms-api.txt. The developer guides in full: https://docs.sparklayer.io/developers/llms.txt.
> - **OpenAPI specs:** `core`, `ordering`, `pricing`, `purchasing`, `stock`, `files`, `sync-log`: https://docs.sparklayer.io/openapi/<api>.yaml (also `.json`). Ignite: https://docs.sparklayer.io/openapi/ignite.yaml.
> - **MCP:** Search and read these docs from your assistant with the docs MCP server at https://docs.sparklayer.io/mcp.

The Purchasing API stores your customers' purchases: carts, quotes, purchases awaiting approval or confirmation, and placed orders. Use it to keep the orders shown in My Account up to date.

> **Purchases aren't sent to your eCommerce platform**
>
> Purchases you create with this API show in the My Account area for your B2B customers, but SparkLayer doesn't push them to your eCommerce platform (such as Shopify). To have an order on your platform too, create it with the platform's own API, such as the [Shopify order API](https://shopify.dev/docs/api/admin-rest/latest/resources/order).

For orders created on your platform to sync to SparkLayer, they need a few properties, which depend on your platform. On Shopify, each order needs:

- The `b2b` tag
- The `sparklayer.order_import` metafield, set to `true`

See [Import order history into SparkLayer](https://docs.sparklayer.io/help/platforms/shopify/metafields.md#import-order-history-into-sparklayer) for the Shopify steps.

## Creating and updating purchases

Create and update purchases with [Create or update a purchase](https://docs.sparklayer.io/developers/api/purchasing/create-update-purchase.md) (`PUT /api/v1/purchases/{lookupBy}/{identifier}`). If no purchase matches `lookupBy` and `identifier`, a new one is created.

- **Send the full purchase every time.** The request replaces the purchase's previous data, so include every field, not just the ones that changed. This prevents stale data and keeps order integrations simple.
- **Leave out calculated fields.** The API calculates them, so don't set them in the request body.
- **Expect a `400` for missing or invalid values.** The error identifies the value to fix.

### Purchase identifiers

The `purchase_identifiers` object holds the purchase's IDs in each system. Use one of them as `lookupBy`:

| Identifier | Description |
| --- | --- |
| `sparklayer` | SparkLayer's ID for the purchase (a UUID). For an order placed through SparkLayer, this is the ID of the SparkLayer cart, which SparkLayer passes to your eCommerce platform when the customer completes checkout. Send it so the order moves from `awaiting_merchant` to `order` and replaces the cart. For other orders, leave it blank. |
| `platform` | The purchase's ID on your eCommerce platform. |
| `internal` | Your ID for the purchase, such as the order number in your ERP or order management system (OMS). |
| `visible` | The ID shown to the customer in their recent orders. |

### Purchase types

Most integrations send purchases of type `order`.

| Purchase type | Description |
| --- | --- |
| `quote` | A quote, with a quote status. |
| `awaiting_approval` | A purchase waiting for another person at the customer's company to approve it, typically with [company users](https://docs.sparklayer.io/help/customers/company-users.md). |
| `awaiting_merchant` | An order completed in SparkLayer but not yet confirmed, because it's pending on the eCommerce platform or the sync hasn't finished. |
| `order` | A finalised order, saved on the eCommerce platform or in your order source of truth, such as an ERP or OMS. |

### Packages

A purchase is mostly made up of packages, which hold the line items, shipping methods, addresses and IDs.

1. When you first create an order, put its line items in a `processing` package.
2. As the order progresses, move items to the relevant packages, such as `shipment`, `return` or `cancelled`. You can have more than one of each, for example for partial shipments.

The order status customers see is calculated from the package dates, such as when each package was created and shipped.

## Purchase transactions

Record payments, refunds and other transactions against a purchase with [Create or update a purchase transaction](https://docs.sparklayer.io/developers/api/purchasing/create-or-update-a-purchase-transaction.md). SparkLayer uses the transactions to calculate the purchase's payment status.

If you record transactions with this API, don't also send them to your eCommerce platform: the same transaction could be recorded twice.

- **Identify the purchase** in the request URL, with `lookupBy` and `identifier`. A purchase can have many transactions, but each needs a unique `platform_id`: the ID the third-party system (such as your payment provider) uses for the transaction. `purchase_spark_id` in the response is read-only, so don't send it.
- **Set the amount** in `total`: positive for a payment, negative for a refund. A `currency_code` is also required. 
- **Choose whether it counts** with `apply_to_balance`.

| `apply_to_balance` | What happens |
| --- | --- |
| `false` | The transaction is stored for information only, and isn't used to calculate the payment status. The [list endpoint](https://docs.sparklayer.io/developers/api/purchasing/get-purchase-transactions.md) returns it, but nothing else happens. Use this for pending or failed transactions, which can help with debugging. |
| `true` | The transaction counts towards the purchase's payment status. SparkLayer also adds an entry to the purchase history, which the customer sees, and adjusts the outstanding balance of the purchase's customer by the `total`. |

Changing a transaction from `true` to `false` deletes the history entry it created, and reverses the change to the customer's balance and payment status.

Use `full_transaction` to store the full transaction, as a JSON string, as the third-party system holds it. SparkLayer stores it as given, without redacting anything, so remove or obscure sensitive data, such as full card numbers, before you send it.

## Endpoints

- [GET Get a purchase](https://docs.sparklayer.io/developers/api/purchasing/get-purchase.md)
- [DELETE Delete a purchase](https://docs.sparklayer.io/developers/api/purchasing/delete-purchase.md)
- [PUT Create or update a purchase](https://docs.sparklayer.io/developers/api/purchasing/create-update-purchase.md)
- [GET List purchases](https://docs.sparklayer.io/developers/api/purchasing/get-purchases.md)
- [GET Get a customer's SKU order stats](https://docs.sparklayer.io/developers/api/purchasing/get-order-stats-for-a-customer.md)
- [GET List purchase history](https://docs.sparklayer.io/developers/api/purchasing/get-purchase-history.md)
- [POST Create a purchase history entry](https://docs.sparklayer.io/developers/api/purchasing/create-a-purchase-history-entry.md)
- [GET List purchase transactions](https://docs.sparklayer.io/developers/api/purchasing/get-purchase-transactions.md)
- [PUT Create or update a purchase transaction](https://docs.sparklayer.io/developers/api/purchasing/create-or-update-a-purchase-transaction.md)
- [GET Get the store currency](https://docs.sparklayer.io/developers/api/purchasing/get-store-currency.md)
- [PUT Update the store currency](https://docs.sparklayer.io/developers/api/purchasing/update-store-currency.md)

## Next steps

- [Authentication](https://docs.sparklayer.io/developers/authentication.md): get an access token for your requests.
- [Ordering API](https://docs.sparklayer.io/developers/api/ordering.md): place orders on behalf of customers.
- [Files API](https://docs.sparklayer.io/developers/api/files.md): download the files customers attach to orders.
