> ## Documentation Index
> Fetch the complete documentation index at: https://dev.phygitals.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Start a purchase

> Initiates a purchase and returns a session ID to poll for fulfillment.

Initiates a purchase from a pack. Returns a `session_id` that you pass to [`/api/v1/vm/buy/status`](/api-reference/purchase/status) to poll for the pulled items.

<Note>
  **Prerequisites:** The pack must have `in_stock: true` and `enable: true`. `amount` must be between `1` and the pack's `max_per_mint`.
</Note>

## Request body

<ParamField body="id" type="string" required>
  Pack ID from [`/api/v1/vm/available`](/api-reference/packs/list).
</ParamField>

<ParamField body="amount" type="number" required>
  Number of pulls. Must be `≥ 1` and `≤ max_per_mint`.
</ParamField>

<ParamField body="user_id" type="string" required>
  Partner-defined identifier for the end user making the purchase. Pulled items are associated with this `user_id` for inventory lookup, sellback, and shipping.
</ParamField>

## Example request

```http theme={"dark"}
POST /api/v1/vm/buy/init
X-API-Key: <your_api_key>
Content-Type: application/json

{
  "id": "BSG6Dy...",
  "amount": 2,
  "user_id": "user_abc123"
}
```

## Response

<ResponseExample>
  ```json 200 theme={"dark"}
  {
    "session_id": "550e8400-e29b-41d4-a716-446655440000",
    "nfts": [
      {
        "id": "9XnY...mint_address",
        "content": {
          "metadata": {
            "name": "Juan Soto RC Auto /25",
            "image": "https://cdn.phygitals.com/cards/soto-rc-auto.png",
            "back_image": "https://cdn.phygitals.com/cards/soto-rc-auto-back.png",
            "attributes": [
              { "trait_type": "Year", "value": "2024" },
              { "trait_type": "Category", "value": "Baseball" }
            ]
          }
        },
        "buyback_price": 8.50
      }
    ]
  }
  ```
</ResponseExample>

<ResponseField name="session_id" type="string">
  Unique session identifier. Pass to [`/api/v1/vm/buy/status`](/api-reference/purchase/status) to poll for fulfillment.
</ResponseField>

<ResponseField name="nfts" type="array">
  Array of pulled items.
</ResponseField>

<ResponseField name="nfts[].id" type="string">
  Item identifier. Used as `item_id` in [`/api/v1/vm/buyback`](/api-reference/sellback).
</ResponseField>

<ResponseField name="nfts[].content.metadata" type="object">
  Item metadata: `name`, `image`, `back_image`, and `attributes`.
</ResponseField>

<ResponseField name="nfts[].content.metadata.back_image" type="string | null">
  CDN URL for the card back image, or `null` when not available.
</ResponseField>

<ResponseField name="nfts[].buyback_price" type="number">
  The sellback value in USD.
</ResponseField>

<ResponseField name="nfts[].type" type="string">
  Item source. One of: `enft`, `ebay`, `fanatics`, `alt`, `external`.
</ResponseField>

<ResponseField name="nfts[].mint_address" type="string | null">
  On-chain mint address. `null` for items that haven't been minted (in [sandbox](/enterprise-api/sandbox), always `null` for ebay-sourced items).
</ResponseField>

<ResponseField name="nfts[].collection_address" type="string | null">
  On-chain collection address.
</ResponseField>

<ResponseField name="nfts[].token_standard" type="string | null">
  Token standard for on-chain items.
</ResponseField>

See the [item shape reference](/item-shape) for the full canonical schema.

## Errors

| Status | Body                                                    | Cause                                              |
| ------ | ------------------------------------------------------- | -------------------------------------------------- |
| `400`  | `{ "error": "Claw machine is out of stock" }`           | Pack is not currently purchasable. See note below. |
| `400`  | `{ "error": "Amount must be greater than 0" }`          | Invalid amount                                     |
| `400`  | `{ "error": "Amount must be less than or equal to N" }` | Exceeds `max_per_mint`                             |
| `400`  | `{ "error": "VM not found" }`                           | Unknown pack ID                                    |
| `400`  | `{ "error": "user_id is required" }`                    | Missing `user_id`                                  |
| `500`  | `{ "error": "Error during transaction" }`               | Internal server error                              |

<Note>
  `Claw machine is out of stock` is returned when the pack is not currently purchasable. In [sandbox](/enterprise-api/sandbox), this single error covers three distinct underlying conditions: `enable: false`, `in_stock: false`, or no candidate listings available in any rarity tier at request time. Production may differentiate these with distinct messages in the future. Partners should treat any "out of stock" 400 as terminal for this pack and re-fetch [`/api/v1/vm/available`](/api-reference/packs/list) before retrying.
</Note>

## Sandbox notes

In [sandbox](/enterprise-api/sandbox), item selection is `Math.random()`-weighted across the pack's `rarity_distribution.weight` field, then a uniform random pick within the chosen tier's FMV range. **Items are not reserved or removed from inventory.** The same listing can be returned to other `user_id`s on subsequent calls. Sandbox `buy/init` also fulfills synchronously, so the items are already pulled by the time the response arrives. Calling `buy/init` twice with the same body produces two distinct `session_id`s and stacks the items.
