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.

What this guide covers

The purchase-to-pay (P2P) workflow moves goods and money through four stages:
  1. Purchase order — commit to buying from a vendor
  2. Goods receipt — receive the goods into inventory
  3. Vendor bill — record the vendor’s invoice and clear the GRNI accrual
  4. AP payment — pay the bill and close the liability
Each stage is a separate API call. GL entries post automatically at the right stage. No manual journal entries are needed for the standard flow.

Prerequisites

  • API key with scopes: purchasing:write, purchasing:read, accounting:write
  • An active vendor account (account_type: vendor)
  • One or more product IDs to order
  • A warehouse location ID

Stage 1: Create the purchase order

POST /v1/orders with document_type: "purchase_order". The order is created in draft status. No GL entry fires at this stage.
curl -s -X POST "https://api.arcuserp.com/v1/orders" \
  -H "Authorization: Bearer $ARCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: po-acme-2026-05-12-001" \
  -d '{
    "document_type": "purchase_order",
    "account_id": "acct_acme_vendor_uuid",
    "location_id": "loc_main_warehouse_uuid",
    "payment_terms": "Net30",
    "line_items": [
      {
        "product_id": "prod_dome_riser_uuid",
        "quantity": 24,
        "cost_rate": 18.00,
        "notes": "Dome Riser Kit - Q2 restock"
      },
      {
        "product_id": "prod_flat_riser_uuid",
        "quantity": 12,
        "cost_rate": 15.00
      }
    ]
  }'
Save po.data.id (the PO UUID). You will reference it in later stages.

Confirm the PO (open it)

Once reviewed, transition to open status, which makes it eligible for receiving:
curl -s -X PATCH "https://api.arcuserp.com/v1/orders/$PO_ID" \
  -H "Authorization: Bearer $ARCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "open"}'

Stage 2: Receive goods

POST /v1/orders/{po_id}/receive records the goods arriving at your warehouse. GL entries that fire at this stage:
AccountDebitCredit
Inventory (Finished Goods)unit_cost x qty_received
GRNI (Goods Received Not Invoiced)unit_cost x qty_received
GRNI is a liability accrual that stays open until you post the vendor bill in Stage 3.
curl -s -X POST "https://api.arcuserp.com/v1/orders/$PO_ID/receive" \
  -H "Authorization: Bearer $ARCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: recv-po-00042-2026-05-14" \
  -d '{
    "received_date": "2026-05-14",
    "location_id": "loc_main_warehouse_uuid",
    "line_items": [
      {
        "order_item_id": "oi_dome_riser_uuid",
        "quantity_received": 24,
        "bin_id": "bin_A1_uuid"
      },
      {
        "order_item_id": "oi_flat_riser_uuid",
        "quantity_received": 12,
        "bin_id": "bin_A2_uuid"
      }
    ]
  }'
The response includes inventory_updated: true and the GL journal entry ID for the GRNI posting. Inventory on-hand increases immediately. Partial receipt: you can receive fewer than the ordered quantity. The remaining quantity stays open on the PO. Fire another receive call when the rest arrives.

Stage 3: Enter and post the vendor bill

When the vendor’s invoice arrives, create a vendor bill linked to the PO. Posting the bill clears the GRNI accrual and records the AP liability.

Create the draft bill

curl -s -X POST "https://api.arcuserp.com/v1/vendor-bills" \
  -H "Authorization: Bearer $ARCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: bill-acme-inv-20260514" \
  -d '{
    "vendor_id": "acct_acme_vendor_uuid",
    "po_id": "'"$PO_ID"'",
    "vendor_invoice_number": "INV-2026-0514",
    "bill_date": "2026-05-14",
    "due_date": "2026-06-13",
    "line_items": [
      {
        "po_item_id": "oi_dome_riser_uuid",
        "quantity": 24,
        "unit_cost": 18.00,
        "description": "Dome Riser Kit"
      },
      {
        "po_item_id": "oi_flat_riser_uuid",
        "quantity": 12,
        "unit_cost": 15.00,
        "description": "Flat Riser Kit"
      }
    ]
  }'
The bill is created in draft status. No GL entry yet.

Post the bill

curl -s -X POST "https://api.arcuserp.com/v1/vendor-bills/$BILL_ID/post" \
  -H "Authorization: Bearer $ARCUS_API_KEY"
GL entries that fire when the bill is posted:
AccountDebitCredit
GRNI (Goods Received Not Invoiced)total bill amount
Accounts Payabletotal bill amount
This clears the GRNI accrual from Stage 2. The AP liability is now open until Stage 4.

Stage 4: Pay the vendor bill

POST /v1/ap-payments closes the AP liability and records the outgoing cash.
curl -s -X POST "https://api.arcuserp.com/v1/ap-payments" \
  -H "Authorization: Bearer $ARCUS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ap-pay-bill-20260613" \
  -d '{
    "vendor_id": "acct_acme_vendor_uuid",
    "payment_date": "2026-06-13",
    "payment_method": "check",
    "bank_account_id": "bank_operating_uuid",
    "memo": "Acme Inv 2026-0514 - Dome/Flat Riser restock",
    "applications": [
      {
        "bill_id": "'"$BILL_ID"'",
        "amount": 612.00
      }
    ]
  }'
GL entries that fire at payment:
AccountDebitCredit
Accounts Payable612.00
Bank (Operating Checking)612.00
The AP liability is cleared. The vendor bill transitions to paid status.

GL summary: the full P2P transaction trail

StageEventDebitCredit
2Receive goodsInventory FGGRNI
3Post vendor billGRNIAccounts Payable
4Pay billAccounts PayableBank
At Stage 4, GRNI nets to zero and Inventory FG reflects the real cost. This is the standard accrual accounting pattern (NetSuite, Acumatica, QuickBooks Enterprise all follow the same sequence).

Partial payments and multi-bill payments

A single AP payment can apply to multiple bills:
{
  "applications": [
    { "bill_id": "bill_uuid_1", "amount": 432.00 },
    { "bill_id": "bill_uuid_2", "amount": 180.00 }
  ]
}
You can also pay a bill partially. The bill remains open with a reduced balance_due until the remaining balance is paid or written off.

Vendor credits

To apply a vendor credit against a bill payment, include vendor_credit_ids[] in the payment body:
{
  "vendor_credit_ids": ["vc_uuid_1"],
  "applications": [
    { "bill_id": "bill_uuid", "amount": 462.00 }
  ]
}
The credit reduces the cash portion of the payment. If credits fully cover the bill, no bank JE posts.

Common errors

CodeHTTPMeaning
po_not_found404PO UUID does not exist for this entity
po_not_receivable422PO status is not open or processing
quantity_exceeds_ordered422Receipt line quantity exceeds PO line quantity remaining
bill_already_posted409Attempted to post a bill that is already in open status
bill_balance_insufficient422Payment application amount exceeds bill balance due
bank_account_not_found404bank_account_id does not exist for this entity
insufficient_scope403API key missing purchasing:write or accounting:write scope

Idempotency

Every write in this flow accepts Idempotency-Key. Use a stable key tied to your source record (e.g. po-<source-po-id>, recv-<source-receipt-id>, bill-<vendor-invoice-number>). Keys expire after 24 hours.

Verifying the full chain

After all four stages, run these checks:
# Confirm PO is fulfilled
curl "https://api.arcuserp.com/v1/orders/$PO_ID" \
  -H "Authorization: Bearer $ARCUS_API_KEY"
# Expect: status = fulfilled or processing

# Confirm inventory increased
curl "https://api.arcuserp.com/v1/inventory?product_id=prod_dome_riser_uuid" \
  -H "Authorization: Bearer $ARCUS_API_KEY"
# Expect: on_hand increased by 24

# Confirm bill is paid
curl "https://api.arcuserp.com/v1/vendor-bills/$BILL_ID" \
  -H "Authorization: Bearer $ARCUS_API_KEY"
# Expect: status = paid, balance_due = 0

# Run AP aging to confirm no residual payable
curl "https://api.arcuserp.com/v1/reports/ap-aging" \
  -H "Authorization: Bearer $ARCUS_API_KEY"

Next steps