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

# Creating Products, Kits, and Variants

> One POST call to create a fully assembled product graph: variant parent with kit variants, qty-break pricing, and component assemblies.

## The atomic-create principle

The Arcus Products API accepts the **complete nested resource graph in a single POST**. You express what you want; the API creates every child in one atomic transaction. If any child fails, the entire request rolls back -- no orphan products, no partial state.

## product\_type discriminator

Every product has a `product_type` that controls which children are valid:

| product\_type    | What it is                    | Inline children accepted                                                        |
| ---------------- | ----------------------------- | ------------------------------------------------------------------------------- |
| `physical`       | Standalone stockable item     | `pricing[]`, `vendors[]`                                                        |
| `service`        | Non-stockable service line    | `pricing[]`, `vendors[]`                                                        |
| `kit`            | Bill-of-materials assembly    | `components[]`, `pricing[]`, `vendors[]`                                        |
| `box`            | Packaging unit (also a kit)   | `components[]`, `pricing[]`, `vendors[]`                                        |
| `variant_parent` | Parent with multiple variants | `variants[]`, each variant may include `components[]`, `pricing[]`, `vendors[]` |

## Full example: variant parent with kit variants

A "Riser Kit" with two kit variants -- Dome and Flat -- each with their own component assemblies, three qty-break pricing tiers, and a vendor assignment. One POST call.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.arcuserp.com/v1/products \
    -H "Authorization: Bearer ark_live_ent_wss_abc123" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: riser-kit-2026-05-12-001" \
    -d '{
      "title": "Riser Kit",
      "product_type": "variant_parent",
      "description": "Modular riser kit with dome or flat options",
      "variants": [
        {
          "title": "Dome Riser Kit",
          "sku": "RISER-DOME",
          "product_type": "kit",
          "components": [
            {"component_product_id": "prod_stone_uuid", "quantity": 2},
            {"component_product_id": "prod_dome_lid_uuid", "quantity": 1}
          ],
          "pricing": [
            {"pricing_level_id": "pl_default_uuid", "min_quantity": 1,  "list_price": 49.99},
            {"pricing_level_id": "pl_default_uuid", "min_quantity": 5,  "list_price": 44.99},
            {"pricing_level_id": "pl_default_uuid", "min_quantity": 10, "list_price": 39.99}
          ],
          "vendors": [
            {"vendor_id": "acct_acme_uuid", "vendor_sku": "RD-001", "default_unit_cost": 18.00}
          ]
        },
        {
          "title": "Flat Riser Kit",
          "sku": "RISER-FLAT",
          "product_type": "kit",
          "components": [
            {"component_product_id": "prod_stone_uuid", "quantity": 2},
            {"component_product_id": "prod_flat_lid_uuid", "quantity": 1}
          ],
          "pricing": [
            {"pricing_level_id": "pl_default_uuid", "min_quantity": 1,  "list_price": 44.99},
            {"pricing_level_id": "pl_default_uuid", "min_quantity": 5,  "list_price": 39.99},
            {"pricing_level_id": "pl_default_uuid", "min_quantity": 10, "list_price": 34.99}
          ]
        }
      ],
      "vendors": [
        {"vendor_id": "acct_acme_uuid", "vendor_sku": "RISER-PARENT", "default_unit_cost": 15.00}
      ]
    }'
  ```

  ```typescript TypeScript SDK theme={null}
  import Arcus from '@arcuserp/sdk';

  const arcus = new Arcus({ apiKey: 'ark_live_ent_wss_abc123' });

  const product = await arcus.products.create({
    title: 'Riser Kit',
    product_type: 'variant_parent',
    description: 'Modular riser kit with dome or flat options',
    variants: [
      {
        title: 'Dome Riser Kit',
        sku: 'RISER-DOME',
        product_type: 'kit',
        components: [
          { component_product_id: 'prod_stone_uuid', quantity: 2 },
          { component_product_id: 'prod_dome_lid_uuid', quantity: 1 },
        ],
        pricing: [
          { pricing_level_id: 'pl_default_uuid', min_quantity: 1,  list_price: 49.99 },
          { pricing_level_id: 'pl_default_uuid', min_quantity: 5,  list_price: 44.99 },
          { pricing_level_id: 'pl_default_uuid', min_quantity: 10, list_price: 39.99 },
        ],
        vendors: [
          { vendor_id: 'acct_acme_uuid', vendor_sku: 'RD-001', default_unit_cost: 18.00 },
        ],
      },
      {
        title: 'Flat Riser Kit',
        sku: 'RISER-FLAT',
        product_type: 'kit',
        components: [
          { component_product_id: 'prod_stone_uuid', quantity: 2 },
          { component_product_id: 'prod_flat_lid_uuid', quantity: 1 },
        ],
        pricing: [
          { pricing_level_id: 'pl_default_uuid', min_quantity: 1,  list_price: 44.99 },
          { pricing_level_id: 'pl_default_uuid', min_quantity: 5,  list_price: 39.99 },
          { pricing_level_id: 'pl_default_uuid', min_quantity: 10, list_price: 34.99 },
        ],
      },
    ],
    vendors: [
      { vendor_id: 'acct_acme_uuid', vendor_sku: 'RISER-PARENT', default_unit_cost: 15.00 },
    ],
  }, {
    headers: { 'Idempotency-Key': 'riser-kit-2026-05-12-001' },
  });

  console.log(product.data.id);
  console.log(product.data.variants[0].pricing[0].list_price); // 49.99 (number, not string)
  ```

  ```python Python SDK theme={null}
  import arcuserp

  client = arcuserp.Arcus(api_key="ark_live_ent_wss_abc123")

  product = client.products.create(
      title="Riser Kit",
      product_type="variant_parent",
      description="Modular riser kit with dome or flat options",
      variants=[
          {
              "title": "Dome Riser Kit",
              "sku": "RISER-DOME",
              "product_type": "kit",
              "components": [
                  {"component_product_id": "prod_stone_uuid", "quantity": 2},
                  {"component_product_id": "prod_dome_lid_uuid", "quantity": 1},
              ],
              "pricing": [
                  {"pricing_level_id": "pl_default_uuid", "min_quantity": 1,  "list_price": 49.99},
                  {"pricing_level_id": "pl_default_uuid", "min_quantity": 5,  "list_price": 44.99},
                  {"pricing_level_id": "pl_default_uuid", "min_quantity": 10, "list_price": 39.99},
              ],
              "vendors": [
                  {"vendor_id": "acct_acme_uuid", "vendor_sku": "RD-001", "default_unit_cost": 18.00},
              ],
          },
          {
              "title": "Flat Riser Kit",
              "sku": "RISER-FLAT",
              "product_type": "kit",
              "components": [
                  {"component_product_id": "prod_stone_uuid", "quantity": 2},
                  {"component_product_id": "prod_flat_lid_uuid", "quantity": 1},
              ],
              "pricing": [
                  {"pricing_level_id": "pl_default_uuid", "min_quantity": 1,  "list_price": 44.99},
                  {"pricing_level_id": "pl_default_uuid", "min_quantity": 5,  "list_price": 39.99},
                  {"pricing_level_id": "pl_default_uuid", "min_quantity": 10, "list_price": 34.99},
              ],
          },
      ],
      vendors=[
          {"vendor_id": "acct_acme_uuid", "vendor_sku": "RISER-PARENT", "default_unit_cost": 15.00},
      ],
      extra_headers={"Idempotency-Key": "riser-kit-2026-05-12-001"},
  )

  print(product.data.id)
  print(product.data.variants[0].pricing[0]["list_price"])  # 49.99 (float, not string)
  ```
</CodeGroup>

The response includes every child hydrated inline:

```json theme={null}
{
  "data": {
    "id": "prod_abc123",
    "title": "Riser Kit",
    "product_type": "variant_parent",
    "entity_id": "ent_wss_abc",
    "variants": [
      {
        "id": "pv_dome_uuid",
        "title": "Dome Riser Kit",
        "sku": "RISER-DOME",
        "components": [
          { "id": "kc_uuid1", "component_product_id": "prod_stone_uuid", "quantity_per_kit": 2 },
          { "id": "kc_uuid2", "component_product_id": "prod_dome_lid_uuid", "quantity_per_kit": 1 }
        ],
        "pricing": [
          { "id": "pp_uuid1", "pricing_level_id": "pl_default_uuid", "qty_break": 1,  "list_price": 49.99 },
          { "id": "pp_uuid2", "pricing_level_id": "pl_default_uuid", "qty_break": 5,  "list_price": 44.99 },
          { "id": "pp_uuid3", "pricing_level_id": "pl_default_uuid", "qty_break": 10, "list_price": 39.99 }
        ],
        "vendors": [
          { "id": "pv_uuid1", "vendor_id": "acct_acme_uuid", "vendor_sku": "RD-001", "default_unit_cost": 18.00 }
        ]
      }
    ],
    "vendors": [
      { "id": "pv_parent_uuid", "vendor_id": "acct_acme_uuid", "vendor_sku": "RISER-PARENT" }
    ]
  }
}
```

## Qty-break pricing with `pricing_level_id`

Each `pricing[]` element defines one row in a tiered price book:

| Field              | Required                    | Description                                                                                                                                               |
| ------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pricing_level_id` | Yes (or `account_id`)       | The pricing level this tier applies to. Use `GET /v1/pricing-levels` to list available levels.                                                            |
| `account_id`       | Yes (or `pricing_level_id`) | Account-specific override price instead of a level-based price.                                                                                           |
| `min_quantity`     | No                          | The minimum order quantity that activates this tier. Defaults to 1. Each tier must have a unique `min_quantity` within the same `pricing_level_id` scope. |
| `list_price`       | No                          | The sell price at this tier.                                                                                                                              |
| `sell_price`       | No                          | Explicit sell price (overrides `list_price` if both provided).                                                                                            |

All money fields (`list_price`, `sell_price`, `adjustment`) are returned as JavaScript numbers (float), not strings.

## Idempotency

Include `Idempotency-Key: <your-unique-key>` on every create call. Replaying the same key within 24 hours returns the original response without re-creating resources.

```bash theme={null}
curl -X POST https://api.arcuserp.com/v1/products \
  -H "Idempotency-Key: order-import-batch-2026-05-12-row-42" \
  ...
```

## Standalone kit (no variants)

```bash theme={null}
curl -X POST https://api.arcuserp.com/v1/products \
  -H "Authorization: Bearer ark_live_ent_wss_abc123" \
  -d '{
    "title": "Dome Lid Assembly",
    "product_type": "kit",
    "sku": "DLA-001",
    "components": [
      {"component_product_id": "prod_dome_uuid", "quantity": 1},
      {"component_product_id": "prod_gasket_uuid", "quantity": 2}
    ],
    "pricing": [
      {"pricing_level_id": "pl_default_uuid", "min_quantity": 1, "list_price": 24.99}
    ]
  }'
```

## Error handling

Validation errors return the exact `param` path so you know which field in which child failed:

```json theme={null}
{
  "error": "missing_required_field",
  "code": "missing_required_field",
  "type": "validation_error",
  "hint": "pricing_level_id or account_id is required for each pricing row.",
  "param": "variants[0].pricing[2].pricing_level_id"
}
```

Transactional rollback: if any child write fails, no rows are written. The response always reflects the full success or full failure state.

## Adding pricing tiers to an existing product

Use `POST /v1/products/{id}/pricing` to add tiers incrementally after creation:

```bash theme={null}
curl -X POST https://api.arcuserp.com/v1/products/prod_abc123/pricing \
  -H "Authorization: Bearer ark_live_ent_wss_abc123" \
  -d '{
    "pricing_level_id": "pl_wholesale_uuid",
    "min_quantity": 1,
    "list_price": 42.00
  }'
```
