Vendor Update API
curl --request PATCH \
--url https://api.pazy.io/v1.0/vendor/:vendorIdimport requests
url = "https://api.pazy.io/v1.0/vendor/:vendorId"
response = requests.patch(url)
print(response.text)const options = {method: 'PATCH'};
fetch('https://api.pazy.io/v1.0/vendor/:vendorId', 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/vendor/:vendorId",
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/vendor/:vendorId"
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/vendor/:vendorId")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pazy.io/v1.0/vendor/:vendorId")
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_bodyVendor APIs
Vendor Update API
Update one or more fields of an existing vendor — name, contact, owner, address, payment routing, and custom (flex) fields
Vendor Update API
curl --request PATCH \
--url https://api.pazy.io/v1.0/vendor/:vendorIdimport requests
url = "https://api.pazy.io/v1.0/vendor/:vendorId"
response = requests.patch(url)
print(response.text)const options = {method: 'PATCH'};
fetch('https://api.pazy.io/v1.0/vendor/:vendorId', 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/vendor/:vendorId",
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/vendor/:vendorId"
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/vendor/:vendorId")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pazy.io/v1.0/vendor/:vendorId")
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 | Constraints | Description |
|---|---|---|---|---|
vendorId | string | Yes | 1-100 characters | Slug / unique identifier of the vendor to update |
Body Parameters
The request body has a single top-level key,vendorData. All fields inside vendorData are optional — only the fields you include will be considered for update. Fields you omit are left untouched.
Two schema rules apply to the whole payload:
- At least one field is required.
vendorDatamust contain a minimum of one property;{"vendorData": {}}is rejected with400 VALIDATION_ERROR. - Unknown keys are rejected.
vendorData,address,communications, and eachcustomFieldsentry all reject properties that are not listed below with400 VALIDATION_ERROR— they are not silently ignored.
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
vendorData | object | Yes | At least 1 property | Container for the partial update payload |
vendorData.name | string | No | 1-255 characters | Vendor display name. Also renames the underlying vendor user record (the last word becomes the last name, everything before it the first name). |
vendorData.email | string | No | Valid email format | Vendor email address. Replaces the vendor’s active email identifier. If the email’s domain changes, the vendor logo is re-fetched from the new domain. |
vendorData.phone | string | No | +{country_code}{number} | Vendor phone number, e.g. +919876543210. Must include the + and country code — a value without it is rejected per-field with INVALID_NUMBER. |
vendorData.owner | string | No | 1-100 characters | New owner — the user id / slug of a user in your organization. If the new owner was a watcher on this vendor, that watcher entry is removed (a user cannot be both owner and watcher). |
vendorData.address | object | No | Known keys only | Vendor address. Merged with the existing address. See Address Fields below. |
vendorData.communications | object | No | Known keys only | Payment confirmation email recipients. Merged per list. See Communications Fields below. |
vendorData.customFields | object | No | Slug-keyed map | Flex fields to set, keyed by field slug. See Custom Fields below. |
Address Fields
vendorData.address accepts the following properties. All are optional and merged with the existing address — any key you omit (or send empty) keeps its current value, so you only need to send the parts that change. The one exception is country, which falls back to India rather than the existing value when the vendor is DOMESTIC.
| Field | Type | Description |
|---|---|---|
addressLine1 | string | Address line 1. Stored encrypted. |
addressLine2 | string | Address line 2. Stored encrypted. |
city | string | City. Validated against known city names — an unknown value rejects the whole address with INVALID_ADDRESS. |
state | string | State code. Resolved against the state list for the given locality; an unresolvable code rejects the whole address with INVALID_ADDRESS. |
pinCode | string | Postal / PIN code. For DOMESTIC this is validated as an Indian PIN code; for INTERNATIONAL it is accepted as-is. |
zipCode | string | Accepted by the schema for convenience, but not persisted — send pinCode to actually change the postal code. |
country | string | Country name. Validated against known country names. When omitted, DOMESTIC vendors default to India and INTERNATIONAL vendors keep their existing country. |
locality | string | DOMESTIC or INTERNATIONAL. Selects the validation rules used for state, pinCode, and the country default. |
Address validation is all-or-nothing: the first invalid field rejects the entire
address update with a single INVALID_ADDRESS error whose message names the offending field (for example, Given address is not valid. Invalid state code). No partial address is written.Communications Fields
vendorData.communications configures who receives payment confirmation emails. Each list contains user ids / slugs from your organization.
| Field | Type | Description |
|---|---|---|
to | array of strings | ”To” recipients (user ids / slugs) |
cc | array of strings | ”CC” recipients (user ids / slugs) |
bcc | array of strings | ”BCC” recipients (user ids / slugs) |
- Each list is replaced, not appended to. Sending
to: ["user_a"]makesuser_athe only “To” recipient. - Merged per list. Lists you omit — or send as an empty array — keep their current recipients. There is no way to clear a list through this endpoint.
- Duplicates are removed within each list.
- All ids are validated together. If any id across
to,cc, andbccis not a user in your organization, the wholecommunicationsupdate is rejected withINVALID_USER_IN_COMMUNICATIONSand the offending ids are returned inwrongUserSlugs.
Custom Fields
vendorData.customFields is a map keyed by the flex-field slug configured for your organization. Each value is an object requiring both type and value:
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Flex-field type. Must match the field’s configured type. One of VARCHAR, DATETIME, DROPDOWN, NUMERIC |
value | string | number | Yes | Value to set — see the per-type table below |
value per type:
type | Expected value | Rejected with |
|---|---|---|
VARCHAR | Any text | — |
DATETIME | Date string in YYYY-MM-DD | INVALID_DATE_INPUT |
NUMERIC | A number (numeric strings are accepted and coerced) | INVALID_NUMERIC_INPUT |
DROPDOWN | The id of the tag value to select, not its label — must be an integer belonging to that slug | INVALID_VALUE_ID |
context.updates.customFields.<slug> for each. Notes:
- The
expense-headslug cannot be updated through this endpoint and always returnsTAG_NOT_ALLOWED. - Use the Tags APIs to look up valid slugs and, for
DROPDOWN, the tag value ids to send.
Full Request Body Example
Every supported field in one payload — in practice, send only the ones you want to change:{
"vendorData": {
"name": "Acme Suppliers",
"email": "accounts@acmesuppliers.com",
"phone": "+919876543210",
"owner": "<user-slug>",
"address": {
"addressLine1": "24, Industrial Estate",
"addressLine2": "Off Hosur Road",
"city": "Bengaluru",
"state": "KA",
"pinCode": "560068",
"country": "India",
"locality": "DOMESTIC"
},
"communications": {
"to": ["<user-slug>"],
"cc": ["<user-slug>", "<user-slug>"],
"bcc": []
},
"customFields": {
"vendor-category": { "type": "DROPDOWN", "value": 4821 },
"internal-code": { "type": "VARCHAR", "value": "ACME-2024" },
"contract-expiry": { "type": "DATETIME", "value": "2027-03-31" },
"credit-days": { "type": "NUMERIC", "value": 45 }
}
}
}
Code Examples
curl -X PATCH https://api.pazy.io/v1.0/vendor/<vendorId> \
-H "Authorization: Api-Key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"vendorData": {
"name": "Acme Suppliers",
"email": "vendor@example.com",
"phone": "+919876543210"
}
}'
const response = await fetch('https://api.pazy.io/v1.0/vendor/<vendorId>', {
method: 'PATCH',
headers: {
'Authorization': 'Api-Key YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
vendorData: {
name: 'Acme Suppliers',
email: 'vendor@example.com',
phone: '+919876543210'
}
})
});
const result = await response.json();
import requests
url = "https://api.pazy.io/v1.0/vendor/<vendorId>"
headers = {
"Authorization": "Api-Key YOUR_API_KEY",
"Content-Type": "application/json"
}
payload = {
"vendorData": {
"name": "Acme Suppliers",
"email": "vendor@example.com",
"phone": "+919876543210"
}
}
response = requests.patch(url, headers=headers, json=payload)
result = response.json()
Success Response
HTTP Status:200 OK
The endpoint always returns 200 OK when the request itself is valid — even when individual fields fail to update. Check ok first, then inspect context.updates.<field> to see the per-field outcome. A 200 with ok: false means the write itself failed and nothing was applied — see Update Failed.
Response Fields:
| Field | Type | Description |
|---|---|---|
ok | boolean | Indicates whether the request was processed |
update | boolean | Indicates that the update operation ran |
error | object | null | Top-level error if the entire request failed (null when per-field results are returned) |
context | object | Container for per-field results |
context.updates | object | Map keyed by the field name you sent in vendorData |
context.updates.<field>.update | boolean | true if that field was updated; false if it was rejected or skipped |
context.updates.<field>.error | object | Present only when update is false. Contains a code and message describing the reason. |
| Field | Failure code | Extra fields returned |
|---|---|---|
name | DUPLICATE_VENDOR_NAME | isDuplicate: true, duplicateVendor: { id, name } |
communications | INVALID_USER_IN_COMMUNICATIONS | wrongUserSlugs: [...] |
Response Example — Successful Multi-Field Update
{
"ok": true,
"update": true,
"error": null,
"context": {
"updates": {
"name": { "update": true },
"email": { "update": true },
"phone": { "update": true }
}
}
}
Response Example — Mixed Success and Failures
{
"ok": true,
"update": true,
"error": null,
"context": {
"updates": {
"name": {
"update": false,
"error": {
"code": "DUPLICATE_VENDOR_NAME",
"message": "Vendor with the same name already exist"
},
"isDuplicate": true,
"duplicateVendor": {
"id": "<otherVendorId>",
"name": "Acme Trading"
}
},
"phone": {
"update": false,
"error": {
"code": "SAME_VALUE_GIVEN",
"message": "Phone is same as the current one"
}
},
"owner": { "update": true }
}
}
}
Response Example — communications with Unknown Users
{
"ok": true,
"update": true,
"error": null,
"context": {
"updates": {
"communications": {
"update": false,
"error": {
"code": "INVALID_USER_IN_COMMUNICATIONS",
"message": "Users in communications do not exist in the organization"
},
"wrongUserSlugs": ["<unknownUserId>"]
}
}
}
}
Response Example — customFields Mixed Outcome
{
"ok": true,
"update": true,
"error": null,
"context": {
"updates": {
"customFields": {
"fasttrack": { "update": true },
"test-upload": { "update": true },
"wrong-tag": {
"update": false,
"error": {
"code": "INVALID_TAG",
"message": "Invalid tag key"
}
},
"hinge": {
"update": false,
"error": {
"code": "INVALID_TAG_TYPE",
"message": "Tag type is of 'DATETIME' but received VARCHAR"
}
}
}
}
}
}
Per-Field Error Codes
| Field | Error code | When it occurs |
|---|---|---|
name | DUPLICATE_VENDOR_NAME | Another vendor in the same organization already uses this name. Response includes duplicateVendor.id and duplicateVendor.name. Suppressed if your organization allows duplicate vendor names. |
name, email, phone, owner, address, communications, customFields.<slug> | SAME_VALUE_GIVEN | The submitted value matches the existing value — nothing changed |
phone | INVALID_NUMBER | Phone number does not follow +{country_code}{number} |
owner | INVALID_OWNER | Owner user id does not exist in your organization |
address | INVALID_ADDRESS | A field in the address failed validation. The message names the reason: Invalid state code, Invalid city name, Invalid pin-code, or Invalid country name. |
communications | INVALID_USER_IN_COMMUNICATIONS | One or more of the to / cc / bcc slugs is not a valid user. Invalid ids are returned in wrongUserSlugs. |
customFields.<slug> | INVALID_TAG | The slug does not match any flex field configured for the organization |
customFields.<slug> | TAG_NOT_ALLOWED | The slug is expense-head, which cannot be updated through this endpoint |
customFields.<slug> | INVALID_VALUE_ID | For DROPDOWN: the value id is unknown, not an integer, or not linked to that tag |
customFields.<slug> | INVALID_TAG_TYPE | The type sent does not match the configured flex-field type |
customFields.<slug> | INVALID_DATE_INPUT | For DATETIME: value is not in YYYY-MM-DD |
customFields.<slug> | INVALID_NUMERIC_INPUT | For NUMERIC: value is not a finite number |
Error Responses
Validation Error
HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed: /vendorData/email: must match format \"email\""
}
}
email format, a name outside 1-255 characters, an empty vendorData, an unknown property, a customFields entry missing type or value, or a locality other than DOMESTIC / INTERNATIONAL.
Update Failed
HTTP Status:200 OK
{
"ok": false,
"update": false,
"error": {
"errorCode": "UPDATE_FAILED"
}
}
errorCode (not code) and has no context, so check ok before reading context.updates. Safe to retry.
Vendor Not Found
HTTP Status:404 Not Found
{
"ok": false,
"error": {
"code": "VENDOR_NOT_FOUND",
"message": "Vendor not found"
}
}
Access Denied
HTTP Status:403 Forbidden
{
"ok": false,
"error": {
"code": "ACCESS_DENIED",
"message": "Access denied: You can only view vendors you own"
}
}
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": "INTERNAL_ERROR",
"message": "Internal error"
}
}
Best Practices
- Send only the fields you want to change — omitted fields are left untouched
- Always inspect
context.updates.<field>for the outcome; a200 OKdoes not guarantee every field was applied - For
phone, normalize to E.164 (+{country_code}{number}) before sending — values without+are rejected withINVALID_NUMBER - For
address, the API merges with the existing record. Send only the keys you want to change rather than the full address — and usepinCode, notzipCode, to change the postal code - For
communications, each list you send replaces that list. Fetch the current recipients first and send the full intended list, rather than just the ids you are adding - For
customFields, thetypeyou send must exactly match the field’s configured type — a mismatch is rejected per-field withINVALID_TAG_TYPE - For
DROPDOWNcustom fields, send the tag value id, not the displayed label — a label is rejected withINVALID_VALUE_ID - Use the Vendor Details API to fetch the current state, and the Vendor Audit Trail API to inspect change history after updates
Was this page helpful?