Skip to main content

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.

Overview

Three endpoints support atomic multi-item inventory operations. Each accepts an items[] array and guarantees all-or-nothing behavior: every item writes in a single database transaction. If item 5 of 10 fails, items 0-4 are automatically rolled back.
EndpointPurposeGL fires?
POST /v1/inventory/receiveNon-PO receipts (FIFO layer creation per item)No (not PO-linked)
POST /v1/inventory/adjustmentsCycle counts, shrinkage, revaluationsYes, per item in same transaction
POST /v1/inventory/transfersInter-location transfersNo on draft; yes on submit
Each endpoint also accepts single-item (top-level) payloads for backward compatibility.

POST /v1/inventory/receive

When to use

Use for non-PO standalone receipts — opening stock loads, vendor samples, or inventory counts that don’t correspond to a purchase order. Each item establishes its own FIFO cost layer. For PO-linked receipts use POST /v1/purchase-orders/{id}/receive.

Multi-item atomic receive

curl -s -X POST "https://api.arcuserp.com/v1/inventory/receive" \
  -H "Authorization: Bearer ark_live_ent_wss_abc123xyz" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: receive-batch-20260513-001" \
  -d '{
    "location_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
    "notes": "Restocking run from supplier",
    "items": [
      {
        "product_id": "7f6f3e14-eaad-4c45-b922-209f6dc6d140",
        "quantity": 10,
        "unit_cost": 12.50,
        "notes": "Circuit Breaker 10A White"
      },
      {
        "product_id": "ec9288c3-12b1-474f-bb09-b0b15bb7a8ff",
        "quantity": 5,
        "unit_cost": 8.75,
        "notes": "Square D Breaker Black"
      }
    ]
  }'

Response shape

{
  "object": "inventory_receive_result",
  "items": [
    {
      "index": 0,
      "product_id": "7f6f3e14-eaad-4c45-b922-209f6dc6d140",
      "location_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
      "quantity": 10,
      "unit_cost": 12.5,
      "balance": {
        "id": "0118ff93-8f65-4a72-ae7e-ca2a75526631",
        "on_hand": 10,
        "allocated": 0,
        "available": 10,
        "total_value": "125.00",
        "avg_unit_value": "12.5000"
      },
      "transaction": {
        "id": "3096987e-b156-4ee0-b373-8056f5bd8d91",
        "type": "receiving",
        "quantity": 10,
        "unit_price": "12.5000",
        "total_price": "125.00",
        "transaction_id": "IVT-0000153"
      }
    }
  ],
  "summary": {
    "total_items": 2,
    "total_quantity": 15
  }
}

Required fields per item

FieldTypeNotes
product_idUUIDRequired per item
quantityintegerMust be >= 1
unit_costnumberMust be > 0. Establishes the FIFO cost layer. Zero-cost receives are rejected.
location_idUUIDPer-item override; falls back to root location_id
notesstringOptional. Per-item notes on the transaction record.

Validation error shape (400)

If any item fails upfront validation, no items are written and the response includes exact param paths:
{
  "error": "validation_error",
  "code": "multi_item_validation_failed",
  "type": "validation_error",
  "hint": "One or more items failed validation. Fix all errors before retrying.",
  "errors": [
    {
      "index": 0,
      "param": "items[0].unit_cost",
      "hint": "unit_cost must be a finite positive number. Standalone receive establishes FIFO cost basis; zero-cost receives corrupt downstream COGS."
    }
  ]
}

POST /v1/inventory/adjustments

When to use

Use for cycle-count corrections, shrinkage write-downs, and inventory revaluations. A GL journal entry fires per item inside the same database transaction (Rule 21). Positive deltas (adding stock) require unit_cost to establish a new FIFO layer. Negative deltas consume existing FIFO layers automatically and calculate COGS from the oldest layers.

Multi-item atomic adjustment

curl -s -X POST "https://api.arcuserp.com/v1/inventory/adjustments" \
  -H "Authorization: Bearer ark_live_ent_wss_abc123xyz" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: adj-cycle-count-20260513-001" \
  -d '{
    "location_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
    "reason": "cycle_count",
    "memo": "Monthly cycle count Q2 2026",
    "items": [
      {
        "product_id": "7f6f3e14-eaad-4c45-b922-209f6dc6d140",
        "quantity_delta": 3,
        "unit_cost": 12.50,
        "notes": "Count correction -- physical count higher than system"
      },
      {
        "product_id": "ec9288c3-12b1-474f-bb09-b0b15bb7a8ff",
        "quantity_delta": -2,
        "notes": "Shrinkage discovered during count"
      }
    ]
  }'

Response shape

{
  "object": "inventory_adjustment_result",
  "items": [
    {
      "index": 0,
      "product_id": "7f6f3e14-eaad-4c45-b922-209f6dc6d140",
      "location_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
      "quantity_delta": 3,
      "gl_amount": 37.5,
      "journal_entry_id": "c79ab8cf-b840-471f-bea9-6a24df80f550",
      "balance": {
        "on_hand": 13,
        "allocated": 0,
        "available": 13,
        "total_value": "162.50"
      },
      "transaction": {
        "id": "10e06976-8096-48bc-bbbd-5fe71626f544",
        "type": "adjustment",
        "quantity": 3,
        "unit_price": "12.5000",
        "total_price": "37.50",
        "transaction_id": "IVT-0000155"
      },
      "consumed_layers": []
    },
    {
      "index": 1,
      "product_id": "ec9288c3-12b1-474f-bb09-b0b15bb7a8ff",
      "location_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
      "quantity_delta": -2,
      "gl_amount": -17.5,
      "journal_entry_id": "a8c5c8c6-b0b0-4e63-918d-a247189735f3",
      "balance": {
        "on_hand": 3,
        "allocated": 0,
        "available": 3,
        "total_value": "26.25"
      },
      "transaction": {
        "id": "93659e88-f3ba-4709-9798-388886820ea0",
        "type": "adjustment",
        "quantity": -2,
        "unit_price": "8.7500",
        "total_price": "17.50",
        "transaction_id": "IVT-0000156"
      },
      "consumed_layers": [
        {
          "layer_id": "3de7680e-fba2-444a-a204-7290cc6f08e1",
          "qty_used": 2,
          "qty_after": 3,
          "unit_cost": 8.75,
          "layer_cogs": 17.5
        }
      ]
    }
  ],
  "summary": {
    "total_items": 2,
    "total_gl_impact": 20
  }
}

Required fields per item

FieldTypeNotes
product_idUUIDRequired per item
quantity_deltaintegerNon-zero signed integer. Positive = add stock, negative = remove stock.
unit_costnumberRequired when quantity_delta > 0 (new FIFO layer). Ignored for negative deltas (FIFO auto-resolves).
location_idUUIDPer-item override; falls back to root location_id
reasonstringPer-item override; falls back to root reason. Common values: cycle_count, shrinkage, revaluation, manual.
notesstringOptional. Combined with memo in the transaction record.

GL behavior

Each adjustment item creates its own journal entry (DR/CR balanced to the cent):
  • Positive delta (add stock): DR Inventory Asset, CR Inventory Adjustment/Suspense. Amount = quantity_delta * unit_cost.
  • Negative delta (remove stock): DR Inventory Adjustment/Shrinkage, CR Inventory Asset. Amount = FIFO COGS (oldest layers consumed first).
The journal_entry_id on each item links the transaction to the GL record. gl_amount is positive for additions, negative for removals.

Serialized product adjustments

For serialized products, include matching serial arrays whose length equals abs(quantity_delta):
{
  "product_id": "SERIALIZED_PRODUCT_UUID",
  "quantity_delta": -1,
  "serial_numbers_removed": ["SN-ABC-001"],
  "notes": "Damaged unit removed from stock"
}

POST /v1/inventory/transfers

When to use

Use to move inventory between locations within the same entity (internal transfer) or between entities (intercompany transfer). A transfer is created in draft status. GL entries fire when the transfer is submitted (internal) or shipped (intercompany).

Multi-item atomic transfer

curl -s -X POST "https://api.arcuserp.com/v1/inventory/transfers" \
  -H "Authorization: Bearer ark_live_ent_wss_abc123xyz" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: xfer-replenish-20260513-001" \
  -d '{
    "source_location_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
    "dest_location_id": "828cabe6-3fa2-4fb7-a2af-2280bbaba335",
    "notes": "Weekly replenishment transfer to secondary warehouse",
    "items": [
      {
        "product_id": "7f6f3e14-eaad-4c45-b922-209f6dc6d140",
        "quantity": 5,
        "notes": "Circuit Breaker 10A White"
      },
      {
        "product_id": "ec9288c3-12b1-474f-bb09-b0b15bb7a8ff",
        "quantity": 3,
        "notes": "Square D Breaker Black"
      }
    ]
  }'

Response shape

{
  "data": {
    "id": "e1ce42dc-e9ae-40c4-a923-aa93100b0947",
    "entity_from_id": "65dcd234-53c3-4f54-b772-ee349528d497",
    "entity_to_id": "65dcd234-53c3-4f54-b772-ee349528d497",
    "location_from_id": "91aa7588-4dc7-4edd-b172-7de91f5e1f78",
    "location_to_id": "828cabe6-3fa2-4fb7-a2af-2280bbaba335",
    "transfer_number": "SB-TRF-0000001",
    "status": "draft",
    "transfer_type": "internal",
    "notes": "Weekly replenishment transfer to secondary warehouse",
    "gl_posted": false,
    "items": [
      {
        "id": "d8c6bb7a-9c4c-446a-8518-d6f9398dbedc",
        "transfer_id": "e1ce42dc-e9ae-40c4-a923-aa93100b0947",
        "product_id": "7f6f3e14-eaad-4c45-b922-209f6dc6d140",
        "quantity_sent": "5.0000",
        "quantity_received": "0.0000",
        "notes": "Circuit Breaker 10A White"
      }
    ]
  }
}

Field name aliases

Both naming conventions are accepted:
CanonicalAlias
source_location_idfrom_location_id
dest_location_idto_location_id

GL lifecycle

StatusGL action
draftNo GL. Items stored; no stock movement yet.
submitted (internal)GL fires: DR Transit Inventory / CR Source Warehouse Inventory.
shipped (intercompany)GL fires: DR Due From / CR Source Inventory.
received (intercompany)GL fires: DR Dest Inventory / CR Due To.

Atomicity guarantee

All three endpoints wrap their items[] loop in a single pool.connect() + BEGIN/COMMIT/ROLLBACK transaction:
  1. All items validate upfront (before BEGIN).
  2. BEGIN starts.
  3. Each item writes inside the transaction.
  4. Any item failure triggers ROLLBACK — every item reverts.
  5. COMMIT fires only after all items succeed.
On failure, the response includes failed_item_index and items_completed_before_failure so you know which item triggered the rollback. All items are reverted regardless of items_completed_before_failure — the count is informational only.

Idempotency

All three endpoints honor the Idempotency-Key header. Replaying the same key within 24 hours returns the original response without re-applying the operation. Use a UUID or a deterministic key tied to your batch (e.g. receive-batch-${date}-${batchId}).
  • POST /v1/purchase-orders/{id}/receive — PO-linked receipts (triggers GRNI GL, matches against PO lines)
  • GET /v1/inventory/balances — query current on_hand / allocated / available by product and location
  • GET /v1/inventory/transactions — movement history log
  • GET /v1/inventory/fifo-layers — FIFO cost layers per product/location
  • POST /v1/inventory/serials — bulk enroll serial numbers (status transition only, no on_hand change)