Purchase Order Update API
curl --request PATCH \
--url https://api.pazy.io/v1.0/procurement/purchase-order/:poIdimport requests
url = "https://api.pazy.io/v1.0/procurement/purchase-order/:poId"
response = requests.patch(url)
print(response.text)const options = {method: 'PATCH'};
fetch('https://api.pazy.io/v1.0/procurement/purchase-order/:poId', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pazy.io/v1.0/procurement/purchase-order/:poId",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.pazy.io/v1.0/procurement/purchase-order/:poId"
req, _ := http.NewRequest("PATCH", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://api.pazy.io/v1.0/procurement/purchase-order/:poId")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pazy.io/v1.0/procurement/purchase-order/:poId")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
response = http.request(request)
puts response.read_bodyPurchase Order APIs
Purchase Order Update API
Update an existing purchase order by its identifier. Only POs in DRAFTED or DECLINED state can be updated.
Purchase Order Update API
curl --request PATCH \
--url https://api.pazy.io/v1.0/procurement/purchase-order/:poIdimport requests
url = "https://api.pazy.io/v1.0/procurement/purchase-order/:poId"
response = requests.patch(url)
print(response.text)const options = {method: 'PATCH'};
fetch('https://api.pazy.io/v1.0/procurement/purchase-order/:poId', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.pazy.io/v1.0/procurement/purchase-order/:poId",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.pazy.io/v1.0/procurement/purchase-order/:poId"
req, _ := http.NewRequest("PATCH", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://api.pazy.io/v1.0/procurement/purchase-order/:poId")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pazy.io/v1.0/procurement/purchase-order/:poId")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
response = http.request(request)
puts response.read_bodyAuthentication
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
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
poId | string | Yes | The unique slug identifier of the purchase order to update (returned from the PO creation or search API) |
Body Parameters
All body fields are optional. Only the fields you provide will be updated.| Parameter | Type | Required | Description |
|---|---|---|---|
poNumber | string | No | Purchase order number (minimum 1 character) |
lineItems | array | No | List of line items. Replaces all existing line items if provided (minimum 1 item) |
poType | string | No | Purchase order type. Valid values: GOODS, SERVICES |
matchingType | string | No | Matching type. Valid values: TWO_WAY, THREE_WAY |
poDate | string | No | Purchase order date in ISO-8601 format (YYYY-MM-DD) |
vendorId | string | No | Slug identifier of the vendor to link to the PO |
description | string | No | Short description for the purchase order (1-255 characters) |
currency | string | No | ISO 4217 currency code (3 characters, e.g., INR) |
paymentTerms | string | No | Free form outline of the agreed payment terms |
deliveryDate | string | No | Requested delivery date in ISO-8601 format (YYYY-MM-DD) |
termsAndConditions | string | No | Terms and conditions for the purchase order |
additionalNotes | string | No | Additional notes for delivery or special requirements |
state | string | No | Target state: DRAFTED or ACTIVE. When ACTIVE is requested the API validates required fields (merging body with what is already on the PO). If all required fields are present the PO is activated; otherwise it stays in DRAFTED and the response includes submitWarnings. |
tags | object | No | PO-level custom fields (also called tags). Map of field slug to a { value } object. Supports dropdown, text, date, and numeric fields. See Tags and Custom Fields below. |
Line Items Object
Each item in thelineItems array must contain:
| 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. Takes priority over skuName |
skuName | string | Required for THREE_WAY | Name of an existing SKU in your inventory (used when skuCode is not provided) |
tags | object | No | Line-item-level custom fields (also called tags). Map of field slug to a { value } object. |
taxId | integer | No | ID of a GST tax configured in your organization. The tax is applied to the line item total (quantity × rate) |
taxAmount | number | No | GST amount for this line item. When provided with taxId, it must match the calculated tax based on the tax rate |
GST on line items: ProvidetaxIdto apply GST to a line item. WhentaxAmountis provided, it must match the calculated tax for that line item or the request returns a400error. Line items withouttaxIdare unaffected.
SKU matching (THREE_WAY): When the PO usesTHREE_WAYmatching andlineItemsare included in the update, every line item must provideskuCodeorskuName, and it must resolve to an existing SKU. If any line item is missing both fields or cannot be matched, the request is rejected with a400error and the PO is not updated.
Custom Fields
Custom fields use their organization-configured slugs. Dropdown values are organization tag IDs, date values useYYYY-MM-DD, and numeric values are sent as numbers. Include tags on a line item to set line-level custom fields. When lineItems is supplied, it replaces the existing line-item set together with its custom fields.
To get the slugs and field types available to your organization, call the Tag List API and use each data.tags[].id value as the key in tags. For dropdown fields, use the Tag Detail API with that slug to retrieve valid option IDs.
{
"tags": {
"project-code": { "value": "PRJ-42" },
"budget-amount": { "value": 125000.5 }
},
"lineItems": [
{
"identifier": "SKU-001",
"quantity": 120,
"rate": 50,
"tags": { "warehouse": { "value": "WH-01" } }
}
]
}
Editable States
A purchase order can only be updated when it is in one of the following states:| State | Description |
|---|---|
DRAFTED | PO is in draft — editable |
DECLINED | PO was declined and returned for revision — editable |
PENDING, APPROVED, CLOSED, ARCHIVED) will return a 400 error.
Tags and Custom Fields
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:
To discover the available tag slugs, their types, and (for
DROPDOWN tags) the valid value ids, see the tag APIs: the Tag List API, and Tag Detail API.{
"tags": {
"cost-center": { "value": 42 },
"project-code": { "value": "PRJ-2024" },
"expected-by": { "value": "2024-03-15" },
"budget": { "value": 5000 }
}
}
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 |
NUMBER | A number |
NUMERIC | A number (legacy numeric field type) |
Code Examples
curl -X PATCH https://api.pazy.io/v1.0/procurement/purchase-order/po_slug_identifier \
-H "Authorization: Api-Key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"description": "Updated office supplies order",
"deliveryDate": "2024-03-01",
"paymentTerms": "Net 45 days",
"tags": {
"cost-center": { "value": 42 },
"project-code": { "value": "PRJ-2024" }
},
"lineItems": [
{
"identifier": "SKU-001",
"quantity": 120,
"rate": 50.00,
"tags": {
"budget-line": { "value": "Stationery" }
},
"taxId": 3961,
"taxAmount": 300.00
}
]
}'
const response = await fetch('https://api.pazy.io/v1.0/procurement/purchase-order/po_slug_identifier', {
method: 'PATCH',
headers: {
'Authorization': 'Api-Key YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
description: 'Updated office supplies order',
deliveryDate: '2024-03-01',
paymentTerms: 'Net 45 days',
tags: {
'cost-center': { value: 42 },
'project-code': { value: 'PRJ-2024' }
},
lineItems: [
{
identifier: 'SKU-001',
quantity: 120,
rate: 50.00,
tags: {
'budget-line': { value: 'Stationery' }
},
taxId: 3961,
taxAmount: 300.00
}
]
})
});
const result = await response.json();
import requests
url = "https://api.pazy.io/v1.0/procurement/purchase-order/po_slug_identifier"
headers = {
"Authorization": "Api-Key YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"description": "Updated office supplies order",
"deliveryDate": "2024-03-01",
"paymentTerms": "Net 45 days",
"tags": {
"cost-center": {"value": 42},
"project-code": {"value": "PRJ-2024"}
},
"lineItems": [
{
"identifier": "SKU-001",
"quantity": 120,
"rate": 50.00,
"tags": {
"budget-line": {"value": "Stationery"}
},
"taxId": 3961,
"taxAmount": 300.00
}
]
}
response = requests.patch(url, headers=headers, json=payload)
result = response.json()
Success Response
HTTP Status:200 OK
Response Fields:
| Field | Type | Description |
|---|---|---|
ok | boolean | Indicates whether the request was successful |
data | object | Contains the update response data |
data.poId | string | Unique slug identifier of the updated purchase order |
data.state | string | Present when state was requested. ACTIVE if activation succeeded, DRAFTED if it fell back |
data.skuMatchWarnings | array | Present when one or more line items could not be matched to a SKU |
data.submitWarnings | object | Present when state: ACTIVE was requested but required fields were missing |
submitWarnings Object
| Field | Type | Description |
|---|---|---|
po | array of strings | PO-level fields that were missing (e.g. "vendorId", "paymentTerms") |
lineItems | object | Map of line item identifier to missing fields (e.g. "quantity", "rate") |
Response Example — update only (no state change)
{
"ok": true,
"data": {
"poId": "po_slug_identifier"
}
}
Response Example — activated (state: "ACTIVE", all fields present)
{
"ok": true,
"data": {
"poId": "po_slug_identifier",
"state": "ACTIVE"
}
}
Response Example — fell back to draft (state: "ACTIVE", fields missing)
{
"ok": true,
"data": {
"poId": "po_slug_identifier",
"state": "DRAFTED",
"submitWarnings": {
"po": ["paymentTerms"],
"lineItems": {}
}
}
}
Error Responses
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"
}
}
Purchase Order Not Found
HTTP Status:404 Not Found
{
"ok": false,
"error": {
"code": "PURCHASE_ORDER_NOT_FOUND",
"message": "Purchase order not found"
}
}
Invalid State
HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "INVALID_STATE",
"message": "Purchase order cannot be updated in APPROVED state"
}
}
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 Currency
HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "INVALID_CURRENCY",
"message": "Invalid currency Only INR is supported at the moment"
}
}
Vendor Not Found
HTTP Status:404 Not Found
{
"ok": false,
"error": {
"code": "VENDOR_NOT_FOUND",
"message": "Vendor not found"
}
}
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"
}
}
Internal Error
HTTP Status:500 Internal Server Error
{
"ok": false,
"error": {
"code": "PROCUREMENT_CREATION_FAILED",
"message": "Error creating procurement"
}
}
Best Practices
- Only POs in
DRAFTEDorDECLINEDstate can be updated. Check the PO state first using the Purchase Order Details API - All body fields are optional — send only the fields you want to change
- If you provide
lineItems, the entire list of line items will be replaced; ensure you include all intended line items in the request - Use the
poIdreturned from the Purchase Order Creation API or Purchase Order Search API to obtain the identifier - Use ISO 8601 date format (YYYY-MM-DD) for all date fields
- Pass
state: "ACTIVE"to activate a draft PO in the same request as your field updates. Validation merges the body with what is already stored, so you only need to send the fields that are changing — already-set fields on the PO count toward the activation check - Check
submitWarningsin the response whenstate: "ACTIVE"is sent — it identifies any remaining missing fields that prevented activation - Tags can be set both on the PO (
tagsin the body) and on individual line items (tagsinside eachlineItemsentry). Use the tag APIs to look up valid tag slugs, types, and dropdown value ids before sending them
Was this page helpful?