SysAd API
ID EN

API Documentation, original version original

A guide to reading and writing SysAd data over HTTP. Every example can be copied and tried right away.

You are reading the original version. For new integrations, use version 1: its addresses are cleaner and the product filter works fully. The original version keeps working for applications that already use it.
Type your API server address, e.g. https://api.perusahaan.co.id. All examples on this page update and can be copied right away.

Start here

Reading SysAd data through the API takes two stages: log in to get a token, then use that token on every data request. Logging in takes four short steps:

  1. Check accountSend user and pass. You get a list of companies.
  2. Choose entitySend the company id. You get a list of entities.
  3. Choose yearSend the company and entity ids. You get a list of years.
  4. Request tokenSend all the choices above. You get a token.
Already know your company, entity, and year ids? You can go straight to step 4 (request token). Steps 1 to 3 only help you find the ids that apply to your account.

Once you have a token, send it in the token header on every data request. Example: retrieving client data:

curl "https://your-server-address/api/mst/getclient" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"cust_id":["C00001","C00002"]}]'

Basic concepts

Authentication

  • Login uses the user and pass headers (plus other ids, depending on the step).
  • Data requests use the token header. A token is valid for 1 day and is bound to the company, entity, and year you chose.
  • Use HTTPS when available, because the user, password, and token travel in headers.

Reading and writing

  • GET endpoints only read data.
  • POST/PUT endpoints change data and can only be used by API accounts that have been granted write permission. Without it the server answers HTTP 403.

Filtering data (options)

Most data endpoints accept options to narrow the results. The rules are the same everywhere:

  • options is a JSON array containing one object: [{ ... }]. Only the first object is read.
  • Each filter is an array of values, e.g. "cust_id": ["C00001", "C00002"]. Data matching any one of the values is included (OR).
  • When you use several filters at once, data must match all of them (AND).
  • A filter that is omitted or is an empty array means no restriction. [{}] means all data.
  • Values must be exactly the same as the codes in SysAd (letter case and spaces).
How to send it (original version). options is sent as an HTTP header named options containing one-line JSON, e.g. options: [{"cust_id":["C00001"]}].

Response format

Every endpoint answers with JSON in the same shape:

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [ ... ]
}
PartMeaning
successtrue on success, false on failure. Always check this value, not just the HTTP status.
messageSuccess on success. On failure, a short reason in English.
resultThe data. Usually an array of objects (one object per row). It can be an empty array [] when nothing matches.
codeKept for compatibility. Use success and message.

Status codes and messages

Login or permission failures are signaled with an HTTP 4xx status. Failures on data are signaled with success: false (the HTTP status stays 200).

HTTP statusMessageMeaning and what to do
401Invalid credentialWrong username or password.
401Invalid tokenThe token is missing, damaged, or not recognized. Request a new token.
401Token expiredThe token is more than 1 day old. Request a new token.
401User expiredThe account has expired. Contact the administrator.
403User is not registered to use APIThe account has not been registered to use the API. Contact the administrator.
403... don't have access to create or modify dataThe account does not have write permission. Contact the administrator.
429(empty)Too many login attempts from one IP address (default maximum 10 per minute; the administrator can change this). Wait 1 minute.
200Invalid tokenOn data endpoints this can mean the content of options is not valid JSON. Check its shape.
200Failed when getting data requested.The data request failed (e.g. the filter does not fit). Check the filter. If it keeps happening, contact the administrator and mention the time it occurred.
200Invalid optionsThe content of options is incomplete or has the wrong shape.
Note. Technical details of server errors are intentionally not shown in the response, for security. Administrators can see them in the server log.

Release notes

Security update

For applications that already use this API, a few things need adjusting:

  • Old tokens are no longer valid. The token format changed. Request a new token through /api/lgn/token.
  • The pass header is required on /api/lgn/entity and /api/lgn/year too (previously only user).
  • Error statuses are now HTTP 401 or 403, not 200 after a redirect. The JSON body is unchanged.
  • Login limit per IP address (default 10 requests per minute; HTTP 429 when exceeded).
  • Shorter error messages. Technical details exist only in the server log.
  • Filters are always parameterized. Quotes and special characters in filter values are now safe and treated as plain text.

Approval data changes

  • Plan approvals are now stored as a JSON array (name, role, time for each approver) and read from the same place in both the Media plan list and Approve a media plan.
  • The app_by column in plan responses now contains that JSON array (as text). The app_date column is removed; the approval time is inside the array (time).

Endpoint reference

Login

The four first steps to get a token. Shared by all versions.

GET /api/lgn/me Check account and list companies

Verifies your username and password, then returns the companies you are allowed to access. This is the first step every time you log in. Requires user and pass headers

Note. The result can be empty if the account has not been given access to any company.

What to send

NameSent viaTypeRequiredDescription
userHeaderstringrequiredYour SysAd login username.
passHeaderstringrequiredThe password of that account.

Example request

curl "https://your-server-address/api/lgn/me" \
  -H "user: your_username" \
  -H "pass: your_password"
const response = await fetch(
  "https://your-server-address/api/lgn/me",
  {
    method: "GET",
    headers: {
      "user": "your_username",
      "pass": "your_password"
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "Id": "1",
      "Name": "AGENCY",
      "Value": "1",
      "Text": "Sample Media Ltd"
    }
  ]
}

Meaning of result

ColumnTypeDescription
IdstringCompany id. Used as the id header (entity step) and the comp header (year and token steps).
NamestringShort company code.
ValuestringSame as Id (for dropdown components).
TextstringCompany name to show to users.
GET /api/lgn/entity List entities in a company

Returns the entities (business units) of the company you selected. The company id comes from the previous step. Requires user and pass headers

Heads up. Since the security update, the pass header is required on this endpoint. Previously only user and id were needed.

What to send

NameSent viaTypeRequiredDescription
userHeaderstringrequiredYour SysAd login username.
passHeaderstringrequiredThe password of that account.
idHeaderstringrequiredCompany id, from the /api/lgn/me result (field Id).

Example request

curl "https://your-server-address/api/lgn/entity" \
  -H "user: your_username" \
  -H "pass: your_password" \
  -H "id: 1"
const response = await fetch(
  "https://your-server-address/api/lgn/entity",
  {
    method: "GET",
    headers: {
      "user": "your_username",
      "pass": "your_password",
      "id": "1"
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "Id": "DEMO",
      "Name": "DEMO-KOL.ADD",
      "Value": "DEMO",
      "Text": "DEMO"
    }
  ]
}

Meaning of result

ColumnTypeDescription
IdstringEntity id. Used as the ent header in the next steps.
NamestringEntity configuration file name.
ValuestringSame as Id.
TextstringEntity name to display.
GET /api/lgn/year List data years

Returns the years that have data for the selected entity, newest first. Requires user and pass headers

Heads up. Since the security update, the pass header is required on this endpoint.

What to send

NameSent viaTypeRequiredDescription
userHeaderstringrequiredYour SysAd login username.
passHeaderstringrequiredThe password of that account.
compHeaderstringrequiredCompany id, from /api/lgn/me.
entHeaderstringrequiredEntity id, from /api/lgn/entity.

Example request

curl "https://your-server-address/api/lgn/year" \
  -H "user: your_username" \
  -H "pass: your_password" \
  -H "comp: 1" \
  -H "ent: DEMO"
const response = await fetch(
  "https://your-server-address/api/lgn/year",
  {
    method: "GET",
    headers: {
      "user": "your_username",
      "pass": "your_password",
      "comp": "1",
      "ent": "DEMO"
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "Id": 2026,
      "Year": "2026",
      "Value": "2026",
      "Text": "2026"
    },
    {
      "Id": 2025,
      "Year": "2025",
      "Value": "2025",
      "Text": "2025"
    }
  ]
}

Meaning of result

ColumnTypeDescription
IdnumberThe year as a number.
YearstringThe year as text. Sent as the year header when requesting a token.
ValuestringSame as Year.
TextstringYear to display.
GET /api/lgn/token Request an access token

Exchanges your credentials and your company/entity/year choice for a token. Send this token in the token header on every data endpoint. Requires user and pass headers

Note. A token is valid for 1 day. After that, request a new one with the same steps.
Note. A token is bound to the company, entity, and year you chose. Changing any of them means requesting a new token.
Heads up. Treat the token like a password: do not put it in URLs, public code, or logs.
Heads up. Login endpoints are limited per IP address (default 10 requests per minute; the administrator can change this). Beyond that the server answers HTTP 429; wait 1 minute.

What to send

NameSent viaTypeRequiredDescription
userHeaderstringrequiredYour SysAd login username.
passHeaderstringrequiredThe password of that account.
compHeaderstringrequiredCompany id, from /api/lgn/me.
entHeaderstringrequiredEntity id, from /api/lgn/entity.
yearHeaderstringrequired4-digit data year, from /api/lgn/year.

Example request

curl "https://your-server-address/api/lgn/token" \
  -H "user: your_username" \
  -H "pass: your_password" \
  -H "comp: 1" \
  -H "ent: DEMO" \
  -H "year: 2026"
const response = await fetch(
  "https://your-server-address/api/lgn/token",
  {
    method: "GET",
    headers: {
      "user": "your_username",
      "pass": "your_password",
      "comp": "1",
      "ent": "DEMO",
      "year": "2026"
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": "eyJ1c2VyIjoi...(long string)..."
}

Meaning of result

ColumnTypeDescription
resultstringThe access token (a long string). Copy it as is; do not modify it.

Master

Reference data: clients, vendors, products, budgets, employees, and the job structure.

GET /api/mst/getclient Client data

Retrieves the list of clients (customers). Requires the token header

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
cust_id array of string optional Client code to look up. Leave empty or omit to apply no restriction. C00001
[{ "cust_id": ["C00001", "C00002"] }]

Example request

curl "https://your-server-address/api/mst/getclient" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"cust_id":["C00001","C00002"]}]'
const options = [{"cust_id":["C00001","C00002"]}];

const response = await fetch(
  "https://your-server-address/api/mst/getclient",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "id": "C00001",
      "name": "Sample Client Ltd",
      "tax_name": "Sample Client Ltd",
      "ini": "PCK",
      "address": "1 Sample St.",
      "address2": "South Jakarta",
      "npwp": "01.234.567.8-901.000",
      "vatno": "",
      "pkp": "",
      "active": true
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
idstringClient code.
namestringClient name.
tax_namestringName used for tax/invoicing.
inistringClient initials.
addressstringAddress line 1.
address2stringAddress line 2.
npwpstringTax number (NPWP).
vatnostringVAT number.
pkpstringPKP data (VAT-registered taxable entrepreneur).
activebooleantrue if the client is active.
GET /api/mst/getvendor Vendor data

Retrieves the list of vendors (suppliers/media). Requires the token header

Note. There can be a very large number of vendors. Without a filter, all vendors are returned at once, so use vendor_id when you only need a few.

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
vendor_id array of string optional Vendor code to look up. Leave empty or omit to apply no restriction. V00001
[{ "vendor_id": ["V00001"] }]

Example request

curl "https://your-server-address/api/mst/getvendor" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"vendor_id":["V00001"]}]'
const options = [{"vendor_id":["V00001"]}];

const response = await fetch(
  "https://your-server-address/api/mst/getvendor",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "id": "V00001",
      "name": "Sample Vendor Ltd",
      "type": "TV",
      "tax_name": "Sample Vendor Ltd",
      "address": "2 Sample St.",
      "address2": "",
      "npwp": "",
      "vatno": "",
      "pkp": "",
      "active": true
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
idstringVendor code.
namestringVendor name.
typestringVendor type.
tax_namestringName used for tax/invoicing.
addressstringAddress line 1.
address2stringAddress line 2.
npwpstringTax number (NPWP).
vatnostringVAT number.
pkpstringPKP data.
activebooleantrue if the vendor is active.
GET /api/mst/getproduct Product (brand) data

Retrieves the list of products together with the client that owns each one. Requires the token header

Heads up. The original version ignores filters and always returns all products. Use the v1 endpoint if you need filtering.

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.

Example request

curl "https://your-server-address/api/mst/getproduct" \
  -H "token: YOUR_TOKEN"
const response = await fetch(
  "https://your-server-address/api/mst/getproduct",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN"
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "cust_id": "C00001",
      "cust_name": "Sample Client Ltd",
      "id": "PRD001",
      "name": "Sample Product",
      "ini": "PRC",
      "adv": "Sample Client Ltd",
      "cat": null
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
cust_idstringCode of the client that owns the product.
cust_namestringClient name.
idstringProduct code.
namestringProduct name.
inistringProduct initials.
advstringAdvertiser.
catstringProduct category. May be empty (null).
GET /api/mst/getbudget Budget data

Retrieves the budgets for the year chosen when the token was requested. Requires the token header

Heads up. The budget table is read per year, according to the year in the token.

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
cust_id array of string optional Client code. Leave empty or omit to apply no restriction. C00001
product_id array of string optional Product code. Leave empty or omit to apply no restriction. PRD001
budget_id array of string optional Budget number. Leave empty or omit to apply no restriction. BD260001
[{ "budget_id": ["BD260001"] }]

Example request

curl "https://your-server-address/api/mst/getbudget" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"budget_id":["BD260001"]}]'
const options = [{"budget_id":["BD260001"]}];

const response = await fetch(
  "https://your-server-address/api/mst/getbudget",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "cust_id": "C00001",
      "cust_name": "Sample Client Ltd",
      "product_id": "PRD001",
      "product_name": "Sample Product",
      "budget_id": "BD260001",
      "media": "T",
      "desc": "Q1 campaign",
      "amount": 1000000000
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
cust_idstringClient code.
cust_namestringClient name.
product_idstringProduct code.
product_namestringProduct name.
budget_idstringBudget number.
mediastringMedia type (media code letter).
descstringBudget description.
amountnumberTotal yearly budget value, calculated according to the budget type (Original/Forecast/CPRP).
POST /api/mst/createbudget Create a new budget

Creates one new budget for a product. The budget number is generated automatically and returned in the response. Requires the token header; the account must have write permission

Note. Only the first object in the array is processed. To create several budgets, send one request per budget.
Heads up. Requires an API account that has been granted write permission. Without it the server answers HTTP 403.
Heads up. If po_id is already used, the response has success:false, the message "PO Id ... already exist.", and result contains the existing budget.

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
po_id string required Client PO number. Must not match an existing budget. PO-2026-001
product_id string required Product code in SysAd. PRD001
period string required Campaign period date, format yyyy-MM-dd. Use the same year as the year in the token. 2026-03-15
media string required Media placement: MT (television), MP (print), MR (radio), MC (cinema), MD (digital), MO (other). MT
value number optional PO value. Treated as 0 if omitted. 1000000
type string optional Budget type: O (original), F (forecast), C (CPRP), I (internal). Treated as O if omitted. O
desc string optional Budget description. Q1 campaign
[{ "po_id": "PO-2026-001", "product_id": "PRD001", "period": "2026-03-15", "media": "MT", "value": 1000000, "type": "O", "desc": "Q1 campaign" }]

Example request

curl -X POST "https://your-server-address/api/mst/createbudget" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"po_id":"PO-2026-001","product_id":"PRD001","period":"2026-03-15","media":"MT","value":1000000,"type":"O","desc":"Q1 campaign"}]'
const options = [{"po_id":"PO-2026-001","product_id":"PRD001","period":"2026-03-15","media":"MT","value":1000000,"type":"O","desc":"Q1 campaign"}];

const response = await fetch(
  "https://your-server-address/api/mst/createbudget",
  {
    method: "POST",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "budget_id": "BD260001"
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
budget_idstringThe newly created budget number.
GET /api/mst/getemployee Employee data

Retrieves the list of employees. Requires the token header

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
emp_id array of string optional Employee id to look up. Leave empty or omit to apply no restriction. 180001
[{ "emp_id": ["180001"] }]

Example request

curl "https://your-server-address/api/mst/getemployee" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"emp_id":["180001"]}]'
const options = [{"emp_id":["180001"]}];

const response = await fetch(
  "https://your-server-address/api/mst/getemployee",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "id": "180001",
      "ini": "BS",
      "name": "Budi Santoso",
      "rate": 0,
      "dept": "H-3",
      "email": "budi@contoh.co.id",
      "group": "",
      "occupation": "Media Planner"
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
idstringEmployee id.
inistringInitials.
namestringEmployee name.
ratenumberEmployee rate.
deptstringDepartment.
emailstringEmail address.
groupstringGroup.
occupationstringJob title.
GET /api/mst/getchart Job structure (chart)

Retrieves the job structure as a tree. The parent-child relationship is read from the parent column, which points to id. Requires the token header

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
chart_id array of string optional Job id (a number, sent as text). Leave empty or omit to apply no restriction. 10
[{ "chart_id": ["10"] }]

Example request

curl "https://your-server-address/api/mst/getchart" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"chart_id":["10"]}]'
const options = [{"chart_id":["10"]}];

const response = await fetch(
  "https://your-server-address/api/mst/getchart",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "id": 10,
      "parent": 16,
      "name": "MEDIA DIRECTOR",
      "color": 16744703,
      "occupation": ""
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
idnumberJob id.
parentnumberId of the parent job.
namestringJob name.
colornumberColor code on the chart.
occupationstringJob description.

Media

Media plans and their approval.

GET /api/mda/getplan Media plan list

Retrieves the list of media plans together with their order, approval, and invoice status. Requires the token header

Note. All options are optional. Without a filter, all plans for the token's year are returned.

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
from string optional Start date of the data period, format yyyy-MM-dd. Leave empty for no restriction. 2026-01-01
to string optional End date of the data period, format yyyy-MM-dd. Leave empty for no restriction. 2026-12-31
cust_id array of string optional Client code. Leave empty or omit to apply no restriction. C00001
product_id array of string optional Product code. Leave empty or omit to apply no restriction. PRD001
po_id array of string optional Client PO number. Leave empty or omit to apply no restriction. PO-2026-001
budget_id array of string optional Budget number. Leave empty or omit to apply no restriction. BD260001
plan_id array of string optional Plan number, format PLANNO/TYPE.GROUP. Leave empty or omit to apply no restriction. AAA.260001/T.01
[{ "from": "2026-01-01", "to": "2026-12-31", "cust_id": ["C00001"] }]

Example request

curl "https://your-server-address/api/mda/getplan" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"from":"2026-01-01","to":"2026-12-31","cust_id":["C00001"]}]'
const options = [{"from":"2026-01-01","to":"2026-12-31","cust_id":["C00001"]}];

const response = await fetch(
  "https://your-server-address/api/mda/getplan",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "plan_id": "AAA.260001/T.01",
      "cust_id": "C00001",
      "cust": "Sample Client Ltd",
      "product_id": "PRD001",
      "product": "Sample Product",
      "p_date": null,
      "desc": "Q1 campaign",
      "from": "2026-03-01",
      "to": "2026-03-31",
      "ref": "PO-2026-001",
      "budget_id": "BD260001",
      "app_by": "[{\"name\":\"Budi\",\"role\":\"Manager\",\"time\":\"2026-03-02 10:15:00\"}]",
      "order_id": "",
      "order_date": "",
      "ins": 120,
      "value": 500000000,
      "paid": 450000000,
      "nett": 425000000,
      "media": "T",
      "adtype_id": "",
      "mcs_id": "",
      "mcs_date": "",
      "ar_id": "",
      "ar_date": null,
      "status": "",
      "fee": 15
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
plan_idstringPlan number, format PLANNO/TYPE.GROUP, e.g. AAA.260001/T.01 (T = television, group 01).
cust_idstringClient code.
custstringClient name.
product_idstringProduct code.
productstringProduct name.
p_datestringPlan date. May be empty (null).
descstringPlan description.
fromstringStart of the airing period.
tostringEnd of the airing period.
refstringClient reference/PO number.
budget_idstringBudget number used.
app_bystringApproval data as a JSON array (text) with the name, role, and time of each approver. Empty if not yet approved.
order_idstringMedia order number.
order_datestringOrder date.
insnumberNumber of insertions/airings.
valuenumberGross value.
paidnumberAmount paid.
nettnumberNett value.
mediastringMedia type.
adtype_idstringAd type.
mcs_idstringMCS number.
mcs_datestringMCS date.
ar_idstringInvoice (receivable) number.
ar_datestringInvoice date. May be empty (null).
statusstringPlan status.
feenumberAgency fee.
periodstringData period.
POST /api/mda/approveplan Approve a media plan

Records the approval of one or more plans. Approval data is stored as a JSON array containing the name, role, and time of each approver. Requires the token header; the account must have write permission

Note. The app_by value you send replaces any previous approval data on that plan. For several approvers, put them all in one array.
Note. The same approval data can be read back through the plan list (app_by column).
Heads up. Requires an API account that has been granted write permission. Without it the server answers HTTP 403.
Heads up. app_by must be text (a string) whose content is JSON, not a raw JSON array. If the format is wrong, the response has success:false with the message "app_by must be in correct json format." or names the missing property (name, role, or time).

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
plan_id string required Plan number, format PLANNO/TYPE.GROUP. AAA.260001/T.01
app_by string required Text containing a JSON array. Each item has name (approver name), role (job title), and time (time, yyyy-MM-dd HH:mm:ss). [{"name":"Budi","role":"Manager","time":"2026-03-02 10:15:00"}]
[{ "plan_id": "AAA.260001/T.01", "app_by": "[{\"name\":\"Budi\",\"role\":\"Manager\",\"time\":\"2026-03-02 10:15:00\"}]" }]

Example request

curl -X POST "https://your-server-address/api/mda/approveplan" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"plan_id":"AAA.260001/T.01","app_by":"[{\"name\":\"Budi\",\"role\":\"Manager\",\"time\":\"2026-03-02 10:15:00\"}]"}]'
const options = [{"plan_id":"AAA.260001/T.01","app_by":"[{\"name\":\"Budi\",\"role\":\"Manager\",\"time\":\"2026-03-02 10:15:00\"}]"}];

const response = await fetch(
  "https://your-server-address/api/mda/approveplan",
  {
    method: "POST",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "plan_id": "AAA.260001/T.01",
      "app_by": "[{\"name\":\"Budi\",\"role\":\"Manager\",\"time\":\"2026-03-02 10:15:00\"}]"
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
plan_idstringNumber of the approved plan.
app_bystringThe stored approval data, as a JSON array (text) with name, role, and time.

Finance

Financial data.

GET /api/ar/get Invoices (receivables)

Retrieves the list of receivable invoices (account receivable). Requires the token header

Heads up. Filtering by client (cust_id) and by date range (from, to) is not supported on this endpoint yet. Use product_id or inv_id.

What to send

NameSent viaTypeRequiredDescription
tokenHeaderstringrequiredToken from /api/lgn/token.
options Header JSON array optional Contains one filter object (see the table below). May be omitted to retrieve all data.

Contents of options

NameTypeRequiredDescriptionExample
product_id array of string optional Product code. Leave empty or omit to apply no restriction. PRD001
inv_id array of string optional Invoice number. Leave empty or omit to apply no restriction. INV-2026-0001
[{ "inv_id": ["INV-2026-0001"] }]

Example request

curl "https://your-server-address/api/ar/get" \
  -H "token: YOUR_TOKEN" \
  -H 'options: [{"inv_id":["INV-2026-0001"]}]'
const options = [{"inv_id":["INV-2026-0001"]}];

const response = await fetch(
  "https://your-server-address/api/ar/get",
  {
    method: "GET",
    headers: {
      "token": "YOUR_TOKEN",
      "options": JSON.stringify(options)
    }
  }
);
const data = await response.json();
if (!data.success) { console.error(data.message); } else { console.log(data.result); }

Example success response

{
  "code": 0,
  "success": true,
  "message": "Success",
  "result": [
    {
      "ar_id": "INV-2026-0001",
      "ar_date": "2026-03-05",
      "cust_id": "C00001",
      "cust": "Sample Client Ltd",
      "product_id": "PRD001",
      "product": "Sample Product",
      "due": "2026-04-04",
      "nett": 425000000,
      "fee": 63750000,
      "vat_id": "",
      "vat": 53625000,
      "amount": 542375000
    }
  ]
}

Meaning of each column in result

ColumnTypeDescription
ar_idstringInvoice number.
ar_datestringInvoice date.
cust_idstringClient code.
custstringClient name.
product_idstringProduct code.
productstringProduct name.
duestringDue date.
nettnumberNett value.
feenumberAgency fee.
vat_idstringTax invoice number.
vatnumberVAT amount.
amountnumberTotal amount billed.

FAQ

The response says "Invalid token" although my token is correct?

If the HTTP status is 200 but the message is Invalid token, the problem is usually the content of options: it is not valid JSON, not an array, or (for v1) not URL-encoded. Check it with a tool such as jsonlint.com. If the HTTP status is 401, the token really is not recognized or has expired; request a new token.

The result is empty ([]) although the data exists?

Check that the token was created for the right company, entity, and year; data is read per year according to the token. Also check the filters: client/product codes must match exactly, and using several filters at once means all of them must match.

How long does a token last? Do I have to log in every time?

A token is valid for 1 day. Store it and reuse it until it expires. You do not need to log in for every request.

I cannot create or approve data (HTTP 403)

Your API account has not been granted write permission. Ask your SysAd administrator to enable it.

The curl examples do not work on Windows

The examples use single quotes (') and \ to continue lines, which suit Git Bash, Linux, and macOS. In the Windows Command Prompt, write the command on one line and replace single quotes with double quotes (escape double quotes inside the JSON with \). The easiest way: use an app such as Postman or Insomnia, or use the JavaScript examples.