> ## 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.

# Atomic-Create

> How Arcus create endpoints accept a complete nested resource graph in a single POST, committing or rolling back as one transaction.

## What atomic-create means

Most ERP APIs require multiple round trips to build a complete record. You create the parent, get the ID, then POST each child separately. If one child call fails midway, you are left with a partial record.

Arcus create endpoints accept the **entire resource graph in a single POST**. Children (addresses, line items, pricing tiers, kit components, contacts) nest directly inside the parent body. The server writes every row in a single database transaction. If anything fails, the whole request rolls back and you receive a structured error with the exact field path that failed.

The pattern is the same across all primary resources:

```
POST /v1/accounts        # account + addresses + contacts + payment methods + tags
POST /v1/orders          # order + line items + discount + tax + allocation + payment
POST /v1/products        # product + variants + components + pricing tiers + vendors
POST /v1/vendor-bills    # bill + line items (linked to PO + receipt)
POST /v1/journal-entries # entry + balanced lines (two or more)
```

***

## Anatomy of an atomic-create request

Every atomic-create request follows the same structure:

```json theme={null}
{
  "parent_field_1": "value",
  "parent_field_2": "value",
  "children": [
    {
      "child_field": "value",
      "grandchildren": [...]
    }
  ]
}
```

The response returns the full hydrated record. You never need a follow-up GET to see what was created.

***

## Transactional guarantee

Every child is written inside a single `BEGIN` / `COMMIT` block. The database constraint rules that apply are:

1. **All-or-nothing**: if any child fails validation or insert, the parent row and every already-inserted child are rolled back. You get a complete record or nothing.

2. **Validated upfront**: required fields on every child are validated before any write begins. The first validation error stops execution and returns a 422 with the exact `param` path.

3. **Side effects in-transaction**: GL postings, inventory reservations, and activity log entries all write inside the same transaction (or queue atomically post-commit for async paths).

This means there are no orphan records. If you POST a product with bad pricing data, you do not end up with a product row that you then have to clean up.

***

## Error response: exact param path

When a nested child fails, the `param` field points to the exact location inside the request body. This makes it possible to display an error next to the right field in your UI without parsing the error message.

```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"
}
```

The `param` format is dot-and-bracket notation, matching the shape of the request body:

* `line_items[2].quantity` -- third line item, quantity field
* `variants[0].pricing[2].pricing_level_id` -- first variant, third pricing tier, pricing level field
* `addresses[1].country` -- second address, country field

***

## Idempotency

Every atomic-create endpoint honors the `Idempotency-Key` header. Generate the key before the first attempt and reuse it on every retry. If Arcus processed the original request but your network failed, replaying the same key returns the original response without writing anything twice.

```bash theme={null}
curl -X POST https://api.arcuserp.com/v1/orders \
  -H "Authorization: Bearer $ARCUS_API_KEY" \
  -H "Idempotency-Key: order-import-session-2026-05-12-row-001" \
  -d '{...}'
```

Keys are scoped per entity and expire after 24 hours.

***

## Hydrated response

The 201 response always includes every child expanded inline. No additional API calls are needed to verify what was created.

```json theme={null}
{
  "data": {
    "id": "prod_abc123",
    "title": "Dome Riser Kit",
    "product_type": "kit",
    "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 }
    ]
  }
}
```

Money fields are always returned as JavaScript numbers (float), never as Postgres numeric strings.

***

## Incremental edits still work

Atomic-create does not replace the per-child endpoints. After creating a parent, you can add children incrementally:

```http theme={null}
POST /v1/accounts/{id}/addresses
POST /v1/products/{id}/pricing
POST /v1/orders/{id}/payments
```

These endpoints use the same canonical handlers as atomic-create. The idempotency behavior is additive: if you POST the same child body with the same idempotency key, you get the original child record back without inserting a duplicate.

***

## When to use atomic-create vs incremental add

| Situation                                             | Recommendation                                      |
| ----------------------------------------------------- | --------------------------------------------------- |
| Onboarding a new record with all its children at once | Atomic-create (single POST)                         |
| Migrating data from another system                    | Atomic-create with idempotency key per source ID    |
| Adding one address to an existing account             | Incremental add: `POST /v1/accounts/{id}/addresses` |
| Adding one pricing tier after a product is live       | Incremental add: `POST /v1/products/{id}/pricing`   |

***

## Entity isolation

All child rows inherit `entity_id` from the authenticated API key. You cannot set `entity_id` in the request body; any attempt returns `403 forbidden_field`. This ensures data isolation is structurally enforced at the API layer -- one API key, one entity, every time.

***

## See Also

* [Creating Products, Kits, and Variants](/guides/creating-products-kits-variants) -- variant parent with kit variants and qty-break pricing in one call
* [Creating Accounts with Addresses](/guides/creating-accounts-with-addresses) -- full B2B account graph with addresses, contacts, and payment methods
* [Creating Orders](/guides/creating-orders) -- order with line items, tax, allocation, and inline payment
* [Purchase-to-Pay Flow](/guides/purchase-to-pay-flow) -- PO through receipt, vendor bill, and AP payment
* [Idempotency](/concepts/idempotency) -- how idempotency keys work across all create endpoints
* [Error Handling](/guides/error-handling) -- retry logic and structured error logging
