Skip to main content
Purchase Order Creation API
curl --request POST \
  --url https://api.pazy.io/v1.0/procurement/purchase-order

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

ParameterTypeRequiredDescription
poNumberstringYesPurchase order number to assign to the document (minimum 1 character)
lineItemsarrayYesList of line items that make up the purchase order (minimum 1 item required)
poTypestringYesIndicates whether the purchase order is for goods or services. Valid values: GOODS, SERVICES
matchingTypestringYesSpecifies whether the purchase order follows a two-way or three-way match process. Valid values: TWO_WAY, THREE_WAY
poDatestringYesPurchase order date in ISO-8601 format (YYYY-MM-DD)
vendorIdstringNoIdentifier of the vendor that should be linked to the purchase order (minimum 1 character)
gstinstringNoGSTIN of the organization (minimum 1 character)
descriptionstringNoShort description for the purchase order (1-255 characters)
currencystringNoISO 4217 currency code that will be used for the purchase order amounts (3 characters, e.g., INR, USD)
paymentTermsstringNoFree form outline of the agreed payment terms (minimum 1 character)
deliveryDatestringNoRequested delivery date for the purchase order in ISO-8601 format (YYYY-MM-DD)
termsAndConditionsstringNoTerms and conditions that must be displayed on the purchase order (minimum 1 character)
additionalNotesstringNoAdditional notes to append to the purchase order document (minimum 1 character)
statestringNoTarget 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.

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:
FieldLevelRequirement
vendorIdPOVendor must be linked
descriptionPODescription must be non-empty
paymentTermsPOPayment terms must be non-empty
quantityEach line itemMust be greater than 0
rateEach line itemMust 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:
ParameterTypeRequiredDescription
quantitynumberYesQuantity requested for the line item
ratenumberYesUnit rate to be applied to the line item
identifierstringYesFree-text label for the line item (minimum 1 character)
skuCodestringRequired for THREE_WAYItem 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
skuNamestringRequired for THREE_WAYName of an existing SKU in your inventory. Used to link this line item to a SKU when skuCode is not provided
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 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)

Code Examples

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": "vaz0eCPVFvCU2X1gW2Z",
    "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",
    "lineItems": [
      {
        "identifier": "SKU-001",
        "quantity": 100,
        "rate": 50.00
      },
      {
        "identifier": "SKU-002",
        "quantity": 50,
        "rate": 75.50
      }
    ]
  }'

Success Response

HTTP Status: 200 OK Response Fields:
FieldTypeDescription
okbooleanIndicates whether the request was successful
dataobjectContains the purchase order creation response data
data.poIdstringUnique slug identifier for the purchase order (use this to retrieve or update the PO)
data.statestringState the PO was created in — DRAFTED or APPROVED (active)
data.skuMatchWarningsarrayNot applicable for THREE_WAY POs (unmatched SKUs are rejected). Reserved for future use
data.submitWarningsobjectPresent only when submit: true was passed but required fields were missing. The PO is still created in draft state

skuMatchWarnings Object

FieldTypeDescription
lineItemIdentifierstringThe identifier value of the unmatched line item
skuCodestringThe skuCode that was provided (if applicable)
skuNamestringThe skuName that was provided (if applicable)
messagestringHuman-readable description of why the SKU could not be matched

submitWarnings Object

FieldTypeDescription
poarray of stringsPO-level fields that were missing (e.g. "vendorId", "description", "paymentTerms")
lineItemsobjectMap of line item identifier to an array of missing fields (e.g. "quantity", "rate")

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

{
  "ok": true,
  "data": {
    "poId": "po_slug_identifier",
    "state": "DRAFTED"
  }
}

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

{
  "ok": true,
  "data": {
    "poId": "po_slug_identifier",
    "state": "ACTIVE"
  }
}

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

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

Response Example — state: "ACTIVE" with SKU warnings

{
  "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
{
  "ok": false,
  "error": {
    "code": "MISSING_REQUIRED_FIELD",
    "message": "poNumber is required"
  }
}

Invalid Procurement Type

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "INVALID_PROCUREMENT_TYPE",
    "message": "Invalid procurement type"
  }
}

Invalid Matching Type

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "INVALID_MATCHING_TYPE",
    "message": "Invalid matching type"
  }
}

Invalid Date

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "INVALID_DATE",
    "message": "Invalid date"
  }
}

Invalid Expected Delivery Date

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "INVALID_EXPECTED_DELIVERY_DATE",
    "message": "Invalid expected delivery date"
  }
}

SKU Required (THREE_WAY match)

HTTP Status: 400 Bad Request
{
  "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
{
  "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
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /body/lineItems: must NOT have fewer than 1 items"
  }
}
HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: /body/lineItems/0/quantity: must be number"
  }
}
HTTP Status: 400 Bad Request
{
  "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
{
  "ok": false,
  "error": {
    "code": "INVALID_CURRENCY",
    "message": "Invalid currency Only INR is supported at the moment"
  }
}

Invalid Description Length

HTTP Status: 400 Bad Request
{
  "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
{
  "ok": false,
  "error": {
    "code": "VENDOR_NOT_FOUND",
    "message": "Vendor not found"
  }
}

Invalid GSTIN

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "INVALID_GSTIN",
    "message": "Invalid GSTIN"
  }
}

Invalid State

HTTP Status: 400 Bad Request
{
  "ok": false,
  "error": {
    "code": "INVALID_STATE",
    "message": "Invalid state"
  }
}

Authentication Errors

HTTP Status: 401 Unauthorized
{
  "ok": false,
  "error": {
    "code": "MISSING_CREDENTIALS",
    "message": "Missing Credentials"
  }
}
{
  "ok": false,
  "error": {
    "code": "INVALID_API_KEY",
    "message": "Invalid API Key"
  }
}

Permission Errors

HTTP Status: 403 Forbidden
{
  "ok": false,
  "error": {
    "code": "INSUFFICIENT_PERMISSIONS",
    "message": "Permission check failed - PERMISSION_CHECK_FAILED"
  }
}

Purchase Order Creation Errors

HTTP Status: 500 Internal Server Error
{
  "ok": false,
  "error": {
    "code": "PROCUREMENT_CREATION_FAILED",
    "message": "Error creating procurement"
  }
}

Internal Error

HTTP Status: 500 Internal Server Error
{
  "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