A Node.js library for interfacing with Tracelink's REST API.
To explore and test endpoints interactively in the browser, use the API Playground.
npm install tracelink-apiconst { TracelinkClient } = require('tracelink-api');
// Create a new client
const client = new TracelinkClient({
access_token: 'your-api-token',
});
// Test the connection
const user = await client.user.get();
console.log('Connected as:', user.user.name);const client = new TracelinkClient({
access_token: 'your-api-token', // Required
format: 'json', // 'json' or 'xml' (default: 'json')
charset: 'UTF-8', // 'UTF-8' or 'CP850' (default: 'UTF-8')
});// Get company master data
const company = await client.company.get();
// List departments
const departments = await client.company.listDepartments();// Get current user
const current_user = await client.user.get();
// List all users
const users = await client.user.list();
// List user groups
const groups = await client.user.listGroups();// Create a new order
const new_order = await client.order.create({
number: '2024-001',
name: 'My new order',
description: 'Order description',
});
console.log('Created order with ID:', new_order.order_id);
// Create order with auto-numbering
const auto_order = await client.order.createAutoNumbered({
name: 'Auto-numbered order',
}, 1000, 1); // Start at 1000, +1 for each
// Get a specific order
const order = await client.order.get(1040);
// List all orders
const orders = await client.order.list();
// Update an order
await client.order.update(1040, {
name: 'Updated name',
description: 'New description',
});
// Delete an order
await client.order.delete(1040);// Create suborder
const suborder = await client.suborder.create(1040, {
number: '2024-001-A',
name: 'Subtask 1',
});
// Get suborder
const sub = await client.suborder.get(suborder.order_sub_id);
// List suborders
const suborders = await client.suborder.list();
// Update suborder
await client.suborder.update(sub.order_sub_id, { name: 'New name' });
// Delete suborder
await client.suborder.delete(sub.order_sub_id);Tracelink has multiple modules: purchase, genobj, customer, supplier, crm, docs, etc.
// Create a purchase
const purchase = await client.object.create('purchase', {
number: 'PO-001',
name: 'Material purchase',
exp_delivery_dt: '2024-12-25 10:00:00',
});
// Get a specific object
const obj = await client.object.get('purchase', 156);
// List all objects in a module
const purchases = await client.object.list('purchase');
// Update an object
await client.object.update('purchase', {
purchase_id: 156,
name: 'Updated name',
});
// Delete an object
await client.object.delete('purchase', 'purchase_id', 156);For modules with QR-code type, you must first create a tag_id:
// Create tag_id
const tag = await client.object.createTag('2', 1);
console.log('Tag ID:', tag.object.tag_id);
// Create object with tag_id
const genobj = await client.object.create('genobj', {
tag_id: tag.object.tag_id,
name: 'Stock product',
description: 'My description',
});// Add time registration to an order
const timereg = await client.order.addModule('timereg', {
order_id: 1040,
order_sub_id: 0,
hours: 2,
minutes: 30,
});
// List time registrations for an order
const timeregs = await client.order.listModule('timereg', 1040);
// List all time registrations
const all_timeregs = await client.order.listModule('timereg');
// Update time registration
await client.order.updateModule('timereg', {
timereg_id: 1186,
hours: 3,
});
// Delete time registration
await client.order.deleteModule('timereg', 'timereg_id', 1186);// Add genobj to CRM activity
const relation = await client.object.createRelation('genobj', 'crm', {
genobj_id: 546271,
crm_id: 16126,
unit_order_count_f: 1,
});
// List relations
const relations = await client.object.listRelations('genobj', 'crm', 16126);
// Update relation
await client.object.updateRelation('genobj', 'crm', {
genobj_crm_id: 45150,
unit_order_count_f: 2,
});
// Delete relation
await client.object.deleteRelation('genobj', 'crm', 'genobj_crm_id', 45150);const fs = require('fs');
// Upload document to order
const file_data = fs.readFileSync('document.pdf');
await client.order.uploadDocument(1040, {
data: file_data.toString('base64'),
filename: 'document.pdf',
type: 'pdf',
});
// Upload document to module object
await client.object.uploadDocument('purchase', 'purchase_id', 156, {
data: file_data.toString('base64'),
filename: 'invoice.pdf',
type: 'pdf',
});
// List documents for a module
const docs = await client.util.listDocuments('genobj', {
filter: { update_date: '>2024-01-01' },
});All list methods support sorting, filtering and paging:
// Sorting
const orders = await client.order.list({
sort: 'create_date', // Single field
reverse: true, // Descending order
});
// Multiple sort fields
const orders = await client.order.list({
sort: ['create_date', 'name'], // First by create_date, then by name
});
// Filtering
const orders = await client.order.list({
filter: {
locked: '=0', // Equal to
create_date: '>2024-01-01', // Greater than
name: 'Project', // LIKE (default)
},
});
// Filter operators
const orders = await client.order.list({
filter: {
order_id: 'IN(1,2,3)', // IN list
name: '~Test', // NOT LIKE
create_date: 'B2024-01-01,2024-12-31', // Between
},
});
// OR filtering
const orders = await client.order.list({
filter_or: true,
filter: {
name: 'Project A',
description: 'Project B',
},
});
// Paging
const orders = await client.order.list({
limit: 100, // Max 1000
page: 0, // First page (0-indexed)
});
// Combined
const orders = await client.order.list({
sort: 'name',
limit: 25,
filter: { locked: '=0' },
});| Operator | Description | Example |
|---|---|---|
| (empty) | LIKE (default) | name: 'Project' |
~ |
NOT LIKE | name: '~Test' |
= |
Equal to | locked: '=0' |
!= |
Not equal | locked: '!=1' |
< |
Less than | price: '<100' |
<= |
Less than or equal | price: '<=100' |
> |
Greater than | create_date: '>2024-01-01' |
>= |
Greater than or equal | create_date: '>=2024-01-01' |
IN() |
In list | order_id: 'IN(1,2,3)' |
!IN() |
Not in list | order_id: '!IN(4,5,6)' |
B |
Between | date: 'B2024-01-01,2024-12-31' |
For safe request retries, you can use idempotency keys:
const crypto = require('crypto');
const idempotency_key = crypto.randomUUID().substring(0, 32);
const order = await client.order.create({
name: 'My order',
}, {
idempotency_key,
});
// If a network error occurs, you can safely resend the same request
// with the same idempotency_key - the order will only be created onceconst { TracelinkClient, TracelinkError } = require('tracelink-api');
try {
const order = await client.order.get(99999);
} catch (error) {
if (error instanceof TracelinkError) {
console.error('Tracelink error:', error.message);
console.error('HTTP code:', error.code);
console.error('Full response:', error.response);
} else {
console.error('Network error:', error);
}
}import { TracelinkClient, TracelinkError } from 'tracelink-api';
const client = new TracelinkClient({
access_token: 'your-api-token',
});The library includes TypeScript definitions:
import { TracelinkClient, TracelinkConfig, OrderParams } from 'tracelink-api';
const config: TracelinkConfig = {
access_token: 'your-api-token',
};
const client = new TracelinkClient(config);
const options: OrderParams = {
sort: 'name',
limit: 25,
filter: { locked: '=0' },
};
const orders = await client.order.list(options);The library follows JavaScript best practices:
- Functions: camelCase (
listDepartments,createAutoNumbered,uploadDocument) - Variables: snake_case (
access_token,order_id,idempotency_key)
| Name | Internal Name | Description | Access By | Type |
|---|---|---|---|---|
| Custom lists | custlist | Custom lists for extra fields | ||
| TimeReg | timereg | Time registration | Order | |
| Customer | customer | Customers | ||
| Task | task | Tasks and routes | Order | |
| GenObj | genobj | Generic objects (stock, bookings, etc.) | Direct/Order | QR-code |
| Batch | batch_genobj | Batch portions for stock | GenObj | QR-code |
| Docs | docs | Guidelines, procedures, documents | ||
| CRM | crm | CRM and sales quotes | ||
| Purchase | purchase | Purchase orders | ||
| Supplier | supplier | Purchasing suppliers | ||
| Stock location | stockloc | Stock locations for GenObj/Batch | QR-code |
All objects have 5 metadata fields (metadata_1 to metadata_5) and 3 foreigndata fields:
await client.order.create({
name: 'My order',
metadata_1: 'Extra info',
metadata_2: JSON.stringify(['value1', 'value2']), // Array format
foreigndata_1: 'external-id-123', // ID from external system
});Metadata fields are configurable in the Tracelink system and can be used for any purpose. They can be configured as checkboxes, input fields, dropdowns, etc. The fields can also be extended into "tables" with multiple columns using JSON arrays.
Example for packing dimensions where height=3, width=8, depth=2:
metadata_1: JSON.stringify(['3', '8', '2'])Foreigndata fields hold external information (e.g., IDs from external systems). These fields are not displayed in the Tracelink system and are only for storing external references.
Column names follow a naming convention where the suffix indicates the type:
| Suffix | Type | Description |
|---|---|---|
_cy |
Currency | Decimal |
_pct |
Percent | Decimal with % |
_sc |
Seconds | Duration in seconds |
_i |
Integer | Whole number |
_f |
Decimal | Floating point number |
_bool |
Boolean | True/false |
_byte |
Bytes | Binary data |
_dt, date_time, create_date, update_date |
DateTime | Date and time |
_da, date |
Date | Date only |
_ti |
Time | Time of day |
All dates are in CET (adjusted for daylight saving time). All decimals have a maximum length of 16 (10 before decimal point, 6 after).
const { TracelinkClient } = require('tracelink-api');
const crypto = require('crypto');
async function example() {
// Initialize client
const client = new TracelinkClient({
access_token: process.env.TRACELINK_TOKEN,
});
try {
// Create an order with idempotency
const idempotency_key = crypto.randomUUID().substring(0, 32);
const order = await client.order.create({
name: 'Production Order #123',
description: 'Build 100 units',
deadline_date: '2024-12-31 16:00:00',
metadata_1: 'Priority: High',
}, { idempotency_key });
console.log('Created order:', order.order_id);
// Add time registration
await client.order.addModule('timereg', {
order_id: order.order_id,
order_sub_id: 0,
hours: 5,
minutes: 30,
});
// List orders with filtering
const active_orders = await client.order.list({
filter: {
locked: '=0',
create_date: '>2024-01-01',
},
sort: 'deadline_date',
limit: 50,
});
console.log(`Found ${active_orders.count} active orders`);
} catch (error) {
if (error instanceof TracelinkError) {
console.error('API Error:', error.message);
} else {
console.error('Error:', error);
}
}
}
example();MIT