MENU navbar-image

Introduction

Interactive documentation for the versioned Dukanam mobile API.

Use the login helper below to create a Sanctum device token. The token is saved only in this browser and automatically supplied to every authenticated Try It Out request.

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {ACCESS_TOKEN}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Use the API test login above. A successful login automatically copies the returned data.token into every authenticated endpoint and keeps it across page reloads.

Authentication

Create and manage mobile bearer tokens.

POST api/v1/auth/register

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/auth/register" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"email\": \"[email protected]\",
    \"phone\": \"i\",
    \"business_name\": \"y\",
    \"password\": \"pBNvYg\",
    \"device_name\": \"Scribe API Docs\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/auth/register"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "email": "[email protected]",
    "phone": "i",
    "business_name": "y",
    "password": "pBNvYg",
    "device_name": "Scribe API Docs"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/register';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'b',
            'email' => '[email protected]',
            'phone' => 'i',
            'business_name' => 'y',
            'password' => 'pBNvYg',
            'device_name' => 'Scribe API Docs',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/auth/register

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

name   string     

Must not be greater than 128 characters. Example: b

email   string     

Must be a valid email address. Must not be greater than 255 characters. Example: [email protected]

phone   string  optional    

Must not be greater than 32 characters. Example: i

business_name   string     

Must not be greater than 128 characters. Example: y

password   string     

Must be at least 8 characters. Example: pBNvYg

device_name   string     

A name for the device token. Example: Scribe API Docs

POST api/v1/auth/login

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/auth/login" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"[email protected]\",
    \"password\": \"|]|{+-\",
    \"remember\": false,
    \"device_name\": \"Scribe API Docs\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/auth/login"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "[email protected]",
    "password": "|]|{+-",
    "remember": false,
    "device_name": "Scribe API Docs"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/login';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => '[email protected]',
            'password' => '|]|{+-',
            'remember' => false,
            'device_name' => 'Scribe API Docs',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/auth/login

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

Must be a valid email address. Example: [email protected]

password   string     

Example: |]|{+-

remember   boolean  optional    

Example: false

device_name   string     

A name for the device token. Example: Scribe API Docs

Email a password reset link.

Always returns the same response so callers cannot discover registered email addresses.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/auth/forgot-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"[email protected]\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/auth/forgot-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "[email protected]"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/forgot-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => '[email protected]',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "If an account exists for that email address, a password reset link has been sent."
}
 

Request      

POST api/v1/auth/forgot-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

email   string     

The account email address. Example: [email protected]

Reset an account password.

A successful reset revokes every mobile bearer token and sends a security notification.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/auth/reset-password" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"token\": \"reset-token\",
    \"email\": \"[email protected]\",
    \"password\": \"new-secure-password\",
    \"password_confirmation\": \"new-secure-password\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/auth/reset-password"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "token": "reset-token",
    "email": "[email protected]",
    "password": "new-secure-password",
    "password_confirmation": "new-secure-password"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/reset-password';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'token' => 'reset-token',
            'email' => '[email protected]',
            'password' => 'new-secure-password',
            'password_confirmation' => 'new-secure-password',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "message": "Password reset successfully. Sign in with your new password."
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "This password reset token is invalid."
        ]
    }
}
 

Request      

POST api/v1/auth/reset-password

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

Body Parameters

token   string     

The token delivered in the password-reset email. Example: reset-token

email   string     

The account email address. Example: [email protected]

password   string     

The new password, at least eight characters. Example: new-secure-password

password_confirmation   string     

Must match password. Example: new-secure-password

Return the current user and active workspaces available under each plan and seat assignment.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/auth/me" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/auth/me"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/me';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/auth/me

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

DELETE api/v1/auth/token

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/auth/token" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/auth/token"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/token';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/v1/auth/token

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

DELETE api/v1/auth/tokens

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/auth/tokens" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/auth/tokens"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/auth/tokens';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/v1/auth/tokens

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Businesses

List the active workspaces available under the caller's current plan and seat assignment.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

Response

Response Fields

data   object     
logo_url   string|null     

Temporary signed object-storage URL when the logo is stored on S3. Refresh the resource after it expires.

GET api/v1/businesses/{business}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Response

Response Fields

data   object     
logo_url   string|null     

Temporary signed object-storage URL when the logo is stored on S3. Refresh the resource after it expires.

subscription   object     
plan   object     
limits   object     
items   integer     

Maximum catalogue item count; 0 means unlimited.

Update business settings.

requires authentication

The default_locale field may only be changed by workspace owners and admins. Manual-sharing templates may only be changed by those roles when the paid feature is enabled for the business.

Example request:
curl --request PATCH \
    "https://dukanam.com/api/v1/businesses/1/settings" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"legal_name\": \"n\",
    \"phone\": \"g\",
    \"email\": \"[email protected]\",
    \"gst_registration_type\": \"unregistered\",
    \"gstin\": \"dljnikhwaykcmyu\",
    \"address_line_1\": \"w\",
    \"address_line_2\": \"p\",
    \"city\": \"w\",
    \"state_code\": 1,
    \"pincode\": \"569775\",
    \"default_place_of_supply\": 1,
    \"invoice_prefix\": \"HAWIOT\\/26-27\\/\",
    \"prices_include_tax\": false,
    \"authorized_signatory\": \"g\",
    \"bank_details\": \"z\",
    \"upi_id\": \"m\",
    \"theme\": \"blue\",
    \"default_locale\": \"en\",
    \"reminder_message_template\": \"i\",
    \"reminder_email_subject_template\": \"y\",
    \"invoice_share_subject_template\": \"v\",
    \"invoice_share_message_template\": \"d\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/settings"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "legal_name": "n",
    "phone": "g",
    "email": "[email protected]",
    "gst_registration_type": "unregistered",
    "gstin": "dljnikhwaykcmyu",
    "address_line_1": "w",
    "address_line_2": "p",
    "city": "w",
    "state_code": 1,
    "pincode": "569775",
    "default_place_of_supply": 1,
    "invoice_prefix": "HAWIOT\/26-27\/",
    "prices_include_tax": false,
    "authorized_signatory": "g",
    "bank_details": "z",
    "upi_id": "m",
    "theme": "blue",
    "default_locale": "en",
    "reminder_message_template": "i",
    "reminder_email_subject_template": "y",
    "invoice_share_subject_template": "v",
    "invoice_share_message_template": "d"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/settings';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'b',
            'legal_name' => 'n',
            'phone' => 'g',
            'email' => '[email protected]',
            'gst_registration_type' => 'unregistered',
            'gstin' => 'dljnikhwaykcmyu',
            'address_line_1' => 'w',
            'address_line_2' => 'p',
            'city' => 'w',
            'state_code' => 1,
            'pincode' => '569775',
            'default_place_of_supply' => 1,
            'invoice_prefix' => 'HAWIOT/26-27/',
            'prices_include_tax' => false,
            'authorized_signatory' => 'g',
            'bank_details' => 'z',
            'upi_id' => 'm',
            'theme' => 'blue',
            'default_locale' => 'en',
            'reminder_message_template' => 'i',
            'reminder_email_subject_template' => 'y',
            'invoice_share_subject_template' => 'v',
            'invoice_share_message_template' => 'd',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 1,
        "name": "Anika Stores",
        "slug": "anika-stores",
        "role": "owner",
        "phone": "9876543210",
        "email": "[email protected]",
        "legal_name": "Anika Stores Private Limited",
        "gstin": "27AAPFU0939F1ZV",
        "gst_registration_type": "regular",
        "currency": "INR",
        "timezone": "Asia/Kolkata",
        "address": {
            "line_1": "12 Market Road",
            "line_2": null,
            "city": "Pune",
            "state_code": "27",
            "pincode": "411001"
        },
        "default_place_of_supply": "27",
        "invoice_prefix": "INV",
        "prices_include_tax": true,
        "upi_id": "anikastores@bank",
        "theme": "indigo",
        "default_locale": "hi",
        "is_active": true,
        "manual_sharing": {
            "enabled": true,
            "reminder_message_template": "Hello {customer_name}, your current balance is {amount_due}.",
            "reminder_email_subject_template": "Payment reminder from {business_name}",
            "invoice_share_subject_template": "Invoice {invoice_number} from {business_name}",
            "invoice_share_message_template": "Hello {customer_name}, invoice {invoice_number} for {invoice_total} is ready."
        },
        "subscription": {
            "status": "active",
            "billing_interval": "monthly",
            "trial_ends_at": null,
            "renews_at": "2026-09-19T00:00:00.000000Z",
            "ends_at": null,
            "cancelled_at": null,
            "plan": {
                "id": 2,
                "name": "Smart",
                "slug": "smart",
                "features": [
                    "inventory",
                    "pos",
                    "expenses"
                ],
                "limits": []
            }
        }
    }
}
 

Request      

PATCH api/v1/businesses/{business}/settings

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

name   string     

Must not be greater than 128 characters. Example: b

legal_name   string  optional    

Must not be greater than 191 characters. Example: n

phone   string  optional    

Must not be greater than 32 characters. Example: g

email   string  optional    

Must be a valid email address. Must not be greater than 255 characters. Example: [email protected]

gst_registration_type   string     

Example: unregistered

Must be one of:
  • unregistered
  • regular
  • composition
gstin   string  optional    

Must match the regex /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/. Must be 15 characters. Example: dljnikhwaykcmyu

address_line_1   string  optional    

Must not be greater than 191 characters. Example: w

address_line_2   string  optional    

Must not be greater than 191 characters. Example: p

city   string  optional    

Must not be greater than 96 characters. Example: w

state_code   string  optional    

Example: 1

Must be one of:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 26
  • 27
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 97
  • 99
pincode   string  optional    

Must be 6 digits. Example: 569775

default_place_of_supply   string  optional    

Example: 1

Must be one of:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 26
  • 27
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 97
  • 99
invoice_prefix   string     

Invoice-series prefix, up to 15 characters. GST permits letters, numbers, hyphens, and slashes; the generated invoice number is limited to 16 characters. Example: HAWIOT/26-27/

prices_include_tax   boolean  optional    

Example: false

authorized_signatory   string  optional    

Must not be greater than 128 characters. Example: g

bank_details   string  optional    

Must not be greater than 1000 characters. Example: z

upi_id   string  optional    

Must match the regex /^[a-zA-Z0-9._-]{2,191}@[a-zA-Z0-9.-]{2,63}$/. Must not be greater than 255 characters. Example: m

theme   string  optional    

Example: blue

Must be one of:
  • blue
  • emerald
  • teal
  • violet
  • rose
  • maroon
  • graphite
default_locale   string  optional    

Example: en

Must be one of:
  • en
  • hi
  • ta
  • te
  • ml
  • kn
  • mr
  • gu
  • bn
reminder_message_template   string  optional    

Must not be greater than 2000 characters. Example: i

reminder_email_subject_template   string  optional    

Must not be greater than 191 characters. Example: y

invoice_share_subject_template   string  optional    

Must not be greater than 191 characters. Example: v

invoice_share_message_template   string  optional    

Must not be greater than 2000 characters. Example: d

GET api/v1/businesses/{business}/dashboard

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/dashboard" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/dashboard"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/dashboard';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/dashboard

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Business onboarding

Complete the resumable setup required after registering a new business. Until onboarding finishes, clients may resubmit a completed required step to correct saved data without moving the current step backwards.

GET api/v1/businesses/{business}/onboarding

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/onboarding" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/onboarding

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

PATCH api/v1/businesses/{business}/onboarding/language

requires authentication

Example request:
curl --request PATCH \
    "https://dukanam.com/api/v1/businesses/1/onboarding/language" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"locale\": \"hi\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding/language"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "locale": "hi"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding/language';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'locale' => 'hi',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH api/v1/businesses/{business}/onboarding/language

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

locale   string     

Supported locale code. Example: hi

POST api/v1/businesses/{business}/onboarding/business

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/onboarding/business" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "name=Veera Stores"\
    --form "store_type=kirana-store"\
    --form "supply_type=goods"\
    --form "phone=9876543210"\
    --form "logo=@/path/to/file" 
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding/business"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('name', 'Veera Stores');
body.append('store_type', 'kirana-store');
body.append('supply_type', 'goods');
body.append('phone', '9876543210');
body.append('logo', document.querySelector('input[name="logo"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding/business';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'name',
                'contents' => 'Veera Stores'
            ],
            [
                'name' => 'store_type',
                'contents' => 'kirana-store'
            ],
            [
                'name' => 'supply_type',
                'contents' => 'goods'
            ],
            [
                'name' => 'phone',
                'contents' => '9876543210'
            ],
            [
                'name' => 'logo',
                'contents' => fopen('/path/to/file', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/onboarding/business

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

name   string     

Public business name. Example: Veera Stores

store_type   string     

Store type key. Example: kirana-store

supply_type   string     

goods, services, or both. Example: goods

phone   string     

Public phone in E.164 or a 10-digit Indian local format. Example: 9876543210

logo   file  optional    

Optional PNG, JPG, or WebP logo up to 2 MB. Example: /path/to/file

Response

Response Fields

data   object     
logo_url   string|null     

Temporary signed object-storage URL when the logo is stored on S3. Refresh the resource after it expires.

PATCH api/v1/businesses/{business}/onboarding/tax

requires authentication

Example request:
curl --request PATCH \
    "https://dukanam.com/api/v1/businesses/1/onboarding/tax" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"gst_status\": \"registered\",
    \"gst_registration_type\": \"normal\",
    \"gstin\": \"29ABCDE1234F1Z5\",
    \"legal_name\": \"a\",
    \"address_line_1\": \"12 Market Road\",
    \"address_line_2\": \"k\",
    \"city\": \"Bengaluru\",
    \"state_code\": \"29\",
    \"pincode\": \"560001\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding/tax"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "gst_status": "registered",
    "gst_registration_type": "normal",
    "gstin": "29ABCDE1234F1Z5",
    "legal_name": "a",
    "address_line_1": "12 Market Road",
    "address_line_2": "k",
    "city": "Bengaluru",
    "state_code": "29",
    "pincode": "560001"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding/tax';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'gst_status' => 'registered',
            'gst_registration_type' => 'normal',
            'gstin' => '29ABCDE1234F1Z5',
            'legal_name' => 'a',
            'address_line_1' => '12 Market Road',
            'address_line_2' => 'k',
            'city' => 'Bengaluru',
            'state_code' => '29',
            'pincode' => '560001',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH api/v1/businesses/{business}/onboarding/tax

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

gst_status   string     

registered, not_registered, or not_sure. Example: registered

gst_registration_type   string  optional    

Required when registered: normal or composition. Example: normal

gstin   string  optional    

Required when registered. Example: 29ABCDE1234F1Z5

legal_name   string  optional    

Must not be greater than 191 characters. Example: a

address_line_1   string     

Example: 12 Market Road

address_line_2   string  optional    

Must not be greater than 191 characters. Example: k

city   string     

Example: Bengaluru

state_code   string     

Indian GST state code. Example: 29

pincode   string     

Six digit PIN code. Example: 560001

POST api/v1/businesses/{business}/onboarding/steps/{step}

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/onboarding/steps/architecto" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"skip\": true
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding/steps/architecto"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "skip": true
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding/steps/architecto';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'skip' => true,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/onboarding/steps/{step}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

step   string     

The step. Example: architecto

Body Parameters

skip   boolean  optional    

Set true to finish this optional step later. Example: true

POST api/v1/businesses/{business}/onboarding/product-imports

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/onboarding/product-imports" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "file=@/path/to/file" 
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding/product-imports"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('file', document.querySelector('input[name="file"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding/product-imports';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'file',
                'contents' => fopen('/path/to/file', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/onboarding/product-imports

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

file   file     

Completed Dukanam XLSX template, maximum 5 MB. Example: /path/to/file

Import every validated product row without exceeding the current plan's total item allowance.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/onboarding/product-imports/1/confirm" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/onboarding/product-imports/1/confirm"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/onboarding/product-imports/1/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (422):


{
    "message": "Your current plan includes up to 5 items. Upgrade to Smart Books to add more.",
    "errors": {
        "plan": [
            "Your current plan includes up to 5 items. Upgrade to Smart Books to add more."
        ]
    }
}
 

Request      

POST api/v1/businesses/{business}/onboarding/product-imports/{itemImport_id}/confirm

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

itemImport_id   integer     

The ID of the itemImport. Example: 1

Contacts

GET api/v1/businesses/{business}/contacts

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/contacts" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"customer\",
    \"search\": \"b\",
    \"per_page\": 22
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/contacts"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "customer",
    "search": "b",
    "per_page": 22
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/contacts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'customer',
            'search' => 'b',
            'per_page' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/contacts

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

type   string  optional    

Example: customer

Must be one of:
  • customer
  • supplier
  • both
search   string  optional    

Must not be greater than 128 characters. Example: b

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 22

Create a contact and post its opening balance.

requires authentication

Customer openings post to Accounts receivable; supplier openings post to Accounts payable, offset by Owner equity.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/contacts" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"customer\",
    \"profile_type\": \"consumer\",
    \"name\": \"b\",
    \"company_name\": \"n\",
    \"contact_person\": \"g\",
    \"phone\": \"z\",
    \"email\": \"[email protected]\",
    \"gst_treatment\": \"unregistered\",
    \"gstin\": \"ljnikhwaykcmyuw\",
    \"pan\": \"pwlvqwrsit\",
    \"address\": \"c\",
    \"billing_address_line_1\": \"p\",
    \"billing_address_line_2\": \"s\",
    \"billing_city\": \"c\",
    \"billing_state_code\": 1,
    \"billing_pincode\": \"569775\",
    \"shipping_same_as_billing\": false,
    \"shipping_address_line_1\": \"n\",
    \"shipping_address_line_2\": \"g\",
    \"shipping_city\": \"z\",
    \"shipping_state_code\": 1,
    \"shipping_pincode\": \"569775\",
    \"opening_balance\": 22,
    \"opening_balance_side\": \"receivable\",
    \"opening_balance_date\": \"2026-01-15\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/contacts"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "customer",
    "profile_type": "consumer",
    "name": "b",
    "company_name": "n",
    "contact_person": "g",
    "phone": "z",
    "email": "[email protected]",
    "gst_treatment": "unregistered",
    "gstin": "ljnikhwaykcmyuw",
    "pan": "pwlvqwrsit",
    "address": "c",
    "billing_address_line_1": "p",
    "billing_address_line_2": "s",
    "billing_city": "c",
    "billing_state_code": 1,
    "billing_pincode": "569775",
    "shipping_same_as_billing": false,
    "shipping_address_line_1": "n",
    "shipping_address_line_2": "g",
    "shipping_city": "z",
    "shipping_state_code": 1,
    "shipping_pincode": "569775",
    "opening_balance": 22,
    "opening_balance_side": "receivable",
    "opening_balance_date": "2026-01-15"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/contacts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'customer',
            'profile_type' => 'consumer',
            'name' => 'b',
            'company_name' => 'n',
            'contact_person' => 'g',
            'phone' => 'z',
            'email' => '[email protected]',
            'gst_treatment' => 'unregistered',
            'gstin' => 'ljnikhwaykcmyuw',
            'pan' => 'pwlvqwrsit',
            'address' => 'c',
            'billing_address_line_1' => 'p',
            'billing_address_line_2' => 's',
            'billing_city' => 'c',
            'billing_state_code' => 1,
            'billing_pincode' => '569775',
            'shipping_same_as_billing' => false,
            'shipping_address_line_1' => 'n',
            'shipping_address_line_2' => 'g',
            'shipping_city' => 'z',
            'shipping_state_code' => 1,
            'shipping_pincode' => '569775',
            'opening_balance' => 22,
            'opening_balance_side' => 'receivable',
            'opening_balance_date' => '2026-01-15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/contacts

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

type   string     

Example: customer

Must be one of:
  • customer
  • supplier
  • both
profile_type   string     

Example: consumer

Must be one of:
  • consumer
  • business
name   string     

Must not be greater than 128 characters. Example: b

company_name   string  optional    

Must not be greater than 191 characters. Example: n

contact_person   string  optional    

Must not be greater than 128 characters. Example: g

phone   string  optional    

Must not be greater than 32 characters. Example: z

email   string  optional    

Must be a valid email address. Must not be greater than 255 characters. Example: [email protected]

gst_treatment   string     

Example: unregistered

Must be one of:
  • unregistered
  • registered_regular
  • registered_composition
  • consumer
  • overseas
  • sez
gstin   string  optional    

Must match the regex /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/. Must be 15 characters. Example: ljnikhwaykcmyuw

pan   string  optional    

Must match the regex /^[A-Z]{5}[0-9]{4}[A-Z]$/. Must be 10 characters. Example: pwlvqwrsit

address   string  optional    

Must not be greater than 1000 characters. Example: c

billing_address_line_1   string  optional    

Must not be greater than 191 characters. Example: p

billing_address_line_2   string  optional    

Must not be greater than 191 characters. Example: s

billing_city   string  optional    

Must not be greater than 96 characters. Example: c

billing_state_code   string  optional    

Example: 1

Must be one of:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 26
  • 27
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 97
  • 99
billing_pincode   string  optional    

Must be 6 digits. Example: 569775

shipping_same_as_billing   boolean  optional    

Example: false

shipping_address_line_1   string  optional    

Must not be greater than 191 characters. Example: n

shipping_address_line_2   string  optional    

Must not be greater than 191 characters. Example: g

shipping_city   string  optional    

Must not be greater than 96 characters. Example: z

shipping_state_code   string  optional    

Example: 1

Must be one of:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 26
  • 27
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 97
  • 99
shipping_pincode   string  optional    

Must be 6 digits. Example: 569775

opening_balance   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 22

opening_balance_side   string     

Example: receivable

Must be one of:
  • receivable
  • payable
opening_balance_date   string  optional    

Must be a valid date. Example: 2026-01-15

GET api/v1/businesses/{business}/contacts/{id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/contacts/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/contacts/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/contacts/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/contacts/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

id   integer     

The ID of the contact. Example: 1

Update a contact and replace its opening-balance journal.

requires authentication

Example request:
curl --request PUT \
    "https://dukanam.com/api/v1/businesses/1/contacts/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"customer\",
    \"profile_type\": \"consumer\",
    \"name\": \"b\",
    \"company_name\": \"n\",
    \"contact_person\": \"g\",
    \"phone\": \"z\",
    \"email\": \"[email protected]\",
    \"gst_treatment\": \"unregistered\",
    \"gstin\": \"ljnikhwaykcmyuw\",
    \"pan\": \"pwlvqwrsit\",
    \"address\": \"c\",
    \"billing_address_line_1\": \"p\",
    \"billing_address_line_2\": \"s\",
    \"billing_city\": \"c\",
    \"billing_state_code\": 1,
    \"billing_pincode\": \"569775\",
    \"shipping_same_as_billing\": false,
    \"shipping_address_line_1\": \"n\",
    \"shipping_address_line_2\": \"g\",
    \"shipping_city\": \"z\",
    \"shipping_state_code\": 1,
    \"shipping_pincode\": \"569775\",
    \"opening_balance\": 22,
    \"opening_balance_side\": \"receivable\",
    \"opening_balance_date\": \"2026-01-15\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/contacts/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "customer",
    "profile_type": "consumer",
    "name": "b",
    "company_name": "n",
    "contact_person": "g",
    "phone": "z",
    "email": "[email protected]",
    "gst_treatment": "unregistered",
    "gstin": "ljnikhwaykcmyuw",
    "pan": "pwlvqwrsit",
    "address": "c",
    "billing_address_line_1": "p",
    "billing_address_line_2": "s",
    "billing_city": "c",
    "billing_state_code": 1,
    "billing_pincode": "569775",
    "shipping_same_as_billing": false,
    "shipping_address_line_1": "n",
    "shipping_address_line_2": "g",
    "shipping_city": "z",
    "shipping_state_code": 1,
    "shipping_pincode": "569775",
    "opening_balance": 22,
    "opening_balance_side": "receivable",
    "opening_balance_date": "2026-01-15"
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/contacts/1';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'customer',
            'profile_type' => 'consumer',
            'name' => 'b',
            'company_name' => 'n',
            'contact_person' => 'g',
            'phone' => 'z',
            'email' => '[email protected]',
            'gst_treatment' => 'unregistered',
            'gstin' => 'ljnikhwaykcmyuw',
            'pan' => 'pwlvqwrsit',
            'address' => 'c',
            'billing_address_line_1' => 'p',
            'billing_address_line_2' => 's',
            'billing_city' => 'c',
            'billing_state_code' => 1,
            'billing_pincode' => '569775',
            'shipping_same_as_billing' => false,
            'shipping_address_line_1' => 'n',
            'shipping_address_line_2' => 'g',
            'shipping_city' => 'z',
            'shipping_state_code' => 1,
            'shipping_pincode' => '569775',
            'opening_balance' => 22,
            'opening_balance_side' => 'receivable',
            'opening_balance_date' => '2026-01-15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT api/v1/businesses/{business}/contacts/{id}

PATCH api/v1/businesses/{business}/contacts/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

id   integer     

The ID of the contact. Example: 1

Body Parameters

type   string     

Example: customer

Must be one of:
  • customer
  • supplier
  • both
profile_type   string     

Example: consumer

Must be one of:
  • consumer
  • business
name   string     

Must not be greater than 128 characters. Example: b

company_name   string  optional    

Must not be greater than 191 characters. Example: n

contact_person   string  optional    

Must not be greater than 128 characters. Example: g

phone   string  optional    

Must not be greater than 32 characters. Example: z

email   string  optional    

Must be a valid email address. Must not be greater than 255 characters. Example: [email protected]

gst_treatment   string     

Example: unregistered

Must be one of:
  • unregistered
  • registered_regular
  • registered_composition
  • consumer
  • overseas
  • sez
gstin   string  optional    

Must match the regex /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/. Must be 15 characters. Example: ljnikhwaykcmyuw

pan   string  optional    

Must match the regex /^[A-Z]{5}[0-9]{4}[A-Z]$/. Must be 10 characters. Example: pwlvqwrsit

address   string  optional    

Must not be greater than 1000 characters. Example: c

billing_address_line_1   string  optional    

Must not be greater than 191 characters. Example: p

billing_address_line_2   string  optional    

Must not be greater than 191 characters. Example: s

billing_city   string  optional    

Must not be greater than 96 characters. Example: c

billing_state_code   string  optional    

Example: 1

Must be one of:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 26
  • 27
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 97
  • 99
billing_pincode   string  optional    

Must be 6 digits. Example: 569775

shipping_same_as_billing   boolean  optional    

Example: false

shipping_address_line_1   string  optional    

Must not be greater than 191 characters. Example: n

shipping_address_line_2   string  optional    

Must not be greater than 191 characters. Example: g

shipping_city   string  optional    

Must not be greater than 96 characters. Example: z

shipping_state_code   string  optional    

Example: 1

Must be one of:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 26
  • 27
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 97
  • 99
shipping_pincode   string  optional    

Must be 6 digits. Example: 569775

opening_balance   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 22

opening_balance_side   string     

Example: receivable

Must be one of:
  • receivable
  • payable
opening_balance_date   string  optional    

Must be a valid date. Example: 2026-01-15

DELETE api/v1/businesses/{business}/contacts/{id}

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/businesses/1/contacts/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/contacts/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/contacts/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/v1/businesses/{business}/contacts/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

id   integer     

The ID of the contact. Example: 1

Khata ledger

GET api/v1/businesses/{business}/ledger-entries

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/ledger-entries" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"contact_id\": 16,
    \"kind\": \"n\",
    \"from\": \"2026-01-15\",
    \"to\": \"2026-01-15\",
    \"per_page\": 22
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/ledger-entries"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "contact_id": 16,
    "kind": "n",
    "from": "2026-01-15",
    "to": "2026-01-15",
    "per_page": 22
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/ledger-entries';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'contact_id' => 16,
            'kind' => 'n',
            'from' => '2026-01-15',
            'to' => '2026-01-15',
            'per_page' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/ledger-entries

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

contact_id   integer  optional    

Example: 16

kind   string  optional    

Must not be greater than 32 characters. Example: n

from   string  optional    

Must be a valid date. Example: 2026-01-15

to   string  optional    

Must be a valid date. Must be a date after or equal to from. Example: 2026-01-15

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 22

Post a manual khata adjustment.

requires authentication

The receivable or payable adjustment is offset to Owner equity. Use invoice, purchase, expense, and payment endpoints for operational transactions.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/ledger-entries" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"contact_id\": 16,
    \"kind\": \"customer_credit\",
    \"amount\": 22,
    \"occurred_on\": \"2026-01-15\",
    \"reference\": \"g\",
    \"note\": \"z\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/ledger-entries"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "contact_id": 16,
    "kind": "customer_credit",
    "amount": 22,
    "occurred_on": "2026-01-15",
    "reference": "g",
    "note": "z"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/ledger-entries';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'contact_id' => 16,
            'kind' => 'customer_credit',
            'amount' => 22,
            'occurred_on' => '2026-01-15',
            'reference' => 'g',
            'note' => 'z',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/ledger-entries

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

contact_id   integer     

Example: 16

kind   string     

Example: customer_credit

Must be one of:
  • customer_credit
  • customer_payment
  • supplier_credit
  • supplier_payment
amount   number     

Must not be greater than 999999999. Example: 22

occurred_on   string     

Must be a valid date. Example: 2026-01-15

reference   string  optional    

Must not be greater than 64 characters. Example: g

note   string  optional    

Must not be greater than 1000 characters. Example: z

GET api/v1/businesses/{business}/ledger-entries/{ledgerEntry_id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/ledger-entries/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/ledger-entries/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/ledger-entries/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/ledger-entries/{ledgerEntry_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

ledgerEntry_id   integer     

The ID of the ledgerEntry. Example: 1

Reverse a manual khata adjustment and its journal.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/ledger-entries/1/reverse" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"b\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/ledger-entries/1/reverse"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "b"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/ledger-entries/1/reverse';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/ledger-entries/{ledgerEntry_id}/reverse

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

ledgerEntry_id   integer     

The ID of the ledgerEntry. Example: 1

Body Parameters

reason   string     

Must be at least 3 characters. Must not be greater than 255 characters. Example: b

Inventory

GET api/v1/businesses/{business}/items

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/items" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"search\": \"b\",
    \"low_stock\": false,
    \"per_page\": 22
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/items"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "search": "b",
    "low_stock": false,
    "per_page": 22
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/items';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'search' => 'b',
            'low_stock' => false,
            'per_page' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/items

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

search   string  optional    

Must not be greater than 128 characters. Example: b

low_stock   boolean  optional    

Example: false

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 22

Response

Response Fields

data   object     
photos   object     
url   string     

Temporary signed object-storage URL when the photo is stored on S3. Refresh the item after it expires.

meta   object     
item_allowance   object     
used   integer     

Number of catalogue items currently stored.

limit   integer|null     

Maximum item count; null means unlimited.

remaining   integer|null     

Item slots still available; null means unlimited.

can_create   boolean     

Whether another item can be created on the current plan.

upgrade_plan   object|null     

Recommended plan name and slug when the allowance is limited.

name   string     

Recommended upgrade plan display name.

slug   string     

Recommended upgrade plan slug for billing selection.

Create an item and post opening inventory.

requires authentication

Tracked opening stock creates a balanced Inventory / Owner equity journal at purchase cost. Stock and reorder quantities accept up to three decimal places.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/items" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "name=b"\
    --form "sku=n"\
    --form "barcode=g"\
    --form "item_type=goods"\
    --form "hsn_sac=zmiyvdljnikhwayk"\
    --form "unit=cmyuwpwlvqwrsitc"\
    --form "uqc=pscqldzsnrwtujwv"\
    --form "gst_taxability=taxable"\
    --form "sale_price=24"\
    --form "purchase_price=9"\
    --form "mrp=15"\
    --form "tax_rate=21"\
    --form "cess_rate=7"\
    --form "stock_quantity=8"\
    --form "reorder_level=10"\
    --form "track_inventory="\
    --form "price_includes_tax="\
    --form "remove_photos[]=16"\
    --form "photos[]=@/path/to/file" 
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/items"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('name', 'b');
body.append('sku', 'n');
body.append('barcode', 'g');
body.append('item_type', 'goods');
body.append('hsn_sac', 'zmiyvdljnikhwayk');
body.append('unit', 'cmyuwpwlvqwrsitc');
body.append('uqc', 'pscqldzsnrwtujwv');
body.append('gst_taxability', 'taxable');
body.append('sale_price', '24');
body.append('purchase_price', '9');
body.append('mrp', '15');
body.append('tax_rate', '21');
body.append('cess_rate', '7');
body.append('stock_quantity', '8');
body.append('reorder_level', '10');
body.append('track_inventory', '');
body.append('price_includes_tax', '');
body.append('remove_photos[]', '16');
body.append('photos[]', document.querySelector('input[name="photos[]"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/items';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'name',
                'contents' => 'b'
            ],
            [
                'name' => 'sku',
                'contents' => 'n'
            ],
            [
                'name' => 'barcode',
                'contents' => 'g'
            ],
            [
                'name' => 'item_type',
                'contents' => 'goods'
            ],
            [
                'name' => 'hsn_sac',
                'contents' => 'zmiyvdljnikhwayk'
            ],
            [
                'name' => 'unit',
                'contents' => 'cmyuwpwlvqwrsitc'
            ],
            [
                'name' => 'uqc',
                'contents' => 'pscqldzsnrwtujwv'
            ],
            [
                'name' => 'gst_taxability',
                'contents' => 'taxable'
            ],
            [
                'name' => 'sale_price',
                'contents' => '24'
            ],
            [
                'name' => 'purchase_price',
                'contents' => '9'
            ],
            [
                'name' => 'mrp',
                'contents' => '15'
            ],
            [
                'name' => 'tax_rate',
                'contents' => '21'
            ],
            [
                'name' => 'cess_rate',
                'contents' => '7'
            ],
            [
                'name' => 'stock_quantity',
                'contents' => '8'
            ],
            [
                'name' => 'reorder_level',
                'contents' => '10'
            ],
            [
                'name' => 'track_inventory',
                'contents' => ''
            ],
            [
                'name' => 'price_includes_tax',
                'contents' => ''
            ],
            [
                'name' => 'remove_photos[]',
                'contents' => '16'
            ],
            [
                'name' => 'photos[]',
                'contents' => fopen('/path/to/file', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (422):


{
    "message": "Your current plan includes up to 5 items. Upgrade to Smart Books to add more.",
    "errors": {
        "plan": [
            "Your current plan includes up to 5 items. Upgrade to Smart Books to add more."
        ]
    }
}
 

Request      

POST api/v1/businesses/{business}/items

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

name   string     

Must not be greater than 128 characters. Example: b

sku   string  optional    

Must not be greater than 64 characters. Example: n

barcode   string  optional    

Must not be greater than 64 characters. Example: g

item_type   string     

Example: goods

Must be one of:
  • goods
  • service
hsn_sac   string  optional    

Must match the regex /^[0-9A-Z.-]+$/. Must not be greater than 16 characters. Example: zmiyvdljnikhwayk

unit   string     

Must not be greater than 24 characters. Example: cmyuwpwlvqwrsitc

uqc   string     

Must not be greater than 16 characters. Example: pscqldzsnrwtujwv

gst_taxability   string     

Example: taxable

Must be one of:
  • taxable
  • nil_rated
  • exempt
  • non_gst
sale_price   number     

Must be at least 0. Must not be greater than 999999999. Example: 24

purchase_price   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 9

mrp   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 15

tax_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 21

cess_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 7

stock_quantity   number     

Must be at least 0. Must not be greater than 999999999. Example: 8

reorder_level   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 10

track_inventory   boolean  optional    

Example: false

price_includes_tax   boolean  optional    

Example: false

photos   file[]  optional    

Must be an image. Must not be greater than 5120 kilobytes.

remove_photos   integer[]  optional    

GET api/v1/businesses/{business}/items/{id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/items/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/items/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/items/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/items/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

id   integer     

The ID of the item. Example: 1

Response

Response Fields

data   object     
photos   object     
url   string     

Temporary signed object-storage URL when the photo is stored on S3. Refresh the item after it expires.

Update an item and account for stock corrections.

requires authentication

Quantity increases post inventory-adjustment income; decreases post an operating expense. Stock and reorder quantities accept up to three decimal places.

Example request:
curl --request PUT \
    "https://dukanam.com/api/v1/businesses/1/items/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "name=b"\
    --form "sku=n"\
    --form "barcode=g"\
    --form "item_type=goods"\
    --form "hsn_sac=zmiyvdljnikhwayk"\
    --form "unit=cmyuwpwlvqwrsitc"\
    --form "uqc=pscqldzsnrwtujwv"\
    --form "gst_taxability=taxable"\
    --form "sale_price=24"\
    --form "purchase_price=9"\
    --form "mrp=15"\
    --form "tax_rate=21"\
    --form "cess_rate=7"\
    --form "stock_quantity=8"\
    --form "reorder_level=10"\
    --form "track_inventory="\
    --form "price_includes_tax="\
    --form "remove_photos[]=16"\
    --form "photos[]=@/path/to/file" 
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/items/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('name', 'b');
body.append('sku', 'n');
body.append('barcode', 'g');
body.append('item_type', 'goods');
body.append('hsn_sac', 'zmiyvdljnikhwayk');
body.append('unit', 'cmyuwpwlvqwrsitc');
body.append('uqc', 'pscqldzsnrwtujwv');
body.append('gst_taxability', 'taxable');
body.append('sale_price', '24');
body.append('purchase_price', '9');
body.append('mrp', '15');
body.append('tax_rate', '21');
body.append('cess_rate', '7');
body.append('stock_quantity', '8');
body.append('reorder_level', '10');
body.append('track_inventory', '');
body.append('price_includes_tax', '');
body.append('remove_photos[]', '16');
body.append('photos[]', document.querySelector('input[name="photos[]"]').files[0]);

fetch(url, {
    method: "PUT",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/items/1';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'name',
                'contents' => 'b'
            ],
            [
                'name' => 'sku',
                'contents' => 'n'
            ],
            [
                'name' => 'barcode',
                'contents' => 'g'
            ],
            [
                'name' => 'item_type',
                'contents' => 'goods'
            ],
            [
                'name' => 'hsn_sac',
                'contents' => 'zmiyvdljnikhwayk'
            ],
            [
                'name' => 'unit',
                'contents' => 'cmyuwpwlvqwrsitc'
            ],
            [
                'name' => 'uqc',
                'contents' => 'pscqldzsnrwtujwv'
            ],
            [
                'name' => 'gst_taxability',
                'contents' => 'taxable'
            ],
            [
                'name' => 'sale_price',
                'contents' => '24'
            ],
            [
                'name' => 'purchase_price',
                'contents' => '9'
            ],
            [
                'name' => 'mrp',
                'contents' => '15'
            ],
            [
                'name' => 'tax_rate',
                'contents' => '21'
            ],
            [
                'name' => 'cess_rate',
                'contents' => '7'
            ],
            [
                'name' => 'stock_quantity',
                'contents' => '8'
            ],
            [
                'name' => 'reorder_level',
                'contents' => '10'
            ],
            [
                'name' => 'track_inventory',
                'contents' => ''
            ],
            [
                'name' => 'price_includes_tax',
                'contents' => ''
            ],
            [
                'name' => 'remove_photos[]',
                'contents' => '16'
            ],
            [
                'name' => 'photos[]',
                'contents' => fopen('/path/to/file', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PUT api/v1/businesses/{business}/items/{id}

PATCH api/v1/businesses/{business}/items/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

id   integer     

The ID of the item. Example: 1

Body Parameters

name   string     

Must not be greater than 128 characters. Example: b

sku   string  optional    

Must not be greater than 64 characters. Example: n

barcode   string  optional    

Must not be greater than 64 characters. Example: g

item_type   string     

Example: goods

Must be one of:
  • goods
  • service
hsn_sac   string  optional    

Must match the regex /^[0-9A-Z.-]+$/. Must not be greater than 16 characters. Example: zmiyvdljnikhwayk

unit   string     

Must not be greater than 24 characters. Example: cmyuwpwlvqwrsitc

uqc   string     

Must not be greater than 16 characters. Example: pscqldzsnrwtujwv

gst_taxability   string     

Example: taxable

Must be one of:
  • taxable
  • nil_rated
  • exempt
  • non_gst
sale_price   number     

Must be at least 0. Must not be greater than 999999999. Example: 24

purchase_price   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 9

mrp   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 15

tax_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 21

cess_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 7

stock_quantity   number     

Must be at least 0. Must not be greater than 999999999. Example: 8

reorder_level   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 10

track_inventory   boolean  optional    

Example: false

price_includes_tax   boolean  optional    

Example: false

photos   file[]  optional    

Must be an image. Must not be greater than 5120 kilobytes.

remove_photos   integer[]  optional    

DELETE api/v1/businesses/{business}/items/{id}

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/businesses/1/items/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/items/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/items/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

DELETE api/v1/businesses/{business}/items/{id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

id   integer     

The ID of the item. Example: 1

Sales invoices

GET api/v1/businesses/{business}/invoices

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/invoices" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"posted\",
    \"contact_id\": 16,
    \"from\": \"2026-01-15\",
    \"to\": \"2026-01-15\",
    \"search\": \"n\",
    \"per_page\": 7
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/invoices"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "posted",
    "contact_id": 16,
    "from": "2026-01-15",
    "to": "2026-01-15",
    "search": "n",
    "per_page": 7
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/invoices';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'posted',
            'contact_id' => 16,
            'from' => '2026-01-15',
            'to' => '2026-01-15',
            'search' => 'n',
            'per_page' => 7,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/invoices

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

status   string  optional    

Example: posted

Must be one of:
  • posted
  • partially_paid
  • paid
  • void
  • partially_returned
  • returned
contact_id   integer  optional    

Example: 16

from   string  optional    

Must be a valid date. Example: 2026-01-15

to   string  optional    

Must be a valid date. Must be a date after or equal to from. Example: 2026-01-15

search   string  optional    

Must not be greater than 128 characters. Example: n

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 7

Create a sales invoice.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/invoices" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"contact_id\": 16,
    \"issue_date\": \"2026-01-15\",
    \"due_date\": \"2026-01-15\",
    \"notes\": \"n\",
    \"place_of_supply_state_code\": \"gz\",
    \"reverse_charge\": false,
    \"prices_include_tax\": false,
    \"channel\": \"backoffice\",
    \"idempotency_key\": \"977e5426-8d13-3824-86aa-b092f8ae52c5\",
    \"lines\": [
        {
            \"item_id\": 16,
            \"description\": \"Et animi quos velit et fugiat.\",
            \"quantity\": 1,
            \"unit_price\": 5,
            \"tax_rate\": 19,
            \"cess_rate\": 17,
            \"discount\": 5,
            \"price_includes_tax\": false
        }
    ]
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/invoices"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "contact_id": 16,
    "issue_date": "2026-01-15",
    "due_date": "2026-01-15",
    "notes": "n",
    "place_of_supply_state_code": "gz",
    "reverse_charge": false,
    "prices_include_tax": false,
    "channel": "backoffice",
    "idempotency_key": "977e5426-8d13-3824-86aa-b092f8ae52c5",
    "lines": [
        {
            "item_id": 16,
            "description": "Et animi quos velit et fugiat.",
            "quantity": 1,
            "unit_price": 5,
            "tax_rate": 19,
            "cess_rate": 17,
            "discount": 5,
            "price_includes_tax": false
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/invoices';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'contact_id' => 16,
            'issue_date' => '2026-01-15',
            'due_date' => '2026-01-15',
            'notes' => 'n',
            'place_of_supply_state_code' => 'gz',
            'reverse_charge' => false,
            'prices_include_tax' => false,
            'channel' => 'backoffice',
            'idempotency_key' => '977e5426-8d13-3824-86aa-b092f8ae52c5',
            'lines' => [
                ['item_id' => 16, 'description' => 'Et animi quos velit et fugiat.', 'quantity' => 1, 'unit_price' => 5, 'tax_rate' => 19, 'cess_rate' => 17, 'discount' => 5, 'price_includes_tax' => false],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/invoices

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

contact_id   integer     

Example: 16

issue_date   string     

Must be a valid date. Example: 2026-01-15

due_date   string  optional    

Must be a valid date. Must be a date after or equal to issue_date. Example: 2026-01-15

notes   string  optional    

Must not be greater than 2000 characters. Example: n

place_of_supply_state_code   string  optional    

Must be 2 characters. Example: gz

reverse_charge   boolean  optional    

Example: false

prices_include_tax   boolean  optional    

Example: false

channel   string  optional    

Example: backoffice

Must be one of:
  • backoffice
  • pos
idempotency_key   string  optional    

Must be a valid UUID. Example: 977e5426-8d13-3824-86aa-b092f8ae52c5

lines   object[]     

Must have at least 1 items. Must not have more than 50 items.

item_id   integer  optional    

Example: 16

description   string     

Must not be greater than 255 characters. Example: Et animi quos velit et fugiat.

quantity   number     

Must not be greater than 999999. Example: 1

unit_price   number     

Must be at least 0. Must not be greater than 999999999. Example: 5

tax_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 19

cess_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 17

discount   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 5

price_includes_tax   boolean  optional    

Example: false

Response

Response Fields

document_kind   string     

Tax document type. Enum: invoice, tax_invoice, bill_of_supply.

GET api/v1/businesses/{business}/invoices/{invoice_id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/invoices/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/invoices/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/invoices/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/invoices/{invoice_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

invoice_id   integer     

The ID of the invoice. Example: 1

POST api/v1/businesses/{business}/invoices/{invoice_id}/payments

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/invoices/1/payments" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"amount\": 1,
    \"method\": \"cash\",
    \"payment_account_id\": 16,
    \"paid_on\": \"2026-01-15\",
    \"reference\": \"n\",
    \"notes\": \"g\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/invoices/1/payments"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "amount": 1,
    "method": "cash",
    "payment_account_id": 16,
    "paid_on": "2026-01-15",
    "reference": "n",
    "notes": "g"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/invoices/1/payments';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'amount' => 1,
            'method' => 'cash',
            'payment_account_id' => 16,
            'paid_on' => '2026-01-15',
            'reference' => 'n',
            'notes' => 'g',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/invoices/{invoice_id}/payments

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

invoice_id   integer     

The ID of the invoice. Example: 1

Body Parameters

amount   number     

Must not be greater than 999999999. Example: 1

method   string     

Example: cash

Must be one of:
  • cash
  • bank
  • upi
  • card
  • cheque
  • other
payment_account_id   integer     

Example: 16

paid_on   string  optional    

Must be a valid date. Example: 2026-01-15

reference   string  optional    

Must not be greater than 64 characters. Example: n

notes   string  optional    

Must not be greater than 1000 characters. Example: g

Void an unpaid invoice that has no returns.

requires authentication

Invoices with a payment or a sales return must be settled through the corresponding payment or return workflow and cannot be voided.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/invoices/1/void" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"b\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/invoices/1/void"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "b"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/invoices/1/void';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (422):


{
    "message": "Only unpaid, active invoices without returns can be voided.",
    "errors": {
        "invoice": [
            "Only unpaid, active invoices without returns can be voided."
        ]
    }
}
 

Request      

POST api/v1/businesses/{business}/invoices/{invoice_id}/void

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

invoice_id   integer     

The ID of the invoice. Example: 1

Body Parameters

reason   string     

Must be at least 3 characters. Must not be greater than 255 characters. Example: b

Business documents

GET api/v1/businesses/{business}/documents

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/documents" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"quote\",
    \"status\": \"b\",
    \"contact_id\": 16,
    \"per_page\": 22
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/documents"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "quote",
    "status": "b",
    "contact_id": 16,
    "per_page": 22
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/documents';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'quote',
            'status' => 'b',
            'contact_id' => 16,
            'per_page' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/documents

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

type   string     

Example: quote

Must be one of:
  • quote
  • purchase_order
  • purchase_invoice
  • recurring_invoice
  • sales_return
  • purchase_return
status   string  optional    

Must not be greater than 32 characters. Example: b

contact_id   integer  optional    

Example: 16

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 22

POST api/v1/businesses/{business}/documents/{document_type}

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/documents/quote|purchase_order|purchase_invoice|recurring_invoice|sales_return|purchase_return" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"contact_id\": 16,
    \"source_invoice_id\": 16,
    \"source_document_id\": 16,
    \"issue_date\": \"2026-01-15\",
    \"due_date\": \"2026-01-15\",
    \"frequency\": \"weekly\",
    \"next_issue_date\": \"2026-01-15\",
    \"end_date\": \"2026-01-15\",
    \"notes\": \"n\",
    \"external_reference\": \"g\",
    \"place_of_supply_state_code\": \"zm\",
    \"reverse_charge\": false,
    \"prices_include_tax\": false,
    \"lines\": [
        {
            \"item_id\": 16,
            \"source_invoice_line_id\": 16,
            \"source_document_line_id\": 16,
            \"description\": \"Et animi quos velit et fugiat.\",
            \"quantity\": 1,
            \"unit_price\": 5,
            \"tax_rate\": 19,
            \"cess_rate\": 17,
            \"discount\": 5,
            \"price_includes_tax\": false
        }
    ]
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/documents/quote|purchase_order|purchase_invoice|recurring_invoice|sales_return|purchase_return"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "contact_id": 16,
    "source_invoice_id": 16,
    "source_document_id": 16,
    "issue_date": "2026-01-15",
    "due_date": "2026-01-15",
    "frequency": "weekly",
    "next_issue_date": "2026-01-15",
    "end_date": "2026-01-15",
    "notes": "n",
    "external_reference": "g",
    "place_of_supply_state_code": "zm",
    "reverse_charge": false,
    "prices_include_tax": false,
    "lines": [
        {
            "item_id": 16,
            "source_invoice_line_id": 16,
            "source_document_line_id": 16,
            "description": "Et animi quos velit et fugiat.",
            "quantity": 1,
            "unit_price": 5,
            "tax_rate": 19,
            "cess_rate": 17,
            "discount": 5,
            "price_includes_tax": false
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/documents/quote|purchase_order|purchase_invoice|recurring_invoice|sales_return|purchase_return';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'contact_id' => 16,
            'source_invoice_id' => 16,
            'source_document_id' => 16,
            'issue_date' => '2026-01-15',
            'due_date' => '2026-01-15',
            'frequency' => 'weekly',
            'next_issue_date' => '2026-01-15',
            'end_date' => '2026-01-15',
            'notes' => 'n',
            'external_reference' => 'g',
            'place_of_supply_state_code' => 'zm',
            'reverse_charge' => false,
            'prices_include_tax' => false,
            'lines' => [
                ['item_id' => 16, 'source_invoice_line_id' => 16, 'source_document_line_id' => 16, 'description' => 'Et animi quos velit et fugiat.', 'quantity' => 1, 'unit_price' => 5, 'tax_rate' => 19, 'cess_rate' => 17, 'discount' => 5, 'price_includes_tax' => false],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/documents/{document_type}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

document_type   string     

Example: quote|purchase_order|purchase_invoice|recurring_invoice|sales_return|purchase_return

Body Parameters

contact_id   integer     

Example: 16

source_invoice_id   integer  optional    

Example: 16

source_document_id   integer  optional    

Example: 16

issue_date   string     

Must be a valid date. Example: 2026-01-15

due_date   string  optional    

Must be a valid date. Must be a date after or equal to issue_date. Example: 2026-01-15

frequency   string  optional    

Example: weekly

Must be one of:
  • weekly
  • monthly
  • quarterly
  • yearly
next_issue_date   string  optional    

Must be a valid date. Example: 2026-01-15

end_date   string  optional    

Must be a valid date. Must be a date after or equal to next_issue_date. Example: 2026-01-15

notes   string  optional    

Must not be greater than 2000 characters. Example: n

external_reference   string  optional    

Must not be greater than 64 characters. Example: g

place_of_supply_state_code   string  optional    

Must be 2 characters. Example: zm

reverse_charge   boolean  optional    

Example: false

prices_include_tax   boolean  optional    

Example: false

lines   object[]     

Must have at least 1 items. Must not have more than 100 items.

item_id   integer  optional    

Example: 16

source_invoice_line_id   integer  optional    

Example: 16

source_document_line_id   integer  optional    

Example: 16

description   string     

Must not be greater than 255 characters. Example: Et animi quos velit et fugiat.

quantity   number     

Must not be greater than 999999. Example: 1

unit_price   number     

Must be at least 0. Must not be greater than 999999999. Example: 5

tax_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 19

cess_rate   number  optional    

Must be at least 0. Must not be greater than 100. Example: 17

discount   number  optional    

Must be at least 0. Must not be greater than 999999999. Example: 5

price_includes_tax   boolean  optional    

Example: false

GET api/v1/businesses/{business}/documents/{document_id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/documents/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/documents/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/documents/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/documents/{document_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

document_id   integer     

The ID of the document. Example: 1

POST api/v1/businesses/{business}/documents/{document_id}/convert

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/documents/1/convert" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/documents/1/convert"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/documents/1/convert';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/documents/{document_id}/convert

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

document_id   integer     

The ID of the document. Example: 1

Payments

GET api/v1/businesses/{business}/payments

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/payments" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"direction\": \"received\",
    \"from\": \"2026-01-15\",
    \"to\": \"2026-01-15\",
    \"per_page\": 22
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payments"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "direction": "received",
    "from": "2026-01-15",
    "to": "2026-01-15",
    "per_page": 22
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payments';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'direction' => 'received',
            'from' => '2026-01-15',
            'to' => '2026-01-15',
            'per_page' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/payments

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

direction   string     

Example: received

Must be one of:
  • received
  • made
from   string  optional    

Must be a valid date. Example: 2026-01-15

to   string  optional    

Must be a valid date. Must be a date after or equal to from. Example: 2026-01-15

per_page   integer  optional    

Must be at least 1. Must not be greater than 100. Example: 22

POST api/v1/businesses/{business}/payments

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/payments" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"invoice_id\": 16,
    \"business_document_id\": 16,
    \"amount\": 22,
    \"paid_on\": \"2026-01-15\",
    \"method\": \"cash\",
    \"payment_account_id\": 16,
    \"reference\": \"n\",
    \"notes\": \"g\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payments"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "invoice_id": 16,
    "business_document_id": 16,
    "amount": 22,
    "paid_on": "2026-01-15",
    "method": "cash",
    "payment_account_id": 16,
    "reference": "n",
    "notes": "g"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payments';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'invoice_id' => 16,
            'business_document_id' => 16,
            'amount' => 22,
            'paid_on' => '2026-01-15',
            'method' => 'cash',
            'payment_account_id' => 16,
            'reference' => 'n',
            'notes' => 'g',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/payments

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

invoice_id   integer  optional    

Example: 16

business_document_id   integer  optional    

Example: 16

amount   number     

Must not be greater than 999999999. Example: 22

paid_on   string     

Must be a valid date. Example: 2026-01-15

method   string     

Example: cash

Must be one of:
  • cash
  • bank
  • upi
  • card
  • cheque
  • other
payment_account_id   integer  optional    

Example: 16

reference   string  optional    

Must not be greater than 64 characters. Example: n

notes   string  optional    

Must not be greater than 1000 characters. Example: g

Expenses

Expense records are a core Purchases capability on every plan. Workspace purchase permissions still apply.

List expenses.

requires authentication

Search and filter business expenses. The response includes totals for the current result, the current month, and the retained voided audit trail.

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/expenses?q=internet&category=Utilities&payment_account_id=9&from=2026-08-01&to=2026-08-31&status=active&per_page=20" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/expenses"
);

const params = {
    "q": "internet",
    "category": "Utilities",
    "payment_account_id": "9",
    "from": "2026-08-01",
    "to": "2026-08-31",
    "status": "active",
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/expenses';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'q' => 'internet',
            'category' => 'Utilities',
            'payment_account_id' => '9',
            'from' => '2026-08-01',
            'to' => '2026-08-31',
            'status' => 'active',
            'per_page' => '20',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "id": 41,
            "category": "Utilities",
            "payee": "City Internet Services",
            "amount_paise": 249900,
            "occurred_on": "2026-08-12",
            "payment_method": "bank",
            "payment_account_id": 9,
            "payment_account_name": "HDFC Current Account",
            "note": "August internet bill for the main shop",
            "voided_at": null,
            "void_reason": null,
            "created_at": "2026-08-12T10:30:00.000000Z"
        }
    ],
    "links": {
        "first": "https://example.com/api/v1/businesses/1/expenses?page=1",
        "last": "https://example.com/api/v1/businesses/1/expenses?page=1",
        "prev": null,
        "next": null
    },
    "meta": {
        "current_page": 1,
        "from": 1,
        "last_page": 1,
        "path": "https://example.com/api/v1/businesses/1/expenses",
        "per_page": 20,
        "to": 1,
        "total": 1
    },
    "summary": {
        "filtered_total_paise": 249900,
        "month_total_paise": 384900,
        "month_count": 3,
        "voided_count": 1
    }
}
 

Request      

GET api/v1/businesses/{business}/expenses

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Query Parameters

q   string  optional    

Search category, payee, or description. Example: internet

category   string  optional    

Filter by an exact category. Example: Utilities

payment_account_id   integer  optional    

Filter by the payment account used. Example: 9

from   string  optional    

Include expenses on or after this date (YYYY-MM-DD). Example: 2026-08-01

to   string  optional    

Include expenses on or before this date (YYYY-MM-DD). Example: 2026-08-31

status   string  optional    

Filter by posting status. Example: active

Must be one of:
  • active
  • voided
per_page   integer  optional    

Results per page, from 1 to 100. Example: 20

Response

Response Fields

summary   object     
filtered_total_paise   integer     

Total of non-voided expenses matching the current filters.

month_total_paise   integer     

Total of all non-voided expenses in the current calendar month.

month_count   integer     

Count of all non-voided expenses in the current calendar month.

voided_count   integer     

Count of all retained voided expenses.

Post an expense atomically.

requires authentication

The expense, general-ledger journal, cash-drawer movement, and audit record either all commit or all roll back.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/expenses" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"category\": \"b\",
    \"payee\": \"n\",
    \"amount\": 7,
    \"occurred_on\": \"2026-01-15\",
    \"payment_method\": \"cash\",
    \"payment_account_id\": 16,
    \"note\": \"n\",
    \"idempotency_key\": \"6d61f406-f07d-482d-a284-3e06edfd7f55\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/expenses"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "category": "b",
    "payee": "n",
    "amount": 7,
    "occurred_on": "2026-01-15",
    "payment_method": "cash",
    "payment_account_id": 16,
    "note": "n",
    "idempotency_key": "6d61f406-f07d-482d-a284-3e06edfd7f55"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/expenses';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'category' => 'b',
            'payee' => 'n',
            'amount' => 7,
            'occurred_on' => '2026-01-15',
            'payment_method' => 'cash',
            'payment_account_id' => 16,
            'note' => 'n',
            'idempotency_key' => '6d61f406-f07d-482d-a284-3e06edfd7f55',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/expenses

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

category   string     

Must not be greater than 64 characters. Example: b

payee   string  optional    

Must not be greater than 128 characters. Example: n

amount   number     

Must not be greater than 999999999. Example: 7

occurred_on   string     

Must be a valid date. Example: 2026-01-15

payment_method   string     

Example: cash

Must be one of:
  • cash
  • bank
  • upi
  • card
  • other
payment_account_id   integer  optional    

Example: 16

note   string     

Must not be greater than 1000 characters. Example: n

idempotency_key   string  optional    

Stable UUID used to make creation retries safe. Example: 6d61f406-f07d-482d-a284-3e06edfd7f55

GET api/v1/businesses/{business}/expenses/{expense_id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/expenses/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/expenses/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/expenses/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/expenses/{expense_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

expense_id   integer     

The ID of the expense. Example: 1

Void an expense atomically.

requires authentication

The void marker, journal reversal, cash-drawer reversal, and audit record either all commit or all roll back.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/expenses/1/void" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"reason\": \"b\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/expenses/1/void"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "reason": "b"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/expenses/1/void';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'reason' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/expenses/{expense_id}/void

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

expense_id   integer     

The ID of the expense. Example: 1

Body Parameters

reason   string     

Must be at least 3 characters. Must not be greater than 255 characters. Example: b

POS

GET api/v1/businesses/{business}/pos/items

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/pos/items" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"q\": \"b\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/pos/items"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "q": "b"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/pos/items';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'q' => 'b',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/pos/items

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

q   string  optional    

Must not be greater than 128 characters. Example: b

GET api/v1/businesses/{business}/pos/upi

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/pos/upi" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"amount\": 1,
    \"payment_account_id\": 16
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/pos/upi"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "amount": 1,
    "payment_account_id": 16
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/pos/upi';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'amount' => 1,
            'payment_account_id' => 16,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/pos/upi

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

amount   number     

Must not be greater than 10000000. Example: 1

payment_account_id   integer  optional    

Example: 16

List held POS carts for the business.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/pos/carts" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/pos/carts"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/pos/carts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/pos/carts

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Hold a POS cart for later checkout.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/pos/carts" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"b\",
    \"contact_id\": 16,
    \"lines\": [
        {
            \"item_id\": 16,
            \"quantity\": 4326.41688
        }
    ]
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/pos/carts"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "b",
    "contact_id": 16,
    "lines": [
        {
            "item_id": 16,
            "quantity": 4326.41688
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/pos/carts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'b',
            'contact_id' => 16,
            'lines' => [
                ['item_id' => 16, 'quantity' => 4326.41688],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/pos/carts

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

name   string     

Must not be greater than 96 characters. Example: b

contact_id   integer  optional    

Example: 16

lines   object[]     

Must have at least 1 items. Must not have more than 100 items.

item_id   integer     

Example: 16

quantity   number     

Example: 4326.41688

POST api/v1/businesses/{business}/pos/checkout

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/pos/checkout" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"contact_id\": 16,
    \"idempotency_key\": \"a4855dc5-0acb-33c3-b921-f4291f719ca0\",
    \"cart_id\": 16,
    \"lines\": [
        {
            \"item_id\": 16,
            \"quantity\": 4326.41688,
            \"discount\": 77
        }
    ],
    \"payments\": [
        {
            \"method\": \"cash\",
            \"payment_account_id\": 16,
            \"amount\": 4326.41688
        }
    ]
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/pos/checkout"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "contact_id": 16,
    "idempotency_key": "a4855dc5-0acb-33c3-b921-f4291f719ca0",
    "cart_id": 16,
    "lines": [
        {
            "item_id": 16,
            "quantity": 4326.41688,
            "discount": 77
        }
    ],
    "payments": [
        {
            "method": "cash",
            "payment_account_id": 16,
            "amount": 4326.41688
        }
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/pos/checkout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'contact_id' => 16,
            'idempotency_key' => 'a4855dc5-0acb-33c3-b921-f4291f719ca0',
            'cart_id' => 16,
            'lines' => [
                ['item_id' => 16, 'quantity' => 4326.41688, 'discount' => 77],
            ],
            'payments' => [
                ['method' => 'cash', 'payment_account_id' => 16, 'amount' => 4326.41688],
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/pos/checkout

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

contact_id   integer     

Example: 16

idempotency_key   string     

Must be a valid UUID. Example: a4855dc5-0acb-33c3-b921-f4291f719ca0

cart_id   integer  optional    

Example: 16

lines   object[]     

Must have at least 1 items. Must not have more than 100 items.

item_id   integer     

Example: 16

quantity   number     

Example: 4326.41688

discount   number  optional    

Must be at least 0. Example: 77

payments   object[]  optional    

Must not have more than 4 items.

method   string     

Example: cash

Must be one of:
  • cash
  • upi
  • card
  • bank
  • cheque
payment_account_id   integer  optional    

Example: 16

amount   number     

Example: 4326.41688

Cash register

GET api/v1/businesses/{business}/cash-register

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/cash-register" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"cash_register_id\": 16
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/cash-register"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "cash_register_id": 16
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/cash-register';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'cash_register_id' => 16,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/cash-register

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

cash_register_id   integer  optional    

Example: 16

POST api/v1/businesses/{business}/cash-register/open

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/cash-register/open" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"cash_register_id\": 16,
    \"opening_float\": 22
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/cash-register/open"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "cash_register_id": 16,
    "opening_float": 22
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/cash-register/open';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'cash_register_id' => 16,
            'opening_float' => 22,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/cash-register/open

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

cash_register_id   integer  optional    

Example: 16

opening_float   number     

Must be at least 0. Must not be greater than 999999999. Example: 22

POST api/v1/businesses/{business}/cash-register/movements

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/cash-register/movements" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"cash_in\",
    \"cash_register_id\": 16,
    \"amount\": 22,
    \"notes\": \"g\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/cash-register/movements"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "cash_in",
    "cash_register_id": 16,
    "amount": 22,
    "notes": "g"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/cash-register/movements';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'type' => 'cash_in',
            'cash_register_id' => 16,
            'amount' => 22,
            'notes' => 'g',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/cash-register/movements

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

type   string     

Example: cash_in

Must be one of:
  • cash_in
  • cash_out
cash_register_id   integer  optional    

Example: 16

amount   number     

Must not be greater than 999999999. Example: 22

notes   string     

Must not be greater than 255 characters. Example: g

POST api/v1/businesses/{business}/cash-register/close

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/cash-register/close" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"counted_cash\": 1,
    \"cash_register_id\": 16,
    \"closing_notes\": \"n\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/cash-register/close"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "counted_cash": 1,
    "cash_register_id": 16,
    "closing_notes": "n"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/cash-register/close';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'counted_cash' => 1,
            'cash_register_id' => 16,
            'closing_notes' => 'n',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/cash-register/close

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

counted_cash   number     

Must be at least 0. Must not be greater than 999999999. Example: 1

cash_register_id   integer  optional    

Example: 16

closing_notes   string  optional    

Must not be greater than 1000 characters. Example: n

Reports

GET api/v1/businesses/{business}/reports

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/reports" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"from\": \"2026-01-15\",
    \"to\": \"2026-01-15\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/reports"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "from": "2026-01-15",
    "to": "2026-01-15"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/reports';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'from' => '2026-01-15',
            'to' => '2026-01-15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/reports

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

from   string  optional    

Must be a valid date. Example: 2026-01-15

to   string  optional    

Must be a valid date. Must be a date after or equal to from. Example: 2026-01-15

Business compliance guidance

Get the compliance profile, registrations, evidence metadata and assessed obligations.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/compliance" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "business_id": 16,
        "state_code": "29",
        "profile": {
            "constitution": "proprietorship",
            "masked_pan": "ABC****34F",
            "district": "Bengaluru Urban",
            "local_body": "BBMP",
            "premises_type": "rented",
            "supply_type": "both",
            "gst_registration_status": "not_registered",
            "previous_year_turnover_paise": 180000000,
            "other_pan_turnover_paise": 25000000,
            "employee_count": 4,
            "contract_worker_count": 0,
            "female_employee_count": 2,
            "interstate_sales": false,
            "ecommerce_sales": true,
            "imports": false,
            "exports": false,
            "manufactures": false,
            "food_business": true,
            "uses_weighing_scale": true,
            "packs_or_imports_goods": false,
            "pharmacy": false,
            "serves_alcohol": false,
            "fire_risk": false,
            "pollution_activity": false,
            "reminder_days": [
                90,
                60,
                30,
                7,
                0
            ],
            "assessment_completed_at": "2026-08-20T12:00:00+05:30",
            "next_review_at": "2026-11-20T12:00:00+05:30"
        },
        "categories": [
            {
                "id": 13,
                "slug": "food-service",
                "name": "Restaurant, café and food service",
                "nic_section": "I"
            }
        ],
        "gst_registrations": [],
        "obligations": [
            {
                "id": 81,
                "rule_code": "fssai-registration-licence",
                "rule_version": 1,
                "title": "FSSAI registration or food licence",
                "applicability_reason": "Every food business operator must hold the matching registration or licence. Matched using Guidance Shop facts.",
                "applicability_level": "required",
                "obligation_type": "licence",
                "authority_name": "Food Safety and Standards Authority of India",
                "source_url": "https://fssai.gov.in/business/registration",
                "application_url": "https://foscos.fssai.gov.in/",
                "status": "action_required",
                "reference_number": null,
                "issued_on": null,
                "due_on": null,
                "expires_on": null,
                "notes": null,
                "manual_override": false,
                "last_assessed_at": "2026-08-20T12:00:00+05:30",
                "documents": []
            }
        ],
        "guidance_notice": "Guidance is generated from configurable rules and recorded business facts. Review items marked professional review and verify official sources before relying on them.",
        "options": {
            "constitutions": {
                "proprietorship": "Proprietorship",
                "partnership": "Partnership firm",
                "llp": "Limited Liability Partnership",
                "private_limited": "Private limited company",
                "public_limited": "Public limited company",
                "opc": "One Person Company",
                "huf": "Hindu Undivided Family",
                "trust_society": "Trust / society",
                "cooperative": "Co-operative society",
                "government": "Government entity",
                "other": "Other"
            },
            "supply_types": {
                "goods": "Goods only",
                "services": "Services only",
                "both": "Goods and services"
            },
            "gst_statuses": {
                "not_assessed": "Not assessed",
                "not_registered": "Not registered",
                "pending": "Application pending",
                "active": "Registered / active",
                "suspended": "Suspended",
                "cancelled": "Cancelled / surrendered"
            },
            "gst_registration_types": {
                "normal": "Normal taxpayer",
                "composition": "Composition taxpayer",
                "casual": "Casual taxable person",
                "non_resident": "Non-resident taxable person",
                "isd": "Input Service Distributor (ISD)",
                "tds": "Tax Deductor (TDS)",
                "tcs": "Tax Collector / e-commerce operator (TCS)",
                "sez_unit": "SEZ unit",
                "sez_developer": "SEZ developer",
                "oidar": "OIDAR / online money gaming supplier",
                "uin": "Unique Identity Number holder",
                "temporary": "Temporary registration"
            }
        }
    },
    "meta": {
        "available_categories": [
            {
                "id": 13,
                "slug": "food-service",
                "name": "Restaurant, café and food service",
                "nic_section": "I",
                "description": "Restaurants, cafés, caterers, cloud kitchens and mobile food service."
            }
        ]
    }
}
 

Request      

GET api/v1/businesses/{business_id}/compliance

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

Update business facts and immediately run the deterministic compliance assessment.

requires authentication

Example request:
curl --request PUT \
    "https://dukanam.com/api/v1/businesses/1/compliance/profile" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"state_code\": \"29\",
    \"category_ids\": [
        13
    ],
    \"constitution\": \"proprietorship\",
    \"pan\": \"ABCDE1234F\",
    \"district\": \"Bengaluru Urban\",
    \"local_body\": \"BBMP\",
    \"premises_type\": \"rented\",
    \"supply_type\": \"both\",
    \"gst_registration_status\": \"not_registered\",
    \"previous_year_turnover\": 1800000,
    \"other_pan_turnover\": 250000,
    \"employee_count\": 4,
    \"contract_worker_count\": 0,
    \"female_employee_count\": 2,
    \"interstate_sales\": false,
    \"ecommerce_sales\": true,
    \"imports\": false,
    \"exports\": false,
    \"manufactures\": false,
    \"food_business\": true,
    \"uses_weighing_scale\": true,
    \"packs_or_imports_goods\": false,
    \"pharmacy\": false,
    \"serves_alcohol\": false,
    \"fire_risk\": false,
    \"pollution_activity\": false,
    \"reminder_days\": [
        90,
        60,
        30,
        7,
        0
    ]
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/profile"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "state_code": "29",
    "category_ids": [
        13
    ],
    "constitution": "proprietorship",
    "pan": "ABCDE1234F",
    "district": "Bengaluru Urban",
    "local_body": "BBMP",
    "premises_type": "rented",
    "supply_type": "both",
    "gst_registration_status": "not_registered",
    "previous_year_turnover": 1800000,
    "other_pan_turnover": 250000,
    "employee_count": 4,
    "contract_worker_count": 0,
    "female_employee_count": 2,
    "interstate_sales": false,
    "ecommerce_sales": true,
    "imports": false,
    "exports": false,
    "manufactures": false,
    "food_business": true,
    "uses_weighing_scale": true,
    "packs_or_imports_goods": false,
    "pharmacy": false,
    "serves_alcohol": false,
    "fire_risk": false,
    "pollution_activity": false,
    "reminder_days": [
        90,
        60,
        30,
        7,
        0
    ]
};

fetch(url, {
    method: "PUT",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/profile';
$response = $client->put(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'state_code' => '29',
            'category_ids' => [13],
            'constitution' => 'proprietorship',
            'pan' => 'ABCDE1234F',
            'district' => 'Bengaluru Urban',
            'local_body' => 'BBMP',
            'premises_type' => 'rented',
            'supply_type' => 'both',
            'gst_registration_status' => 'not_registered',
            'previous_year_turnover' => 1800000,
            'other_pan_turnover' => 250000,
            'employee_count' => 4,
            'contract_worker_count' => 0,
            'female_employee_count' => 2,
            'interstate_sales' => false,
            'ecommerce_sales' => true,
            'imports' => false,
            'exports' => false,
            'manufactures' => false,
            'food_business' => true,
            'uses_weighing_scale' => true,
            'packs_or_imports_goods' => false,
            'pharmacy' => false,
            'serves_alcohol' => false,
            'fire_risk' => false,
            'pollution_activity' => false,
            'reminder_days' => [90, 60, 30, 7, 0],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "business_id": 16,
        "state_code": "29",
        "profile": {
            "constitution": "proprietorship",
            "masked_pan": "ABC****34F",
            "district": "Bengaluru Urban",
            "local_body": "BBMP",
            "premises_type": "rented",
            "supply_type": "both",
            "gst_registration_status": "not_registered",
            "previous_year_turnover_paise": 180000000,
            "other_pan_turnover_paise": 25000000,
            "employee_count": 4,
            "contract_worker_count": 0,
            "female_employee_count": 2,
            "interstate_sales": false,
            "ecommerce_sales": true,
            "imports": false,
            "exports": false,
            "manufactures": false,
            "food_business": true,
            "uses_weighing_scale": true,
            "packs_or_imports_goods": false,
            "pharmacy": false,
            "serves_alcohol": false,
            "fire_risk": false,
            "pollution_activity": false,
            "reminder_days": [
                90,
                60,
                30,
                7,
                0
            ],
            "assessment_completed_at": "2026-08-20T12:00:00+05:30",
            "next_review_at": "2026-11-20T12:00:00+05:30"
        },
        "categories": [
            {
                "id": 13,
                "slug": "food-service",
                "name": "Restaurant, café and food service",
                "nic_section": "I"
            }
        ],
        "gst_registrations": [],
        "obligations": [
            {
                "id": 81,
                "rule_code": "fssai-registration-licence",
                "rule_version": 1,
                "title": "FSSAI registration or food licence",
                "applicability_reason": "Every food business operator must hold the matching registration or licence. Matched using Guidance Shop facts.",
                "applicability_level": "required",
                "obligation_type": "licence",
                "authority_name": "Food Safety and Standards Authority of India",
                "source_url": "https://fssai.gov.in/business/registration",
                "application_url": "https://foscos.fssai.gov.in/",
                "status": "action_required",
                "reference_number": null,
                "issued_on": null,
                "due_on": null,
                "expires_on": null,
                "notes": null,
                "manual_override": false,
                "last_assessed_at": "2026-08-20T12:00:00+05:30",
                "documents": []
            }
        ],
        "guidance_notice": "Guidance is generated from configurable rules and recorded business facts. Review items marked professional review and verify official sources before relying on them.",
        "options": {
            "constitutions": {
                "proprietorship": "Proprietorship",
                "partnership": "Partnership firm",
                "llp": "Limited Liability Partnership",
                "private_limited": "Private limited company",
                "public_limited": "Public limited company",
                "opc": "One Person Company",
                "huf": "Hindu Undivided Family",
                "trust_society": "Trust / society",
                "cooperative": "Co-operative society",
                "government": "Government entity",
                "other": "Other"
            },
            "supply_types": {
                "goods": "Goods only",
                "services": "Services only",
                "both": "Goods and services"
            },
            "gst_statuses": {
                "not_assessed": "Not assessed",
                "not_registered": "Not registered",
                "pending": "Application pending",
                "active": "Registered / active",
                "suspended": "Suspended",
                "cancelled": "Cancelled / surrendered"
            },
            "gst_registration_types": {
                "normal": "Normal taxpayer",
                "composition": "Composition taxpayer",
                "casual": "Casual taxable person",
                "non_resident": "Non-resident taxable person",
                "isd": "Input Service Distributor (ISD)",
                "tds": "Tax Deductor (TDS)",
                "tcs": "Tax Collector / e-commerce operator (TCS)",
                "sez_unit": "SEZ unit",
                "sez_developer": "SEZ developer",
                "oidar": "OIDAR / online money gaming supplier",
                "uin": "Unique Identity Number holder",
                "temporary": "Temporary registration"
            }
        }
    },
    "meta": {
        "available_categories": [
            {
                "id": 13,
                "slug": "food-service",
                "name": "Restaurant, café and food service",
                "nic_section": "I",
                "description": "Restaurants, cafés, caterers, cloud kitchens and mobile food service."
            }
        ]
    }
}
 

Request      

PUT api/v1/businesses/{business_id}/compliance/profile

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

Body Parameters

state_code   string     

Two-character GST state/UT code. Example: 29

category_ids   integer[]     

Must match an existing stored value.

constitution   string     

Legal constitution of the business. Example: proprietorship

Must be one of:
  • proprietorship
  • partnership
  • llp
  • private_limited
  • public_limited
  • opc
  • huf
  • trust_society
  • cooperative
  • government
  • other
pan   string  optional    

PAN. Required for a business marked not registered under GST. Omit to keep the saved PAN. Must match the regex /^[A-Z]{5}[0-9]{4}[A-Z]$/. Must be 10 characters. Example: ABCDE1234F

district   string  optional    

District containing the principal place of business. Must not be greater than 96 characters. Example: Bengaluru Urban

local_body   string  optional    

Municipality, corporation or panchayat. Must not be greater than 128 characters. Example: BBMP

premises_type   string     

How the principal premises is occupied. Example: rented

Must be one of:
  • owned
  • rented
  • leased
  • home
  • mobile
  • virtual
  • other
supply_type   string     

Whether the business supplies goods, services or both. Example: both

Must be one of:
  • goods
  • services
  • both
gst_registration_status   string     

Declared GST registration status. Example: not_registered

Must be one of:
  • not_assessed
  • not_registered
  • pending
  • active
  • suspended
  • cancelled
previous_year_turnover   number     

Previous financial-year PAN-wide aggregate turnover in rupees. Must be at least 0. Must not be greater than 999999999999.99. Example: 1800000

other_pan_turnover   number     

Current-year PAN-wide turnover not recorded in this workspace, in rupees. Must be at least 0. Must not be greater than 999999999999.99. Example: 250000

employee_count   integer     

Direct employee count. Must be at least 0. Must not be greater than 1000000. Example: 4

contract_worker_count   integer     

Contract worker count. Must be at least 0. Must not be greater than 1000000. Example: 0

female_employee_count   integer     

Women employees, not exceeding employee_count. Must be at least 0. Must not be greater than 1000000. Example: 2

interstate_sales   boolean     

Example: false

ecommerce_sales   boolean     

Example: true

imports   boolean     

Example: false

exports   boolean     

Example: false

manufactures   boolean     

Example: false

food_business   boolean     

Example: true

uses_weighing_scale   boolean     

Example: true

packs_or_imports_goods   boolean     

Example: false

pharmacy   boolean     

Example: false

serves_alcohol   boolean     

Example: false

fire_risk   boolean     

Example: false

pollution_activity   boolean     

Example: false

reminder_days   integer[]  optional    

Must be at least 0. Must not be greater than 365.

Re-run assessment against the latest published rule revisions.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/compliance/assess" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/assess"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/assess';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "matched": 8,
        "closed": 1
    }
}
 

Request      

POST api/v1/businesses/{business_id}/compliance/assess

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

POST api/v1/businesses/{business_id}/compliance/gst-registrations

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/compliance/gst-registrations" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"registration_type\": \"composition\",
    \"registration_status\": \"active\",
    \"gstin\": \"29ABCDE1234F1Z5\",
    \"uin\": null,
    \"state_code\": \"29\",
    \"is_primary\": true,
    \"valid_from\": \"2026-04-01\",
    \"valid_until\": null
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/gst-registrations"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "registration_type": "composition",
    "registration_status": "active",
    "gstin": "29ABCDE1234F1Z5",
    "uin": null,
    "state_code": "29",
    "is_primary": true,
    "valid_from": "2026-04-01",
    "valid_until": null
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/gst-registrations';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'registration_type' => 'composition',
            'registration_status' => 'active',
            'gstin' => '29ABCDE1234F1Z5',
            'uin' => null,
            'state_code' => '29',
            'is_primary' => true,
            'valid_from' => '2026-04-01',
            'valid_until' => null,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": 32,
        "business_id": 16,
        "registration_type": "composition",
        "registration_status": "active",
        "gstin": "29ABCDE1234F1Z5",
        "uin": null,
        "state_code": "29",
        "is_primary": true,
        "valid_from": "2026-04-01T00:00:00.000000Z",
        "valid_until": null,
        "created_at": "2026-08-20T06:30:00.000000Z",
        "updated_at": "2026-08-20T06:30:00.000000Z"
    }
}
 

Request      

POST api/v1/businesses/{business_id}/compliance/gst-registrations

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

Body Parameters

registration_type   string     

GST taxpayer/registration profile. Example: composition

Must be one of:
  • normal
  • composition
  • casual
  • non_resident
  • isd
  • tds
  • tcs
  • sez_unit
  • sez_developer
  • oidar
  • uin
  • temporary
registration_status   string     

Current portal status. Example: active

Must be one of:
  • pending
  • active
  • suspended
  • cancelled
gstin   string  optional    

15-character GSTIN. Required except for a UIN record. This field is required unless registration_type is in uin. Must match the regex /^[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z][1-9A-Z]Z[0-9A-Z]$/. Must be 15 characters. Example: 29ABCDE1234F1Z5

uin   string  optional    

Unique Identity Number for UIN records. This field is required when registration_type is uin. Must not be greater than 32 characters.

state_code   string  optional    

Two-character state/UT code; must match the GSTIN prefix. Example: 29

is_primary   boolean     

Whether this is the primary invoicing registration. Example: true

valid_from   string  optional    

Must be a valid date. Example: 2026-04-01

valid_until   string  optional    

Must be a valid date. Must be a date after or equal to valid_from.

DELETE api/v1/businesses/{business_id}/compliance/gst-registrations/{gstRegistration_id}

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/businesses/1/compliance/gst-registrations/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/gst-registrations/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/gst-registrations/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (204):

Empty response
 

Request      

DELETE api/v1/businesses/{business_id}/compliance/gst-registrations/{gstRegistration_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

gstRegistration_id   integer     

The ID of the gstRegistration. Example: 1

GET api/v1/businesses/{business_id}/compliance/obligations/{complianceObligation_id}

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/compliance/obligations/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/obligations/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/obligations/1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 81,
        "rule_code": "fssai-registration-licence",
        "rule_version": 1,
        "title": "FSSAI registration or food licence",
        "applicability_reason": "Every food business operator must hold the matching registration or licence. Matched using Guidance Shop facts.",
        "applicability_level": "required",
        "obligation_type": "licence",
        "authority_name": "Food Safety and Standards Authority of India",
        "source_url": "https://fssai.gov.in/business/registration",
        "application_url": "https://foscos.fssai.gov.in/",
        "status": "obtained",
        "reference_number": "FSSAI-10010022000123",
        "issued_on": "2026-04-01",
        "due_on": null,
        "expires_on": "2027-03-31",
        "notes": "Certificate verified.",
        "manual_override": false,
        "last_assessed_at": "2026-08-20T12:00:00+05:30",
        "documents": [
            {
                "id": 44,
                "original_name": "fssai-certificate.pdf",
                "mime_type": "application/pdf",
                "size": 102400,
                "document_number": "FSSAI-10010022000123",
                "issued_on": "2026-04-01",
                "expires_on": "2027-03-31",
                "is_current": true,
                "download_url": "https://billing.example.com/api/v1/businesses/16/compliance/documents/44/download"
            }
        ]
    }
}
 

Request      

GET api/v1/businesses/{business_id}/compliance/obligations/{complianceObligation_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

complianceObligation_id   integer     

The ID of the complianceObligation. Example: 1

PATCH api/v1/businesses/{business_id}/compliance/obligations/{complianceObligation_id}

requires authentication

Example request:
curl --request PATCH \
    "https://dukanam.com/api/v1/businesses/1/compliance/obligations/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"status\": \"obtained\",
    \"reference_number\": \"LIC-2026-1001\",
    \"issued_on\": \"2026-04-01\",
    \"due_on\": null,
    \"expires_on\": \"2027-03-31\",
    \"notes\": \"Renewal filed by the accountant.\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/obligations/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "status": "obtained",
    "reference_number": "LIC-2026-1001",
    "issued_on": "2026-04-01",
    "due_on": null,
    "expires_on": "2027-03-31",
    "notes": "Renewal filed by the accountant."
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/obligations/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'status' => 'obtained',
            'reference_number' => 'LIC-2026-1001',
            'issued_on' => '2026-04-01',
            'due_on' => null,
            'expires_on' => '2027-03-31',
            'notes' => 'Renewal filed by the accountant.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 81,
        "rule_code": "fssai-registration-licence",
        "rule_version": 1,
        "title": "FSSAI registration or food licence",
        "applicability_reason": "Every food business operator must hold the matching registration or licence. Matched using Guidance Shop facts.",
        "applicability_level": "required",
        "obligation_type": "licence",
        "authority_name": "Food Safety and Standards Authority of India",
        "source_url": "https://fssai.gov.in/business/registration",
        "application_url": "https://foscos.fssai.gov.in/",
        "status": "obtained",
        "reference_number": "FSSAI-10010022000123",
        "issued_on": "2026-04-01",
        "due_on": null,
        "expires_on": "2027-03-31",
        "notes": "Certificate verified.",
        "manual_override": false,
        "last_assessed_at": "2026-08-20T12:00:00+05:30",
        "documents": [
            {
                "id": 44,
                "original_name": "fssai-certificate.pdf",
                "mime_type": "application/pdf",
                "size": 102400,
                "document_number": "FSSAI-10010022000123",
                "issued_on": "2026-04-01",
                "expires_on": "2027-03-31",
                "is_current": true,
                "download_url": "https://billing.example.com/api/v1/businesses/16/compliance/documents/44/download"
            }
        ]
    }
}
 

Request      

PATCH api/v1/businesses/{business_id}/compliance/obligations/{complianceObligation_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

complianceObligation_id   integer     

The ID of the complianceObligation. Example: 1

Body Parameters

status   string     

Owner-tracked status. not_applicable requires notes. Example: obtained

Must be one of:
  • action_required
  • in_progress
  • obtained
  • not_applicable
  • expired
  • no_longer_applicable
reference_number   string  optional    

Application, registration or licence number. Must not be greater than 128 characters. Example: LIC-2026-1001

issued_on   string  optional    

Must be a valid date. Example: 2026-04-01

due_on   string  optional    

Must be a valid date.

expires_on   string  optional    

Must be a valid date. Must be a date after or equal to issued_on. Example: 2027-03-31

notes   string  optional    

Must not be greater than 5000 characters. Example: Renewal filed by the accountant.

POST api/v1/businesses/{business_id}/compliance/obligations/{complianceObligation_id}/documents

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/compliance/obligations/1/documents" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: multipart/form-data" \
    --header "Accept: application/json" \
    --form "document_number=FSSAI-10010022000123"\
    --form "issued_on=2026-04-01"\
    --form "expires_on=2027-03-31"\
    --form "document=@/path/to/file" 
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/obligations/1/documents"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "multipart/form-data",
    "Accept": "application/json",
};

const body = new FormData();
body.append('document_number', 'FSSAI-10010022000123');
body.append('issued_on', '2026-04-01');
body.append('expires_on', '2027-03-31');
body.append('document', document.querySelector('input[name="document"]').files[0]);

fetch(url, {
    method: "POST",
    headers,
    body,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/obligations/1/documents';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'multipart/form-data',
            'Accept' => 'application/json',
        ],
        'multipart' => [
            [
                'name' => 'document_number',
                'contents' => 'FSSAI-10010022000123'
            ],
            [
                'name' => 'issued_on',
                'contents' => '2026-04-01'
            ],
            [
                'name' => 'expires_on',
                'contents' => '2027-03-31'
            ],
            [
                'name' => 'document',
                'contents' => fopen('/path/to/file', 'r')
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 81,
        "rule_code": "fssai-registration-licence",
        "rule_version": 1,
        "title": "FSSAI registration or food licence",
        "applicability_reason": "Every food business operator must hold the matching registration or licence. Matched using Guidance Shop facts.",
        "applicability_level": "required",
        "obligation_type": "licence",
        "authority_name": "Food Safety and Standards Authority of India",
        "source_url": "https://fssai.gov.in/business/registration",
        "application_url": "https://foscos.fssai.gov.in/",
        "status": "obtained",
        "reference_number": "FSSAI-10010022000123",
        "issued_on": "2026-04-01",
        "due_on": null,
        "expires_on": "2027-03-31",
        "notes": "Certificate verified.",
        "manual_override": false,
        "last_assessed_at": "2026-08-20T12:00:00+05:30",
        "documents": [
            {
                "id": 44,
                "original_name": "fssai-certificate.pdf",
                "mime_type": "application/pdf",
                "size": 102400,
                "document_number": "FSSAI-10010022000123",
                "issued_on": "2026-04-01",
                "expires_on": "2027-03-31",
                "is_current": true,
                "download_url": "https://billing.example.com/api/v1/businesses/16/compliance/documents/44/download"
            }
        ]
    }
}
 

Request      

POST api/v1/businesses/{business_id}/compliance/obligations/{complianceObligation_id}/documents

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: multipart/form-data

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

complianceObligation_id   integer     

The ID of the complianceObligation. Example: 1

Body Parameters

document   file     

Private PDF/JPEG/PNG/WebP evidence, maximum 10 MB. Must be a file. Must not be greater than 10240 kilobytes. Example: /path/to/file

document_number   string  optional    

Certificate or licence number. Must not be greater than 128 characters. Example: FSSAI-10010022000123

issued_on   string  optional    

Must be a valid date. Example: 2026-04-01

expires_on   string  optional    

Must be a valid date. Must be a date after or equal to issued_on. Example: 2027-03-31

GET api/v1/businesses/{business_id}/compliance/documents/{complianceDocument_id}/download

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/compliance/documents/1/download" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/compliance/documents/1/download"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/compliance/documents/1/download';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business_id}/compliance/documents/{complianceDocument_id}/download

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business_id   integer     

The ID of the business. Example: 1

complianceDocument_id   integer     

The ID of the complianceDocument. Example: 1

GST compliance

GET api/v1/businesses/{business}/gst

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/gst" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/gst"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/gst';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/gst

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

GET api/v1/businesses/{business}/gst/gstr1

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/gst/gstr1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/gst/gstr1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/gst/gstr1';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/gst/gstr1

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

POST api/v1/businesses/{business}/gst/gstr2b

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/gst/gstr2b" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/gst/gstr2b"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/gst/gstr2b';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/gst/gstr2b

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Billing

List plans and activation availability

requires authentication

Use razorpay_checkout_enabled for paid checkout. paid_plan_activation_enabled refers only to the support-only manual activation fallback, while each plan's activation_available covers either path.

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/billing" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/billing

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Response

Response Fields

data   object     
paid_plan_activation_enabled   boolean     

Whether customers may self-activate paid plans.

razorpay_checkout_enabled   boolean     

Whether Razorpay subscription checkout is configured.

plans   object     
activation_available   boolean     

Whether this plan may be selected through the API.

monthly_mrp_paise   integer     

Regular monthly price before an active offer.

monthly_price_paise   integer     

Effective monthly price after any active offer.

yearly_mrp_paise   integer     

Regular yearly price before an active offer.

yearly_price_paise   integer     

Effective yearly price after any active offer.

offer   object|null     

Active offer label, validity, and discount details.

features   string[]     

Effective feature list, including the core expenses capability on every plan.

limits   object     
items   integer     

Maximum catalogue item count; 0 means unlimited.

List subscription payments

requires authentication

Returns successful subscription charges and mandate authorisations for the business owner. Only captured recurring charges expose an invoice download URL.

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/billing/payments?per_page=20" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing/payments"
);

const params = {
    "per_page": "20",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing/payments';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'per_page' => '20',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/billing/payments

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Query Parameters

per_page   integer  optional    

Results per page. Example: 20

Download a subscription invoice

requires authentication

Downloads the immutable PDF invoice for a captured recurring subscription charge.

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/billing/payments/1/invoice" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing/payments/1/invoice"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing/payments/1/invoice';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/billing/payments/{subscriptionPayment_id}/invoice

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

subscriptionPayment_id   integer     

The ID of the subscriptionPayment. Example: 1

Change the current plan

requires authentication

Paid plans return HTTP 422 while online paid-plan activation is disabled. The free plan remains selectable. Changing away from a Razorpay-backed plan cancels that provider subscription immediately before the new plan is activated, so mobile clients must confirm an immediate loss of paid access before submitting. If Razorpay cannot confirm cancellation, the endpoint returns HTTP 422 and leaves the current plan unchanged.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/billing/change" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"plan_id\": \"architecto\",
    \"billing_interval\": \"monthly\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing/change"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "plan_id": "architecto",
    "billing_interval": "monthly"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing/change';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'plan_id' => 'architecto',
            'billing_interval' => 'monthly',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (422):


{
    "message": "Online paid-plan activation is not configured. Contact support to change this plan.",
    "errors": {
        "plan_id": [
            "Online paid-plan activation is not configured. Contact support to change this plan."
        ]
    }
}
 

Request      

POST api/v1/businesses/{business}/billing/change

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

plan_id   string     

Must match an existing stored value. Example: architecto

billing_interval   string     

Example: monthly

Must be one of:
  • monthly
  • yearly

Start Razorpay subscription checkout

requires authentication

Creates a price-versioned Razorpay subscription. Mobile clients must pass the returned subscription_id to Razorpay Standard Checkout and then send the signed result to the confirm endpoint.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/billing/checkout" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"plan_id\": \"architecto\",
    \"billing_interval\": \"monthly\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing/checkout"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "plan_id": "architecto",
    "billing_interval": "monthly"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing/checkout';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'plan_id' => 'architecto',
            'billing_interval' => 'monthly',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "checkout_id": 1,
        "key": "rzp_test_example",
        "subscription_id": "sub_example",
        "name": "Dukanam",
        "description": "Smart Books · Monthly",
        "amount_paise": 99900,
        "currency": "INR",
        "trial_days": 14,
        "prefill": {
            "name": "Shop Owner",
            "email": "[email protected]",
            "contact": "9876543210"
        }
    }
}
 

Request      

POST api/v1/businesses/{business}/billing/checkout

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

plan_id   string     

Must match an existing stored value. Example: architecto

billing_interval   string     

Example: monthly

Must be one of:
  • monthly
  • yearly

Confirm Razorpay subscription authorisation

requires authentication

The server verifies the Razorpay HMAC signature and fetches provider state before granting access. Trial access starts only after successful payment-method authorisation; a plan without a trial waits for Razorpay to report the subscription as active. When the checkout replaces another Razorpay plan, the previous provider subscription is cancelled immediately before the replacement is activated.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/billing/checkouts/1/confirm" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"razorpay_payment_id\": \"b\",
    \"razorpay_subscription_id\": \"n\",
    \"razorpay_signature\": \"gzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtn\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing/checkouts/1/confirm"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "razorpay_payment_id": "b",
    "razorpay_subscription_id": "n",
    "razorpay_signature": "gzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtn"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing/checkouts/1/confirm';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'razorpay_payment_id' => 'b',
            'razorpay_subscription_id' => 'n',
            'razorpay_signature' => 'gzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtn',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/billing/checkouts/{billingCheckout_id}/confirm

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

billingCheckout_id   integer     

The ID of the billingCheckout. Example: 1

Body Parameters

razorpay_payment_id   string     

Must match the regex /^pay_[A-Za-z0-9]+$/. Must not be greater than 191 characters. Example: b

razorpay_subscription_id   string     

Must match the regex /^sub_[A-Za-z0-9]+$/. Must not be greater than 191 characters. Example: n

razorpay_signature   string     

Must match the regex /^[a-f0-9]{64}$/i. Must be 64 characters. Example: gzmiyvdljnikhwaykcmyuwpwlvqwrsitcpscqldzsnrwtujwvlxjklqppwqbewtn

POST api/v1/businesses/{business}/billing/cancel

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/billing/cancel" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/billing/cancel"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/billing/cancel';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/billing/cancel

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Data exports

Export business data

requires authentication

CSV exports are intended for reporting and use dukanam-{type}-YYYY-MM-DD.csv filenames. The legacy business-backup type returns a dukanam-business-export-v2 owner/admin-only customer-data portability export named dukanam-business-export-YYYY-MM-DD.json. It includes item photos and compliance evidence content, but is not a restorable platform backup.

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/exports?type=invoices" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"type\": \"contacts\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/exports"
);

const params = {
    "type": "invoices",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "type": "contacts"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/exports';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'type' => 'invoices',
        ],
        'json' => [
            'type' => 'contacts',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/exports

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Query Parameters

type   string     

Export type. Example: invoices

Body Parameters

type   string     

Example: contacts

Must be one of:
  • contacts
  • ledger
  • invoices
  • expenses
  • business-backup

Team access

Owners and administrators can manage workspace members. Smart Books supports one accountant seat in addition to the owner. Business supports all documented roles, with five total seats including the owner and pending invitations.

List team members, pending invitations, and the 25 most recent expired invitations.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/team" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/team"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/team';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "members": [
            {
                "id": 2,
                "name": "Priya Rao",
                "email": "[email protected]",
                "role": "cashier",
                "status": "active",
                "joined_at": "2026-08-21T12:00:00.000000Z"
            }
        ],
        "invitations": [],
        "roles": {
            "cashier": {
                "label": "Cashier",
                "description": "POS, sales, and cash register access."
            },
            "accountant": {
                "label": "Accountant",
                "description": "Sales, purchases, inventory, accounting, reports, compliance, and exports."
            }
        },
        "seats": {
            "used": 2,
            "limit": 5,
            "active": 2,
            "pending": 0
        }
    }
}
 

Request      

GET api/v1/businesses/{business}/team

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Response

Response Fields

data   object     
seats   object     
limit   integer     

Total workspace seat limit. A value of 0 means unlimited seats.

Invite a team member.

requires authentication

Pending invitations reserve a seat and expire after seven days.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/team/invitations" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"email\": \"[email protected]\",
    \"role\": \"cashier\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/team/invitations"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "email": "[email protected]",
    "role": "cashier"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/team/invitations';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'email' => '[email protected]',
            'role' => 'cashier',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "data": {
        "id": 12,
        "email": "[email protected]",
        "role": "cashier",
        "status": "pending",
        "expires_at": "2026-08-28T12:00:00.000000Z",
        "accepted_at": null,
        "invited_by": {
            "id": 1,
            "name": "Workspace Owner"
        }
    }
}
 

Request      

POST api/v1/businesses/{business}/team/invitations

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

email   string     

The invited person's email. Example: [email protected]

role   string     

One of admin, manager, cashier, accountant, staff, or viewer. Example: cashier

Resend a pending or expired invitation with a new secure token.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/team/invitations/1/resend" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/team/invitations/1/resend"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/team/invitations/1/resend';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 12,
        "email": "[email protected]",
        "role": "cashier",
        "status": "pending",
        "expires_at": "2026-08-28T12:00:00.000000Z",
        "accepted_at": null,
        "invited_by": {
            "id": 1,
            "name": "Workspace Owner"
        }
    }
}
 

Example response (422):


{
    "message": "All 2 workspace seats are already assigned or reserved.",
    "errors": {
        "email": [
            "All 2 workspace seats are already assigned or reserved."
        ]
    }
}
 

Request      

POST api/v1/businesses/{business}/team/invitations/{teamInvitation_id}/resend

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

teamInvitation_id   integer     

The ID of the teamInvitation. Example: 1

Cancel a pending invitation and release its reserved seat.

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/businesses/1/team/invitations/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/team/invitations/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/team/invitations/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (204):

Empty response
 

Example response (403):


{
    "message": "Only the owner can manage administrator invitations."
}
 

Request      

DELETE api/v1/businesses/{business}/team/invitations/{teamInvitation_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

teamInvitation_id   integer     

The ID of the teamInvitation. Example: 1

Change a member's role or suspend/reactivate access.

requires authentication

Example request:
curl --request PATCH \
    "https://dukanam.com/api/v1/businesses/1/team/members/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"role\": \"manager\",
    \"status\": \"active\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/team/members/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "role": "manager",
    "status": "active"
};

fetch(url, {
    method: "PATCH",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/team/members/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'role' => 'manager',
            'status' => 'active',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "id": 2,
        "name": "Priya Rao",
        "email": "[email protected]",
        "role": "manager",
        "status": "active",
        "joined_at": "2026-08-21T12:00:00.000000Z"
    }
}
 

Example response (422):


{
    "message": "All 2 workspace seats are already assigned or reserved.",
    "errors": {
        "role": [
            "All 2 workspace seats are already assigned or reserved."
        ],
        "status": [
            "All 2 workspace seats are already assigned or reserved."
        ]
    }
}
 

Request      

PATCH api/v1/businesses/{business}/team/members/{member_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

member_id   integer     

The ID of the member. Example: 1

Body Parameters

role   string     

One of admin, manager, cashier, accountant, staff, or viewer. Example: manager

status   string     

Either active or suspended. Example: active

Remove a member's workspace access without deleting their user account.

requires authentication

Example request:
curl --request DELETE \
    "https://dukanam.com/api/v1/businesses/1/team/members/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/team/members/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/team/members/1';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (204):

Empty response
 

Request      

DELETE api/v1/businesses/{business}/team/members/{member_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

member_id   integer     

The ID of the member. Example: 1

Team invitations

Accept a team invitation as an existing user.

requires authentication

Send a valid Sanctum bearer token belonging to the invited email. This endpoint does not create a new token.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa/accept" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa/accept"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa/accept';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "user": {
            "id": 2,
            "name": "Priya Rao",
            "email": "[email protected]"
        },
        "business": {
            "id": 1,
            "name": "Anika Stores",
            "role": "cashier"
        }
    }
}
 

Example response (404):


{
    "message": "Not Found"
}
 

Example response (422):


{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "Sign in with the email address that received this invitation."
        ],
        "invitation": [
            "This workspace is no longer available."
        ]
    }
}
 

Request      

POST api/v1/team-invitations/{token}/accept

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

token   string     

The opaque token from the invitation email. Example: 4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa

Inspect a team invitation.

This endpoint does not require authentication. The opaque token is supplied by the invitation email.

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "business": {
            "id": 1,
            "name": "Anika Stores"
        },
        "email": "[email protected]",
        "role": "cashier",
        "role_label": "Cashier",
        "status": "pending",
        "expires_at": "2026-08-28T12:00:00.000000Z",
        "existing_account": false
    }
}
 

Example response (404):


{
    "message": "Not Found"
}
 

Request      

GET api/v1/team-invitations/{token}

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

token   string     

The opaque token from the invitation email. Example: 4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa

Create an account and accept a team invitation.

Use this unauthenticated endpoint only when the invited email does not already belong to a Dukanam user. The response includes a new Sanctum device token.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa/register-and-accept" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"name\": \"Priya Rao\",
    \"password\": \"secret-pass-123\",
    \"device_name\": \"Priya\'s phone\",
    \"password_confirmation\": \"secret-pass-123\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa/register-and-accept"
);

const headers = {
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "name": "Priya Rao",
    "password": "secret-pass-123",
    "device_name": "Priya's phone",
    "password_confirmation": "secret-pass-123"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/team-invitations/4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa/register-and-accept';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'name' => 'Priya Rao',
            'password' => 'secret-pass-123',
            'device_name' => 'Priya\'s phone',
            'password_confirmation' => 'secret-pass-123',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": {
        "user": {
            "id": 2,
            "name": "Priya Rao",
            "email": "[email protected]"
        },
        "business": {
            "id": 1,
            "name": "Anika Stores",
            "role": "cashier"
        },
        "token": "1|new-mobile-token"
    }
}
 

Example response (403):


{
    "message": "Sign in as the invited user to accept this invitation."
}
 

Example response (404):


{
    "message": "Not Found"
}
 

Example response (422):


{
    "message": "This invitation has expired or is no longer available.",
    "errors": {
        "invitation": [
            "This invitation has expired or is no longer available."
        ]
    }
}
 

Request      

POST api/v1/team-invitations/{token}/register-and-accept

Headers

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

token   string     

The opaque token from the invitation email. Example: 4WvBr4F8Dmcx3FJkL1nEz7PcQs2Yu9Aa

Body Parameters

name   string     

The new user's name. Example: Priya Rao

password   string     

The new user's password; minimum eight characters. Example: secret-pass-123

device_name   string     

The name for the new API token. Example: Priya's phone

password_confirmation   string     

Password confirmation. Example: secret-pass-123

Billing webhooks

Receive Razorpay subscription events

This public provider callback requires a valid X-Razorpay-Signature HMAC header and is idempotent.

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/webhooks/razorpay" \
    --header "X-Razorpay-Signature: string required Razorpay webhook HMAC signature. Example: 0123456789abcdef" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/webhooks/razorpay"
);

const headers = {
    "X-Razorpay-Signature": "string required Razorpay webhook HMAC signature. Example: 0123456789abcdef",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/webhooks/razorpay';
$response = $client->post(
    $url,
    [
        'headers' => [
            'X-Razorpay-Signature' => 'string required Razorpay webhook HMAC signature. Example: 0123456789abcdef',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "received": true
}
 

Example response (401):


{
    "message": "Invalid webhook signature."
}
 

Request      

POST api/v1/webhooks/razorpay

Headers

X-Razorpay-Signature        

Example: string required Razorpay webhook HMAC signature. Example: 0123456789abcdef

Content-Type        

Example: application/json

Accept        

Example: application/json

Lookups

Search tenant-scoped values for lookup controls. Results are limited to the active business and the caller's workspace permissions and plan features.

Search lookup values.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/lookups?source=customers&q=priya&method=upi&direction=received" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/lookups"
);

const params = {
    "source": "customers",
    "q": "priya",
    "method": "upi",
    "direction": "received",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/lookups';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'source' => 'customers',
            'q' => 'priya',
            'method' => 'upi',
            'direction' => 'received',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "data": [
        {
            "value": "42",
            "label": "Priya Sharma",
            "meta": "Customer · 9876543210",
            "attributes": {
                "type": "customer",
                "phone": "9876543210"
            }
        }
    ],
    "meta": {
        "source": "customers",
        "query": "priya",
        "has_more": false
    }
}
 

Request      

GET api/v1/businesses/{business}/lookups

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Query Parameters

source   string     

Lookup source. Example: customers

q   string  optional    

Search text, up to 100 characters. Example: priya

method   string  optional    

Payment method used to filter compatible payment accounts. Example: upi

direction   string  optional    

Document or account direction. Example: received

Response

Response Fields

data   object     
value   string     

Stable form value for the result.

label   string     

Human-readable result label.

meta   string|null     

Optional secondary result description.

attributes   object     

Source-specific form attributes. Contact results can include type, name, phone, and gstin. Item results include name, price, tax, cess, and stock; purchase_price is present only for callers with purchases, inventory, or accounting permission. Payment-account results include type, methods, default, upi, and open. Outstanding-document results include balance.

meta   object     
source   string     

Lookup source used for the response.

query   string     

Normalized search query.

has_more   boolean     

Whether more matching values exist beyond this response.

Payment accounts

GET api/v1/businesses/{business}/payment-accounts

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/payment-accounts" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/payment-accounts

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

POST api/v1/businesses/{business}/payment-accounts

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/payment-accounts" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/payment-accounts

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

PATCH api/v1/businesses/{business}/payment-accounts/{paymentAccount_id}

requires authentication

Example request:
curl --request PATCH \
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "PATCH",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts/1';
$response = $client->patch(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

PATCH api/v1/businesses/{business}/payment-accounts/{paymentAccount_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

paymentAccount_id   integer     

The ID of the paymentAccount. Example: 1

POST api/v1/businesses/{business}/payment-accounts/transfer

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/transfer" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"from_payment_account_id\": 16,
    \"to_payment_account_id\": 16,
    \"amount\": 4326.41688,
    \"transferred_on\": \"2026-01-15\",
    \"reference\": \"m\",
    \"notes\": \"i\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/transfer"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "from_payment_account_id": 16,
    "to_payment_account_id": 16,
    "amount": 4326.41688,
    "transferred_on": "2026-01-15",
    "reference": "m",
    "notes": "i"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts/transfer';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'from_payment_account_id' => 16,
            'to_payment_account_id' => 16,
            'amount' => 4326.41688,
            'transferred_on' => '2026-01-15',
            'reference' => 'm',
            'notes' => 'i',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/payment-accounts/transfer

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

Body Parameters

from_payment_account_id   integer     

Example: 16

to_payment_account_id   integer     

The value and from_payment_account_id must be different. Example: 16

amount   number     

Example: 4326.41688

transferred_on   string     

Must be a valid date. Example: 2026-01-15

reference   string  optional    

Must not be greater than 64 characters. Example: m

notes   string  optional    

Must not be greater than 255 characters. Example: i

Show eligible transactions and statement-reconciliation history.

requires authentication

Example request:
curl --request GET \
    --get "https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconciliation" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"statement_date\": \"2026-01-15\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconciliation"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "statement_date": "2026-01-15"
};

fetch(url, {
    method: "GET",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconciliation';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'statement_date' => '2026-01-15',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (401):

Show headers
cache-control: no-cache, private
content-type: application/json
x-content-type-options: nosniff
x-frame-options: DENY
referrer-policy: strict-origin-when-cross-origin
permissions-policy: camera=(), microphone=(), geolocation=()
cross-origin-opener-policy: same-origin
x-robots-tag: noindex, nofollow
strict-transport-security: max-age=31536000; includeSubDomains
access-control-allow-origin: *
 

{
    "message": "Unauthenticated."
}
 

Request      

GET api/v1/businesses/{business}/payment-accounts/{paymentAccount_id}/reconciliation

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

paymentAccount_id   integer     

The ID of the paymentAccount. Example: 1

Body Parameters

statement_date   string  optional    

Must be a valid date. Must be a date before or equal to today. Example: 2026-01-15

Complete and lock a statement reconciliation.

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconciliation" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"statement_date\": \"2026-01-15\",
    \"statement_balance\": -999999998,
    \"transaction_ids\": [
        16
    ],
    \"notes\": \"n\"
}"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconciliation"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "statement_date": "2026-01-15",
    "statement_balance": -999999998,
    "transaction_ids": [
        16
    ],
    "notes": "n"
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconciliation';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'statement_date' => '2026-01-15',
            'statement_balance' => -999999998,
            'transaction_ids' => [16],
            'notes' => 'n',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/payment-accounts/{paymentAccount_id}/reconciliation

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

paymentAccount_id   integer     

The ID of the paymentAccount. Example: 1

Body Parameters

statement_date   string     

Must be a valid date. Must be a date before or equal to today. Example: 2026-01-15

statement_balance   number     

Must be between -999999999 and 999999999. Example: -999999998

transaction_ids   integer[]  optional    
notes   string  optional    

Must not be greater than 255 characters. Example: n

POST api/v1/businesses/{business}/payment-accounts/{paymentAccount_id}/reconcile/{journalLine_id}

requires authentication

Example request:
curl --request POST \
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconcile/1" \
    --header "Authorization: Bearer {ACCESS_TOKEN}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
const url = new URL(
    "https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconcile/1"
);

const headers = {
    "Authorization": "Bearer {ACCESS_TOKEN}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};


fetch(url, {
    method: "POST",
    headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://dukanam.com/api/v1/businesses/1/payment-accounts/1/reconcile/1';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {ACCESS_TOKEN}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Request      

POST api/v1/businesses/{business}/payment-accounts/{paymentAccount_id}/reconcile/{journalLine_id}

Headers

Authorization        

Example: Bearer {ACCESS_TOKEN}

Content-Type        

Example: application/json

Accept        

Example: application/json

URL Parameters

business   integer     

The business. Example: 1

paymentAccount_id   integer     

The ID of the paymentAccount. Example: 1

journalLine_id   integer     

The ID of the journalLine. Example: 1