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

# Purchase Order Creation API

> Create purchase orders with line items, vendor details, and procurement settings

## Authentication

All requests require an API key in the request headers.

**Headers:**

```
Authorization: Api-Key YOUR_API_KEY
Content-Type: application/json
```

## Request

**Content-Type:** `application/json`

### Body Parameters

| Parameter            | Type   | Required | Description                                                                                                                                                                                                                                                                                                           |
| -------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `poNumber`           | string | Yes      | Purchase order number to assign to the document (minimum 1 character)                                                                                                                                                                                                                                                 |
| `lineItems`          | array  | Yes      | List of line items that make up the purchase order (minimum 1 item required)                                                                                                                                                                                                                                          |
| `poType`             | string | Yes      | Indicates whether the purchase order is for goods or services. Valid values: `GOODS`, `SERVICES`                                                                                                                                                                                                                      |
| `matchingType`       | string | Yes      | Specifies whether the purchase order follows a two-way or three-way match process. Valid values: `TWO_WAY`, `THREE_WAY`                                                                                                                                                                                               |
| `poDate`             | string | Yes      | Purchase order date in ISO-8601 format (YYYY-MM-DD)                                                                                                                                                                                                                                                                   |
| `vendorId`           | string | No       | Identifier of the vendor that should be linked to the purchase order (minimum 1 character)                                                                                                                                                                                                                            |
| `gstin`              | string | Yes      | Entity GSTIN of the organization 15 character length (`{State Code}{PAN}{Entity Number}{Z}{Checksum}`)                                                                                                                                                                                                                |
| `description`        | string | No       | Short description for the purchase order (1-255 characters)                                                                                                                                                                                                                                                           |
| `currency`           | string | No       | ISO 4217 currency code that will be used for the purchase order amounts (3 characters, e.g., INR, USD)                                                                                                                                                                                                                |
| `paymentTerms`       | string | No       | Free form outline of the agreed payment terms (minimum 1 character)                                                                                                                                                                                                                                                   |
| `deliveryDate`       | string | No       | Requested delivery date for the purchase order in ISO-8601 format (YYYY-MM-DD)                                                                                                                                                                                                                                        |
| `termsAndConditions` | string | No       | Terms and conditions that must be displayed on the purchase order (minimum 1 character)                                                                                                                                                                                                                               |
| `additionalNotes`    | string | No       | Additional notes to append to the purchase order document (minimum 1 character)                                                                                                                                                                                                                                       |
| `state`              | string | No       | Target state for the PO: `DRAFTED` or `ACTIVE`. When `ACTIVE` is requested the API validates all required fields. If they are all present the PO is created in active state; if anything is missing it falls back to `DRAFTED` and the response includes `submitWarnings`. Omitting this field defaults to `DRAFTED`. |
| `tags`               | object | No       | PO-level tags (flex fields). Map of tag slug to a `{ value }` object. See [Tags](#tags) below.                                                                                                                                                                                                                        |

### Fields Required for Active State (when `state: "ACTIVE"`)

When `state: "ACTIVE"` is passed, the following fields must be present **in addition to** the schema-required fields for the PO to be activated:

| Field          | Level          | Requirement                     |
| -------------- | -------------- | ------------------------------- |
| `vendorId`     | PO             | Vendor must be linked           |
| `description`  | PO             | Description must be non-empty   |
| `paymentTerms` | PO             | Payment terms must be non-empty |
| `quantity`     | Each line item | Must be greater than 0          |
| `rate`         | Each line item | Must be greater than 0          |

If any of the above are missing, the PO is still created in **draft** state and `submitWarnings` in the response identifies exactly which fields were absent.

### Line Items Object

Each item in the `lineItems` array must contain the following fields:

| Parameter    | Type   | Required                 | Description                                                                                                                                |
| ------------ | ------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `quantity`   | number | Yes                      | Quantity requested for the line item                                                                                                       |
| `rate`       | number | Yes                      | Unit rate to be applied to the line item                                                                                                   |
| `identifier` | string | Yes                      | Free-text label for the line item (minimum 1 character)                                                                                    |
| `skuCode`    | string | Required for `THREE_WAY` | Item code of an existing SKU in your inventory. Used to link this line item to a SKU. Takes priority over `skuName` when both are provided |
| `skuName`    | string | Required for `THREE_WAY` | Name of an existing SKU in your inventory. Used to link this line item to a SKU when `skuCode` is not provided                             |
| `tags`       | object | No                       | Line-item-level tags. Map of tag slug to a `{ value }` object. See [Tags](#tags) below.                                                    |

> **SKU matching (`THREE_WAY`):** When `matchingType` is `THREE_WAY`, every line item **must** include either `skuCode` or `skuName`, and it **must** resolve to an existing SKU in your inventory. If any line item is missing both fields, or if the provided code/name cannot be matched, the request is rejected with a `400` error and the purchase order is **not created**. See [Bulk SKU Creation](/apis/sku-bulk-creation) to pre-populate your inventory.

### PO Type Values

The `poType` field accepts the following values:

* `GOODS`: Purchase order for physical goods or products
* `SERVICES`: Purchase order for services

### Matching Type Values

The `matchingType` field accepts the following values:

* `TWO_WAY`: Purchase order matching based on purchase order and invoice (2-way match)
* `THREE_WAY`: Purchase order matching based on purchase order, invoice, and goods receipt note (3-way match)

### Tags

Tags can be applied both at the **PO level** (`tags` in the request body) and at the **line-item level** (`tags` inside each `lineItems` entry). In both cases `tags` is an object that maps a tag slug to a `{ value }` object:

<Info>
  To discover the available tag slugs, their types, and (for `DROPDOWN` tags) the valid value ids, see the tag APIs: the [Tag List API](/apis/tag-list), and [Tag Detail API](/apis/tag-detail).
</Info>

```json theme={null}
{
  "tags": {
    "cost-center": { "value": 42 },
    "project-code": { "value": "PRJ-2024" },
    "expected-by": { "value": "2024-03-15" },
    "budget": { "value": 5000 }
  }
}
```

The `value` you send depends on the tag's type:

| Tag type   | What to send as `value`                             |
| ---------- | --------------------------------------------------- |
| `DROPDOWN` | The tag value **id** (number) of an existing option |
| `VARCHAR`  | Free text (string)                                  |
| `DATETIME` | A date string in `YYYY-MM-DD` format                |

## Code Examples

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.pazy.io/v1.0/procurement/purchase-order \
    -H "Authorization: Api-Key YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "poNumber": "PO-2024-001",
      "poDate": "2024-01-15",
      "poType": "GOODS",
      "matchingType": "THREE_WAY",
      "vendorId": "<vendorId>",
      "gstin": "29ABCDE1234F1Z5",
      "description": "Office supplies purchase order",
      "currency": "INR",
      "paymentTerms": "Net 30 days",
      "deliveryDate": "2024-02-15",
      "termsAndConditions": "All goods must be delivered in original packaging",
      "additionalNotes": "Please deliver during business hours",
      "state": "ACTIVE",
      "tags": {
        "cost-center": { "value": 42 },
        "project-code": { "value": "PRJ-2024" }
      },
      "lineItems": [
        {
          "identifier": "SKU-001",
          "quantity": 100,
          "rate": 50.00,
          "tags": {
            "budget-line": { "value": "Stationery" }
          }
        },
        {
          "identifier": "SKU-002",
          "quantity": 50,
          "rate": 75.50
        }
      ]
    }'
  ```

  ```javascript JavaScript (Fetch API) theme={null}
  const response = await fetch('https://api.pazy.io/v1.0/procurement/purchase-order', {
    method: 'POST',
    headers: {
      'Authorization': 'Api-Key YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      poNumber: 'PO-2024-001',
      poDate: '2024-01-15',
      poType: 'GOODS',
      matchingType: 'THREE_WAY',
      vendorId: '<vendorId>',
      gstin: '29ABCDE1234F1Z5',
      description: 'Office supplies purchase order',
      currency: 'INR',
      paymentTerms: 'Net 30 days',
      deliveryDate: '2024-02-15',
      termsAndConditions: 'All goods must be delivered in original packaging',
      additionalNotes: 'Please deliver during business hours',
      state: 'ACTIVE',
      tags: {
        'cost-center': { value: 42 },
        'project-code': { value: 'PRJ-2024' }
      },
      lineItems: [
        {
          identifier: 'SKU-001',
          quantity: 100,
          rate: 50.00,
          tags: {
            'budget-line': { value: 'Stationery' }
          }
        },
        {
          identifier: 'SKU-002',
          quantity: 50,
          rate: 75.50
        }
      ]
    })
  });

  const result = await response.json();
  ```

  ```python Python (requests) theme={null}
  import requests

  url = "https://api.pazy.io/v1.0/procurement/purchase-order"
  headers = {
      "Authorization": "Api-Key YOUR_API_KEY",
      "Content-Type": "application/json"
  }

  payload = {
      "poNumber": "PO-2024-001",
      "poDate": "2024-01-15",
      "poType": "GOODS",
      "matchingType": "THREE_WAY",
      "vendorId": "<vendorId>",
      "gstin": "29ABCDE1234F1Z5",
      "description": "Office supplies purchase order",
      "currency": "INR",
      "paymentTerms": "Net 30 days",
      "deliveryDate": "2024-02-15",
      "termsAndConditions": "All goods must be delivered in original packaging",
      "additionalNotes": "Please deliver during business hours",
      "state": "ACTIVE",
      "tags": {
          "cost-center": {"value": 42},
          "project-code": {"value": "PRJ-2024"}
      },
      "lineItems": [
          {
              "identifier": "SKU-001",
              "quantity": 100,
              "rate": 50.00,
              "tags": {
                  "budget-line": {"value": "Stationery"}
              }
          },
          {
              "identifier": "SKU-002",
              "quantity": 50,
              "rate": 75.50
          }
      ]
  }

  response = requests.post(url, headers=headers, json=payload)
  result = response.json()
  ```
</CodeGroup>

## Success Response

**HTTP Status:** `200 OK`

**Response Fields:**

| Field                   | Type    | Description                                                                                                          |
| ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
| `ok`                    | boolean | Indicates whether the request was successful                                                                         |
| `data`                  | object  | Contains the purchase order creation response data                                                                   |
| `data.poId`             | string  | Unique slug identifier for the purchase order (use this to retrieve or update the PO)                                |
| `data.state`            | string  | State the PO was created in — `DRAFTED` or `APPROVED` (active)                                                       |
| `data.skuMatchWarnings` | array   | Not applicable for `THREE_WAY` POs (unmatched SKUs are rejected). Reserved for future use                            |
| `data.submitWarnings`   | object  | Present only when `submit: true` was passed but required fields were missing. The PO is still created in draft state |

### `skuMatchWarnings` Object

| Field                | Type   | Description                                                    |
| -------------------- | ------ | -------------------------------------------------------------- |
| `lineItemIdentifier` | string | The `identifier` value of the unmatched line item              |
| `skuCode`            | string | The `skuCode` that was provided (if applicable)                |
| `skuName`            | string | The `skuName` that was provided (if applicable)                |
| `message`            | string | Human-readable description of why the SKU could not be matched |

### `submitWarnings` Object

| Field       | Type             | Description                                                                              |
| ----------- | ---------------- | ---------------------------------------------------------------------------------------- |
| `po`        | array of strings | PO-level fields that were missing (e.g. `"vendorId"`, `"description"`, `"paymentTerms"`) |
| `lineItems` | object           | Map of line item identifier to an array of missing fields (e.g. `"quantity"`, `"rate"`)  |

### Response Example — draft (no `state` field or `state: "DRAFTED"`)

```json theme={null}
{
  "ok": true,
  "data": {
    "poId": "po_slug_identifier",
    "state": "DRAFTED"
  }
}
```

### Response Example — active (`state: "ACTIVE"`, all fields present)

```json theme={null}
{
  "ok": true,
  "data": {
    "poId": "po_slug_identifier",
    "state": "ACTIVE"
  }
}
```

### Response Example — draft despite `state: "ACTIVE"` (fields missing)

```json theme={null}
{
  "ok": true,
  "data": {
    "poId": "po_slug_identifier",
    "state": "DRAFTED",
    "submitWarnings": {
      "po": ["vendorId", "paymentTerms"],
      "lineItems": {
        "SKU-001": ["rate"]
      }
    }
  }
}
```

### Response Example — `state: "ACTIVE"` with SKU warnings

```json theme={null}
{
  "ok": true,
  "data": {
    "poId": "po_slug_identifier",
    "state": "ACTIVE",
    "skuMatchWarnings": [
      {
        "lineItemIdentifier": "SKU-001",
        "skuCode": "UNKNOWN-CODE",
        "message": "SKU with item code \"UNKNOWN-CODE\" not found in your inventory"
      }
    ]
  }
}
```

## Error Responses

### Missing Required Fields

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "MISSING_REQUIRED_FIELD",
    "message": "poNumber is required"
  }
}
```

### Invalid Procurement Type

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_PROCUREMENT_TYPE",
    "message": "Invalid procurement type"
  }
}
```

### Invalid Matching Type

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_MATCHING_TYPE",
    "message": "Invalid matching type"
  }
}
```

### Invalid Date

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_DATE",
    "message": "Invalid date"
  }
}
```

### Invalid Expected Delivery Date

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_EXPECTED_DELIVERY_DATE",
    "message": "Invalid expected delivery date"
  }
}
```

### SKU Required (THREE\_WAY match)

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "SKU_REQUIRED",
    "message": "skuCode or skuName is required for each line item in a THREE_WAY purchase order. Missing on: \"SKU-001\""
  }
}
```

### SKU Not Found (THREE\_WAY match)

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "SKU_NOT_FOUND",
    "message": "SKU with item code \"UNKNOWN-CODE\" not found in your inventory"
  }
}
```

### Invalid Line Items

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /body/lineItems: must NOT have fewer than 1 items"
  }
}
```

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /body/lineItems/0/quantity: must be number"
  }
}
```

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /body/lineItems/0/identifier: must NOT have fewer than 1 characters"
  }
}
```

### Invalid Currency

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_CURRENCY",
    "message": "Invalid currency Only INR is supported at the moment"
  }
}
```

### Invalid Description Length

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /body/description: must NOT have more than 255 characters"
  }
}
```

### Vendor Not Found

**HTTP Status:** `404 Not Found`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "VENDOR_NOT_FOUND",
    "message": "Vendor not found"
  }
}
```

### Invalid GSTIN

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_GSTIN",
    "message": "Invalid GSTIN"
  }
}
```

### Invalid State

**HTTP Status:** `400 Bad Request`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_STATE",
    "message": "Invalid state"
  }
}
```

### Authentication Errors

**HTTP Status:** `401 Unauthorized`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "MISSING_CREDENTIALS",
    "message": "Missing Credentials"
  }
}
```

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid API Key"
  }
}
```

### Permission Errors

**HTTP Status:** `403 Forbidden`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Permission check failed - PERMISSION_CHECK_FAILED"
  }
}
```

### Purchase Order Creation Errors

**HTTP Status:** `500 Internal Server Error`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "PROCUREMENT_CREATION_FAILED",
    "message": "Error creating procurement"
  }
}
```

### Internal Error

**HTTP Status:** `500 Internal Server Error`

```json theme={null}
{
  "ok": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "Internal error"
  }
}
```

## Best Practices

### Purchase Order Creation

* Ensure all required fields (`poNumber`, `lineItems`, `poType`, `matchingType`, `poDate`) are provided
* Use ISO 8601 date format (YYYY-MM-DD) for all date fields
* Provide at least one line item with valid `quantity`, `rate`, and `identifier` values
* Use `INR` for the `currency` field (only INR is currently supported)
* Choose the appropriate `matchingType` based on your procurement process:
  * Use `TWO_WAY` for simpler matching between PO and invoice
  * Use `THREE_WAY` when goods receipt verification is required
* Link the purchase order to a vendor using `vendorId` if available
* Include `gstin` for tax compliance in Indian markets
* Keep `description` concise (max 255 characters) for better readability
* Use `termsAndConditions` to specify important contractual terms
* Add `additionalNotes` for delivery instructions or special requirements

### Activating a PO on Creation

Pass `state: "ACTIVE"` when the PO is complete and ready to be activated immediately:

* The response `state` will be `ACTIVE` if all required fields are present, or `DRAFTED` if anything was missing
* Check `submitWarnings` in the response — it lists exactly which PO-level or line item fields prevented activation
* A PO created in `DRAFTED` state can be activated later by calling the update API with `state: "ACTIVE"`
* Omitting `state` (or passing `state: "DRAFTED"`) always creates in draft, regardless of what fields are present

### Line Items

* Ensure each line item has a unique `identifier`
* Use positive numbers for `quantity` and `rate`
