Vendor Taxation Creation API
curl --request POST \
--url https://api.pazy.io/v1.0/vendor/:vendorId/taxationimport requests
url = "https://api.pazy.io/v1.0/vendor/:vendorId/taxation"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.pazy.io/v1.0/vendor/:vendorId/taxation', 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/taxation",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$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/taxation"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pazy.io/v1.0/vendor/:vendorId/taxation")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pazy.io/v1.0/vendor/:vendorId/taxation")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
response = http.request(request)
puts response.read_bodyVendor APIs
Vendor Taxation Creation API
Add a GSTIN or PAN to an existing vendor
Vendor Taxation Creation API
curl --request POST \
--url https://api.pazy.io/v1.0/vendor/:vendorId/taxationimport requests
url = "https://api.pazy.io/v1.0/vendor/:vendorId/taxation"
response = requests.post(url)
print(response.text)const options = {method: 'POST'};
fetch('https://api.pazy.io/v1.0/vendor/:vendorId/taxation', 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/taxation",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
]);
$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/taxation"
req, _ := http.NewRequest("POST", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.pazy.io/v1.0/vendor/:vendorId/taxation")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.pazy.io/v1.0/vendor/:vendorId/taxation")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.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
Adds a tax identifier to a vendor that already exists. The identifier being added is selected by the type query parameter — a GSTIN or a PAN — and its value is sent in the body.
A vendor can hold multiple GSTINs (one per state), as long as every GSTIN belongs to the same PAN. A PAN on the other hand is a single-value fallback for vendors without GST registration: it can only be added while the vendor has no GSTIN and no PAN at all.
Use the Vendor Details API to check what the vendor currently holds before calling. To change a value that already exists, use the Vendor Taxation Update API instead — this endpoint never overwrites an existing identifier.
Path Parameters
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
vendorId | string | Yes | 1-100 characters | Slug / unique identifier of the vendor |
Query Parameters
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
type | string | Yes | GSTIN or PAN | Which tax identifier is being added |
Body Parameters
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
value | string | Yes | Non-empty; must be a valid GSTIN or PAN per type | The tax identifier to add. Leading and trailing whitespace is trimmed. |
400 VALIDATION_ERROR — they are not silently ignored.
Values are matched case-sensitively against upper-case formats. Send
29ABCDE1234F1Z5, not 29abcde1234f1z5 — a lower-case value is rejected as an invalid format.Adding a GSTIN
type=GSTIN applies the following checks, in order. The first one that fails rejects the request and nothing is written.
| Check | Rejected with |
|---|---|
| Vendor exists in your organization | 404 VENDOR_NOT_FOUND |
| Caller is an admin / bookkeeper, or the vendor’s owner | 403 ACCESS_DENIED |
Vendor state is ACTIVE or APPROVAL_PENDING | 400 VALIDATION_ERROR — Vendor is not active |
value is a valid GSTIN format | 400 INVALID_GST_NUMBER |
| The GSTIN does not belong to your own business (it is not built on one of your organization’s PANs) | 400 VALIDATION_ERROR |
| No other vendor in the organization already uses this GSTIN | 400 DUPLICATE_GSTIN — Duplicate GSTIN |
| This vendor does not already hold this GSTIN | 400 DUPLICATE_GSTIN — GSTIN already exists for this vendor |
| The new GSTIN carries the same PAN as the vendor’s existing GSTIN / PAN | 400 VALIDATION_ERROR |
- The standard 15-character GSTIN —
^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[CDSZ][0-9A-Z]{1}$, for example29ABCDE1234F1Z5 - Any 15-character value beginning with
99, used for UIN-style registrations
Duplicate GSTIN is not raised for a GSTIN held by another vendor — the per-vendor check (GSTIN already exists for this vendor) always applies regardless.
Adding a PAN
type=PAN applies the same vendor, access, and state checks, then:
| Check | Rejected with |
|---|---|
value is a valid PAN format — ^[A-Z]{5}[0-9]{4}[A-Z]{1}$, for example ABCDE1234F | 400 INVALID_PAN_NUMBER |
| The PAN does not belong to your own business | 400 VALIDATION_ERROR |
| The vendor has no GSTIN and no PAN on record | 400 VALIDATION_ERROR — PAN can only be added when no GST or PAN exists for the vendor |
There is no way to add a PAN alongside a GSTIN. For a GST-registered vendor the PAN is already implied by the GSTIN — Pazy derives it rather than storing it separately. Use
type=PAN only for vendors that are not GST registered.What Happens After the Write
Once the identifier is committed, Pazy runs a few follow-up steps:- Verification. The value is checked against the GST portal (for GSTIN) or the PAN service (for PAN), and the result is returned as
isVerifiedin the response. A value that cannot be verified is still saved —isVerifiedis simplyfalse. - Audit trail. A
CREATEentry is recorded against the vendor with sourceAPI, visible through the Vendor Audit Trail API. - Accounting sync. If an accounting integration is connected, the new tax identifier is pushed to it.
- MSME check. A background MSME verification is queued — for GSTIN only when this is the vendor’s first tax identifier, and always for PAN.
200 OK and the identifier remains saved.
Code Examples
curl -X POST "https://api.pazy.io/v1.0/vendor/<vendorId>/taxation?type=GSTIN" \
-H "Authorization: Api-Key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"value": "29ABCDE1234F1Z5"
}'
const response = await fetch(
'https://api.pazy.io/v1.0/vendor/<vendorId>/taxation?type=GSTIN',
{
method: 'POST',
headers: {
'Authorization': 'Api-Key YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
value: '29ABCDE1234F1Z5'
})
}
);
const result = await response.json();
import requests
url = "https://api.pazy.io/v1.0/vendor/<vendorId>/taxation"
headers = {
"Authorization": "Api-Key YOUR_API_KEY",
"Content-Type": "application/json"
}
params = {"type": "GSTIN"}
payload = {"value": "29ABCDE1234F1Z5"}
response = requests.post(url, headers=headers, params=params, json=payload)
result = response.json()
cURL
curl -X POST "https://api.pazy.io/v1.0/vendor/<vendorId>/taxation?type=PAN" \
-H "Authorization: Api-Key YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"value": "ABCDE1234F"
}'
Success Response
HTTP Status:200 OK
Response Fields:
| Field | Type | Description |
|---|---|---|
ok | boolean | Indicates whether the request was successful |
data | object | Result of the operation |
data.message | string | Human-readable confirmation — GSTIN added successfully or PAN added successfully |
data.isVerified | boolean | true if the identifier was verified with the source authority. For GSTIN this means the registration is active on the GST portal; for PAN it means the PAN was confirmed. false when verification failed or was unavailable — the identifier is still saved. |
Response Example — GSTIN Added
{
"ok": true,
"data": {
"message": "GSTIN added successfully",
"isVerified": true
}
}
Response Example — PAN Added, Not Verified
{
"ok": true,
"data": {
"message": "PAN added successfully",
"isVerified": false
}
}
Error Responses
Invalid GSTIN Format
HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "INVALID_GST_NUMBER",
"message": "Invalid GST format"
}
}
Invalid PAN Format
HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "INVALID_PAN_NUMBER",
"message": "Invalid PAN format"
}
}
Duplicate GSTIN
Returned when another vendor in your organization already holds this GSTIN, or when this vendor already holds it. HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "DUPLICATE_GSTIN",
"message": "Duplicate GSTIN"
}
}
{
"ok": false,
"error": {
"code": "DUPLICATE_GSTIN",
"message": "GSTIN already exists for this vendor"
}
}
PAN Mismatch
Returned when the new GSTIN’s embedded PAN differs from the PAN of the identifiers the vendor already holds. A single vendor cannot span two PANs — create a separate vendor for the other legal entity. HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "New GSTIN must have the same PAN as the vendor's existing GSTIN/PAN"
}
}
PAN Not Allowed
Returned when a PAN is added to a vendor that already has a GSTIN or a PAN. HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "PAN can only be added when no GST or PAN exists for the vendor"
}
}
Tax Identifier Belongs to Your Own Business
Returned when the submitted GSTIN or PAN resolves to one of your own organization’s PANs. HTTP Status:400 Bad Request
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "This GSTIN belongs to your own business, not the vendor's. Please enter the vendor's GSTIN."
}
}
Vendor Not Active
Returned when the vendor is in any state other thanACTIVE or APPROVAL_PENDING — for example a disabled vendor.
HTTP Status: 400 Bad Request
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Vendor is not active"
}
}
Validation Error
Returned before any processing when the request does not match the schema — a missing or unsupportedtype, a missing or empty value, or an unknown property.
HTTP Status: 400 Bad Request
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed: /type: must be equal to one of the allowed values"
}
}
{
"ok": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed: value: must have required property 'value'"
}
}
Vendor Not Found
HTTP Status:404 Not Found
{
"ok": false,
"error": {
"code": "VENDOR_NOT_FOUND",
"message": "Vendor not found"
}
}
Access Denied
Returned when the caller is neither an admin / bookkeeper nor the vendor’s owner. HTTP Status:403 Forbidden
{
"ok": false,
"error": {
"code": "ACCESS_DENIED",
"message": "Access denied: You can only update 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
The API key must carry vendor update permission. HTTP Status:403 Forbidden
{
"ok": false,
"error": {
"code": "INSUFFICIENT_PERMISSIONS",
"message": "Permission check failed - PERMISSION_CHECK_FAILED"
}
}
Internal Error
Returned when the identifier could not be persisted. The write is a single transaction, so nothing is saved and the call is safe to retry. HTTP Status:500 Internal Server Error
{
"ok": false,
"error": {
"code": "INTERNAL_ERROR",
"message": "Error while adding vendor taxation"
}
}
Best Practices
- Send GSTIN and PAN values in upper case — a lower-case value is rejected as an invalid format
- Use this endpoint to add an identifier and the Vendor Taxation Update API to change one; adding a value the vendor already holds is rejected rather than treated as an update
- Check the vendor’s current GSTIN / PAN with the Vendor Details API first — it tells you whether to POST or PATCH
- For a multi-state vendor, add one GSTIN per state through repeated calls. All of them must share the same PAN
- Add a PAN only for vendors without GST registration, and add it before any GSTIN — once a GSTIN exists, PAN can no longer be added
- Treat
isVerified: falseas “saved but unconfirmed”, not as a failure. Re-check the vendor later if verification matters to your workflow - GST and PAN can also be set at creation time through the Vendor Creation API — use this endpoint for vendors that are already on record
Was this page helpful?