Skip to content

Buyers

This guide covers creating and managing buyers (customers) in InvoisX.

Setup

Before using the examples below, set up your HTTP client:

javascript
const API_URL = 'https://invoisx.com/api/v1';
const API_TOKEN = 'your-api-token';

async function api(method, endpoint, data = null) {
  const response = await fetch(`${API_URL}${endpoint}`, {
    method,
    headers: {
      'Authorization': `Bearer ${API_TOKEN}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: data ? JSON.stringify(data) : null,
  });
  return response.json();
}
python
import requests

API_URL = 'https://invoisx.com/api/v1'
API_TOKEN = 'your-api-token'

def api(method, endpoint, data=None):
    response = requests.request(
        method,
        f'{API_URL}{endpoint}',
        headers={
            'Authorization': f'Bearer {API_TOKEN}',
            'Accept': 'application/json',
        },
        json=data
    )
    return response.json()
php
use Illuminate\Support\Facades\Http;

$api = Http::withToken('your-api-token')
    ->accept('application/json')
    ->baseUrl('https://invoisx.com/api/v1');
php
use GuzzleHttp\Client;

$client = new Client([
    'base_uri' => 'https://invoisx.com/api/v1/',
    'headers' => [
        'Authorization' => 'Bearer your-api-token',
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ]
]);
java
import java.net.http.*;
import java.net.URI;

var client = HttpClient.newHttpClient();
var API_URL = "https://invoisx.com/api/v1";
var API_TOKEN = "your-api-token";

HttpResponse<String> api(String method, String endpoint, String jsonBody) throws Exception {
    var builder = HttpRequest.newBuilder()
        .uri(URI.create(API_URL + endpoint))
        .header("Authorization", "Bearer " + API_TOKEN)
        .header("Accept", "application/json")
        .header("Content-Type", "application/json");

    if (jsonBody != null) {
        builder.method(method, HttpRequest.BodyPublishers.ofString(jsonBody));
    } else {
        builder.method(method, HttpRequest.BodyPublishers.noBody());
    }
    return client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
}
csharp
using System.Net.Http;
using System.Text;
using System.Text.Json;

var client = new HttpClient {
    BaseAddress = new Uri("https://invoisx.com/api/v1")
};
client.DefaultRequestHeaders.Add("Authorization", "Bearer your-api-token");
client.DefaultRequestHeaders.Add("Accept", "application/json");

Overview

Buyers represent your customers who receive invoices. Each buyer has contact information, tax details, and address data required by LHDN for e-invoicing compliance.

Creating a Buyer

Complete Buyer

Create a buyer with all required LHDN fields:

javascript
const buyer = await api('POST', '/buyers', {
  name: 'Acme Corporation Sdn Bhd',
  tin: 'C12345678000',
  id_type: 'BRN',
  id_value: '202301012345',
  address: {
    line1: '123 Jalan Business',
    line2: 'Suite 456',
    city: 'Kuala Lumpur',
    state: '14',
    postCode: '50000',
    countryCode: 'MYS'
  },
  contact: {
    phone: '+60123456789',
    email: 'billing@acme.com.my'
  }
});

console.log(`Buyer ID: ${buyer.data.id}`);
console.log(`Is Ready: ${buyer.data.is_ready}`);
python
buyer = api('POST', '/buyers', {
    'name': 'Acme Corporation Sdn Bhd',
    'tin': 'C12345678000',
    'id_type': 'BRN',
    'id_value': '202301012345',
    'address': {
        'line1': '123 Jalan Business',
        'line2': 'Suite 456',
        'city': 'Kuala Lumpur',
        'state': '14',
        'postCode': '50000',
        'countryCode': 'MYS'
    },
    'contact': {
        'phone': '+60123456789',
        'email': 'billing@acme.com.my'
    }
})

print(f"Buyer ID: {buyer['data']['id']}")
print(f"Is Ready: {buyer['data']['is_ready']}")
php
$buyer = $api->post('/buyers', [
    'name' => 'Acme Corporation Sdn Bhd',
    'tin' => 'C12345678000',
    'id_type' => 'BRN',
    'id_value' => '202301012345',
    'address' => [
        'line1' => '123 Jalan Business',
        'line2' => 'Suite 456',
        'city' => 'Kuala Lumpur',
        'state' => '14',
        'postCode' => '50000',
        'countryCode' => 'MYS'
    ],
    'contact' => [
        'phone' => '+60123456789',
        'email' => 'billing@acme.com.my'
    ]
]);

echo "Buyer ID: {$buyer->json('data.id')}\n";
echo "Is Ready: " . ($buyer->json('data.is_ready') ? 'Yes' : 'No') . "\n";
php
$response = $client->post('buyers', ['json' => [
    'name' => 'Acme Corporation Sdn Bhd',
    'tin' => 'C12345678000',
    'id_type' => 'BRN',
    'id_value' => '202301012345',
    'address' => [
        'line1' => '123 Jalan Business',
        'line2' => 'Suite 456',
        'city' => 'Kuala Lumpur',
        'state' => '14',
        'postCode' => '50000',
        'countryCode' => 'MYS'
    ],
    'contact' => [
        'phone' => '+60123456789',
        'email' => 'billing@acme.com.my'
    ]
]]);
$buyer = json_decode($response->getBody(), true);

echo "Buyer ID: {$buyer['data']['id']}\n";
echo "Is Ready: " . ($buyer['data']['is_ready'] ? 'Yes' : 'No') . "\n";
java
var json = """
    {
        "name": "Acme Corporation Sdn Bhd",
        "tin": "C12345678000",
        "id_type": "BRN",
        "id_value": "202301012345",
        "address": {
            "line1": "123 Jalan Business",
            "line2": "Suite 456",
            "city": "Kuala Lumpur",
            "state": "14",
            "postCode": "50000",
            "countryCode": "MYS"
        },
        "contact": {
            "phone": "+60123456789",
            "email": "billing@acme.com.my"
        }
    }""";
var response = api("POST", "/buyers", json);
// Parse response.body() to get buyer ID
csharp
var buyer = new {
    name = "Acme Corporation Sdn Bhd",
    tin = "C12345678000",
    id_type = "BRN",
    id_value = "202301012345",
    address = new {
        line1 = "123 Jalan Business",
        line2 = "Suite 456",
        city = "Kuala Lumpur",
        state = "14",
        postCode = "50000",
        countryCode = "MYS"
    },
    contact = new {
        phone = "+60123456789",
        email = "billing@acme.com.my"
    }
};

var response = await client.PostAsJsonAsync("/buyers", buyer);
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
var buyerId = result.GetProperty("data").GetProperty("id").GetString();
Console.WriteLine($"Buyer ID: {buyerId}");

Draft Buyer

Create a buyer with minimal data for B2C scenarios. Draft buyers only require a name:

javascript
const draftBuyer = await api('POST', '/buyers', {
  name: 'Walk-in Customer',
  is_draft: true
});

// is_ready will be false
console.log(`Is Ready: ${draftBuyer.data.is_ready}`); // false
python
draft_buyer = api('POST', '/buyers', {
    'name': 'Walk-in Customer',
    'is_draft': True
})

# is_ready will be false
print(f"Is Ready: {draft_buyer['data']['is_ready']}")  # False
php
$draftBuyer = $api->post('/buyers', [
    'name' => 'Walk-in Customer',
    'is_draft' => true
]);

// is_ready will be false
echo "Is Ready: " . ($draftBuyer->json('data.is_ready') ? 'Yes' : 'No') . "\n";
php
$response = $client->post('buyers', ['json' => [
    'name' => 'Walk-in Customer',
    'is_draft' => true
]]);
$draftBuyer = json_decode($response->getBody(), true);

// is_ready will be false
echo "Is Ready: " . ($draftBuyer['data']['is_ready'] ? 'Yes' : 'No') . "\n";
java
var json = """
    {"name": "Walk-in Customer", "is_draft": true}""";
var response = api("POST", "/buyers", json);
// is_ready will be false
csharp
var draftBuyer = new { name = "Walk-in Customer", is_draft = true };
var response = await client.PostAsJsonAsync("/buyers", draftBuyer);
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
// is_ready will be false
Console.WriteLine($"Is Ready: {result.GetProperty("data").GetProperty("is_ready")}");

Draft Buyer with Partial Data

You can also create a draft with some fields populated:

json
{
  "name": "Partial Customer",
  "is_draft": true,
  "id_type": "PASSPORT",
  "id_value": "A12345678",
  "contact": {
    "phone": "+60123456789",
    "email": "customer@example.com"
  }
}

Completing Draft Buyers

Use the update endpoint to add missing information and make the buyer ready for LHDN submission.

Understanding is_ready

The is_ready field indicates whether a buyer has complete data for LHDN submission:

StatusDescription
is_ready: trueBuyer has TIN, ID type, and ID value - ready for LHDN
is_ready: falseMissing required fields - invoice submission will fail

ID Types

TypeDescriptionExample
BRNBusiness Registration Number202301012345 (SSM)
NRICMalaysian Identity Card901231145678 (MyKad)
PASSPORTPassport NumberA12345678
ARMYMilitary IDArmy ID number

Foreign Buyers

For buyers outside Malaysia, use state code 17 and the appropriate country code:

javascript
const foreignBuyer = await api('POST', '/buyers', {
  name: 'Singapore Trading Pte Ltd',
  tin: 'EI00000000020',
  id_type: 'PASSPORT',
  id_value: 'S1234567A',
  address: {
    line1: '123 Orchard Road',
    city: 'Singapore',
    state: '17',           // Non-Malaysian
    postCode: '238867',
    countryCode: 'SGP'    // Singapore
  },
  contact: {
    phone: '+6591234567',
    email: 'billing@singapore-trading.sg'
  }
});
python
foreign_buyer = api('POST', '/buyers', {
    'name': 'Singapore Trading Pte Ltd',
    'tin': 'EI00000000020',
    'id_type': 'PASSPORT',
    'id_value': 'S1234567A',
    'address': {
        'line1': '123 Orchard Road',
        'city': 'Singapore',
        'state': '17',           # Non-Malaysian
        'postCode': '238867',
        'countryCode': 'SGP'    # Singapore
    },
    'contact': {
        'phone': '+6591234567',
        'email': 'billing@singapore-trading.sg'
    }
})
php
$foreignBuyer = $api->post('/buyers', [
    'name' => 'Singapore Trading Pte Ltd',
    'tin' => 'EI00000000020',
    'id_type' => 'PASSPORT',
    'id_value' => 'S1234567A',
    'address' => [
        'line1' => '123 Orchard Road',
        'city' => 'Singapore',
        'state' => '17',           // Non-Malaysian
        'postCode' => '238867',
        'countryCode' => 'SGP'    // Singapore
    ],
    'contact' => [
        'phone' => '+6591234567',
        'email' => 'billing@singapore-trading.sg'
    ]
]);
php
$response = $client->post('buyers', ['json' => [
    'name' => 'Singapore Trading Pte Ltd',
    'tin' => 'EI00000000020',
    'id_type' => 'PASSPORT',
    'id_value' => 'S1234567A',
    'address' => [
        'line1' => '123 Orchard Road',
        'city' => 'Singapore',
        'state' => '17',           // Non-Malaysian
        'postCode' => '238867',
        'countryCode' => 'SGP'    // Singapore
    ],
    'contact' => [
        'phone' => '+6591234567',
        'email' => 'billing@singapore-trading.sg'
    ]
]]);
$foreignBuyer = json_decode($response->getBody(), true);
java
var json = """
    {
        "name": "Singapore Trading Pte Ltd",
        "tin": "EI00000000020",
        "id_type": "PASSPORT",
        "id_value": "S1234567A",
        "address": {
            "line1": "123 Orchard Road",
            "city": "Singapore",
            "state": "17",
            "postCode": "238867",
            "countryCode": "SGP"
        },
        "contact": {
            "phone": "+6591234567",
            "email": "billing@singapore-trading.sg"
        }
    }""";
var response = api("POST", "/buyers", json);
csharp
var foreignBuyer = new {
    name = "Singapore Trading Pte Ltd",
    tin = "EI00000000020",
    id_type = "PASSPORT",
    id_value = "S1234567A",
    address = new {
        line1 = "123 Orchard Road",
        city = "Singapore",
        state = "17",           // Non-Malaysian
        postCode = "238867",
        countryCode = "SGP"    // Singapore
    },
    contact = new {
        phone = "+6591234567",
        email = "billing@singapore-trading.sg"
    }
};
var response = await client.PostAsJsonAsync("/buyers", foreignBuyer);

Listing Buyers

javascript
// List all buyers
const buyers = await api('GET', '/buyers');

// With search
const filtered = await api('GET', '/buyers?search=Acme&per_page=10');
python
# List all buyers
buyers = api('GET', '/buyers')

# With search
filtered = api('GET', '/buyers?search=Acme&per_page=10')
php
// List all buyers
$buyers = $api->get('/buyers');

// With search
$filtered = $api->get('/buyers', [
    'search' => 'Acme',
    'per_page' => 10
]);
php
// List all buyers
$response = $client->get('buyers');
$buyers = json_decode($response->getBody(), true);

// With search
$response = $client->get('buyers', ['query' => [
    'search' => 'Acme',
    'per_page' => 10
]]);
$filtered = json_decode($response->getBody(), true);
java
// List all buyers
var response = api("GET", "/buyers", null);

// With search
var filtered = api("GET", "/buyers?search=Acme&per_page=10", null);
csharp
// List all buyers
var response = await client.GetAsync("/buyers");
var buyers = await response.Content.ReadFromJsonAsync<JsonElement>();

// With search
var filtered = await client.GetAsync("/buyers?search=Acme&per_page=10");

Getting a Single Buyer

javascript
const buyer = await api('GET', `/buyers/${buyerId}`);
python
buyer = api('GET', f'/buyers/{buyer_id}')
php
$buyer = $api->get("/buyers/$buyerId");
php
$response = $client->get("buyers/$buyerId");
$buyer = json_decode($response->getBody(), true);
java
var response = api("GET", "/buyers/" + buyerId, null);
csharp
var response = await client.GetAsync($"/buyers/{buyerId}");
var buyer = await response.Content.ReadFromJsonAsync<JsonElement>();

Updating a Buyer

Nested Objects

When updating nested objects (address, contact), you must send the complete object. Partial updates will replace the entire object.

javascript
const updated = await api('PUT', `/buyers/${buyerId}`, {
  name: 'Acme Corporation Sdn Bhd (Updated)',
  contact: {
    phone: '+60198765432',
    email: 'accounts@acme.com.my'
  }
});
python
updated = api('PUT', f'/buyers/{buyer_id}', {
    'name': 'Acme Corporation Sdn Bhd (Updated)',
    'contact': {
        'phone': '+60198765432',
        'email': 'accounts@acme.com.my'
    }
})
php
$updated = $api->put("/buyers/$buyerId", [
    'name' => 'Acme Corporation Sdn Bhd (Updated)',
    'contact' => [
        'phone' => '+60198765432',
        'email' => 'accounts@acme.com.my'
    ]
]);
php
$response = $client->put("buyers/$buyerId", ['json' => [
    'name' => 'Acme Corporation Sdn Bhd (Updated)',
    'contact' => [
        'phone' => '+60198765432',
        'email' => 'accounts@acme.com.my'
    ]
]]);
$updated = json_decode($response->getBody(), true);
java
var json = """
    {
        "name": "Acme Corporation Sdn Bhd (Updated)",
        "contact": {
            "phone": "+60198765432",
            "email": "accounts@acme.com.my"
        }
    }""";
var response = api("PUT", "/buyers/" + buyerId, json);
csharp
var update = new {
    name = "Acme Corporation Sdn Bhd (Updated)",
    contact = new {
        phone = "+60198765432",
        email = "accounts@acme.com.my"
    }
};
var response = await client.PutAsJsonAsync($"/buyers/{buyerId}", update);

Completing a Draft Buyer

javascript
const completed = await api('PUT', `/buyers/${buyerId}`, {
  tin: 'C12345678000',
  id_type: 'BRN',
  id_value: '202301012345',
  address: {
    line1: '123 Jalan Business',
    city: 'Kuala Lumpur',
    state: '14',
    countryCode: 'MYS'
  },
  contact: {
    phone: '+60123456789'
  }
});

// is_ready should now be true
console.log(`Is Ready: ${completed.data.is_ready}`);
python
completed = api('PUT', f'/buyers/{buyer_id}', {
    'tin': 'C12345678000',
    'id_type': 'BRN',
    'id_value': '202301012345',
    'address': {
        'line1': '123 Jalan Business',
        'city': 'Kuala Lumpur',
        'state': '14',
        'countryCode': 'MYS'
    },
    'contact': {
        'phone': '+60123456789'
    }
})

# is_ready should now be true
print(f"Is Ready: {completed['data']['is_ready']}")
php
$completed = $api->put("/buyers/$buyerId", [
    'tin' => 'C12345678000',
    'id_type' => 'BRN',
    'id_value' => '202301012345',
    'address' => [
        'line1' => '123 Jalan Business',
        'city' => 'Kuala Lumpur',
        'state' => '14',
        'countryCode' => 'MYS'
    ],
    'contact' => [
        'phone' => '+60123456789'
    ]
]);

// is_ready should now be true
echo "Is Ready: " . ($completed->json('data.is_ready') ? 'Yes' : 'No') . "\n";
php
$response = $client->put("buyers/$buyerId", ['json' => [
    'tin' => 'C12345678000',
    'id_type' => 'BRN',
    'id_value' => '202301012345',
    'address' => [
        'line1' => '123 Jalan Business',
        'city' => 'Kuala Lumpur',
        'state' => '14',
        'countryCode' => 'MYS'
    ],
    'contact' => [
        'phone' => '+60123456789'
    ]
]]);
$completed = json_decode($response->getBody(), true);

// is_ready should now be true
echo "Is Ready: " . ($completed['data']['is_ready'] ? 'Yes' : 'No') . "\n";
java
var json = """
    {
        "tin": "C12345678000",
        "id_type": "BRN",
        "id_value": "202301012345",
        "address": {
            "line1": "123 Jalan Business",
            "city": "Kuala Lumpur",
            "state": "14",
            "countryCode": "MYS"
        },
        "contact": {
            "phone": "+60123456789"
        }
    }""";
var response = api("PUT", "/buyers/" + buyerId, json);
// is_ready should now be true
csharp
var complete = new {
    tin = "C12345678000",
    id_type = "BRN",
    id_value = "202301012345",
    address = new {
        line1 = "123 Jalan Business",
        city = "Kuala Lumpur",
        state = "14",
        countryCode = "MYS"
    },
    contact = new {
        phone = "+60123456789"
    }
};
var response = await client.PutAsJsonAsync($"/buyers/{buyerId}", complete);
var result = await response.Content.ReadFromJsonAsync<JsonElement>();
// is_ready should now be true
Console.WriteLine($"Is Ready: {result.GetProperty("data").GetProperty("is_ready")}");

Deleting a Buyer

WARNING

Buyers with existing invoices cannot be deleted.

javascript
await api('DELETE', `/buyers/${buyerId}`);
python
api('DELETE', f'/buyers/{buyer_id}')
php
$api->delete("/buyers/$buyerId");
php
$client->delete("buyers/$buyerId");
java
api("DELETE", "/buyers/" + buyerId, null);
csharp
await client.DeleteAsync($"/buyers/{buyerId}");

Buyer Fields Reference

Required Fields

FieldTypeDescription
namestringBusiness or individual name
tinstringTax Identification Number
id_typestringBRN, NRIC, PASSPORT, or ARMY
id_valuestringID number matching the type
address.line1stringStreet address
address.citystringCity name
address.statestringState code (from /states)
address.countryCodestringCountry code (from /countries)
contact.phonestringPhone (E.164 format)

Optional Fields

FieldTypeDescription
address.line2stringAddress line 2
address.line3stringAddress line 3
address.postCodestringPostal code
contact.emailstringEmail address
contact.namestringContact person name
contact.faxstringFax number (E.164 format)
is_draftbooleanCreate as draft buyer

Error Handling

StatusErrorDescription
401UNAUTHORIZEDInvalid or missing token
403FORBIDDENNo access to this company
404NOT_FOUNDBuyer doesn't exist
422VALIDATION_ERRORInvalid data provided
409CONFLICTCannot delete buyer with invoices

Next Steps

InvoisX - Malaysia's Leading e-Invoice Platform