---
title: "Create purchase order"
method: POST
path: "/v1/erp/payouts/purchase_orders"
tags: ["Purchase Order"]
---

# Create purchase order

`POST /v1/erp/payouts/purchase_orders`

Creates a new purchase order to track purchase commitments before invoices are received. This endpoint handles three types of purchase orders with comprehensive validation and automatic folio generation.

## Purchase Order Types

### 1. Against Invoice
Single invoice purchase order with exactly one item at 100%.

**Use case**: Simple purchases with a single invoice expected.

**Example**: Purchasing office equipment for $10,000 CLP.

### 2. By Milestone
Multiple named deliverables with individual percentages.

**Use case**: Projects with defined stages and separate invoicing for each stage.

**Example**: Software project with Design (30%), Development (50%), Testing (20%).

### 3. By Percentage
Multiple payment stages with percentages (no names required).

**Use case**: Service contracts with payment schedule.

**Example**: Consulting service with 30% advance, 50% mid-project, 20% final payment.

## Prerequisites

Before creating a purchase order, you need:

1. **Business Partner**: The vendor/supplier for this purchase
   - Must be pre-registered and approved
   - Find partners: `GET /v1/erp/payouts/business_partners`

2. **Company**: The company making the purchase
   - User must be a member with appropriate role
   - Required roles: admin, owner, or member

## Validation Rules

### General Rules (All Types)
- **amount_cents**: Must be positive integer (in cents)
- **description**: Required field for purchase reference
- **currency**: Must be one of: CLP, UF, USD
- **items**: Must have at least one item
- **percentages**: Item percentages must sum to exactly 100%

### Type-Specific Rules

**AgainstInvoice:**
- Must have exactly 1 item
- Item percentage must be 100%
- Item name is optional (not required)

**ByMilestone:**
- Must have at least 1 item
- Each item must have a name
- Item percentages must sum to 100%

**ByPercentage:**
- Must have at least 2 items
- Item names are optional (not required)
- Item percentages must sum to 100%

## Automatic Features

### Folio Generation
System automatically generates a `folio`: a per-company sequential **integer**
(1, 2, 3, ...), unique within the company. It is not a formatted string.

### Item Amount Calculation
System automatically calculates item amounts based on percentages:
- Formula: `item.amount_cents = (total_amount_cents × percentage / 100).round`
- Automatically recalculated when total amount changes

### Creator Tracking
System automatically assigns current authenticated user as creator (`user_id`).

## Workflow

1. Purchase order is created with status `draft`
2. Admin/creator assigns reviewers to items (if approval required)
3. Reviewers approve or reject items independently
4. When fiscal invoice arrives, it's linked to approved items
5. Linked items can be paid via bank transactions
6. When all items are paid, purchase order is considered complete

## Important Notes

- Regular users can only create purchase orders for companies they are members of
- All amounts are in the smallest currency unit (cents for CLP)
- Item amounts are automatically calculated from percentages
- Full audit trail is maintained for all changes

## Parameters

- `#/paths/~1v1~1erp~1payouts~1rules/get/parameters/0` — unresolved $ref

## Request body

- object
  - `purchase_order` object, required
    - `type` 'Erp::Payouts::PurchaseOrders::AgainstInvoice' | 'Erp::Payouts::PurchaseOrders::ByMilestone' | 'Erp::Payouts::PurchaseOrders::ByPercentage', required — Purchase order type determines validation rules: - AgainstInvoice: Exactly 1 item at 100% (item name optional) - ByMilestone: At least 1 item with names (e.g., "Design 30%", "Development 70%") - ByPercentage: At least 2 items without names (e.g., 30% advance, 70% final)
    - `business_partner_id` string, uuid, required — ID of the vendor/supplier for this purchase
    - `description` string, required — Description of what is being purchased
    - `amount_cents` integer, required — Total amount in cents (smallest currency unit). Item amounts are auto-calculated from percentages.
    - `currency` 'CLP' | 'UF' | 'USD', required — Currency for this purchase order
    - `items_attributes` object[], required — Purchase order items. Percentages must sum to exactly 100%. Item amounts are automatically calculated as: (total_amount × percentage / 100).round
      - `number` integer, required — Order number (must be unique within purchase order)
      - `name` string — Item name/description: - Required for ByMilestone type - Optional for AgainstInvoice and ByPercentage types
      - `percentage` number, required — Percentage of total amount (0-100, up to 2 decimal places)

## Response `201`

Purchase order created successfully

- object
  - `id` string, uuid, required — Unique identifier for the purchase order
  - `company_id` string, uuid, required — ID of the company that owns this purchase order
  - `business_partner_id` string, uuid, required — ID of the vendor/supplier for this purchase order
  - `user_id` string, required — ID of the user who created this purchase order
  - `folio` integer, required — Unique sequential folio number (auto-incremented per company)
  - `description` string, required — Description of the purchase
  - `amount_cents` integer, required — Total amount in cents (smallest currency unit)
  - `currency` 'CLP' | 'UF' | 'USD', required — Currency for this purchase order
  - `type` 'Erp::Payouts::PurchaseOrders::AgainstInvoice' | 'Erp::Payouts::PurchaseOrders::ByMilestone' | 'Erp::Payouts::PurchaseOrders::ByPercentage', required — Purchase order type (STI discriminator)
  - `review_status` 'draft' | 'pending' | 'approved' | 'declined', required — Lifecycle/approval state of the purchase order: - draft: initial draft, not yet submitted for review - pending: submitted, awaiting reviewer decisions - approved: all assigned reviewers approved - declined: at least one reviewer rejected
  - `created_at` string, date-time, required — Timestamp when the purchase order was created
  - `updated_at` string, date-time, required — Timestamp when the purchase order was last updated
  - `available_balance_cents` integer — Amount still available for linking to invoices (amount_cents - linked_amount_cents)
  - `linked_amount_cents` integer — Total amount already linked to invoices
  - `business_partner` object — Vendor/supplier information
    - `id` string, uuid
    - `name` string
    - `partner_id` string, uuid — ID of the underlying Person record for this vendor/supplier.
    - `email` string, email
  - `items` Schema[] — List of purchase order items — unresolved $ref
  - `payables` object[] — Related fiscal invoices (only included in show endpoint)
    - `id` string, uuid
    - `folio` string
    - `amount_cents` integer

## Other responses

- `401` — Unauthorized — missing/invalid `Authorization`, or the caller is not authorized to create a purchase order for this company (Pundit policy). **The response body is empty** for this guard (Pundit renders nothing). Branch your client logic on the status code, not the body. A bad/unknown `company-id` header resolves to a **404**, not a 401. See [Errors](https://docs.cardda.com/docs/errors) for details.
- `422` — Unprocessable Entity - Validation errors

---

[API](https://skmtc.dev/cardda/apis/banking-api.md) · [All operations](https://skmtc.dev/cardda/apis/banking-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/cardda/banking-api/revisions/ff1aeb3fda8b/schema)
