A comprehensive third-party TypeScript client library for IBM Maximo Application Suite (MAS) with full REST API support for both Manage API (OSLC/REST) and Tools API.
This package is distributed as compiled JavaScript with TypeScript declaration files. Source files and tests are not included in the published npm package.
npm install @maximomize/maximo-api-client
The recommended way to create clients is using the Builder pattern, which provides a fluent API and better type safety.
import { MASClientBuilder } from '@maximomize/maximo-api-client';
const client = new MASClientBuilder()
// Common settings only (timeouts, TLS, logger, etc.)
.timeout(60000)
.trustSelfSignedCerts()
.configureManage(manage => manage
.baseUrl('https://manage.example.com')
.withApiKey('manage-api-key')
.disableLeanMode()
)
.configureTools(tools => tools
.baseUrl('https://tools.example.com')
.apiKey('tools-api-key')
)
.build();
Use distinct base URLs for manage and tools when your MAS deployment exposes different hosts.
Note on Defaults:
true) by default.443 if SSL is enabled, 80 otherwise.API if using API Key, OSLC otherwise.import { MaximoClientBuilder } from '@maximomize/maximo-api-client';
const client = new MaximoClientBuilder()
.baseUrl('https://maximo.example.com')
.withCredentials('username', 'password')
.build();
You can still use the configuration object approach, though the builder is preferred.
import { MaximoClient } from '@maximomize/maximo-api-client';
const client = new MaximoClient({
baseUrl: 'https://your-maximo-instance.com/maximo',
userName: 'username',
password: 'password'
});
The Tools API provides administrative utilities for Maximo Manage:
import { MASClientBuilder } from '@maximomize/maximo-api-client';
// Initialize Tools API client using the builder (Recommended)
const masClient = new MASClientBuilder()
.configureManage(manage => manage
.baseUrl('https://manage.example.com')
.withApiKey('manage-api-key')
)
.configureTools(tools => tools
.baseUrl('https://tools.example.com')
.apiKey('your-tools-api-key')
)
.build();
const toolsClient = masClient.tools;
// Or standalone:
import { MaximoToolsClientBuilder } from '@maximomize/maximo-api-client';
const toolsClient = new MaximoToolsClientBuilder()
.baseUrl('https://your-maximo-instance.com')
.apiKey('your-tools-api-key')
.build();
// Integrity checking
await toolsClient.integrityChecker.generateIntegrityCheckerLog();
const allLogs = await toolsClient.logManager.getAllToolsLogs();
const specificLog = await toolsClient.logManager.getToolsLog('logfile-name');
// Pod management
await toolsClient.podManager.stopPods();
await toolsClient.podManager.startPods();
// Database validation
await toolsClient.databaseService.validateDatabaseForMigration();
// Script execution
await toolsClient.scriptRunner.runDbcScript('V9000_05');
// ERD operations
await toolsClient.erdGenerator.generateErd();
const erd = await toolsClient.erdGenerator.viewErd();
// Build status
const buildStatus = await toolsClient.buildStatusChecker.getBuildStatus();
// Log management
await toolsClient.logManager.getAllToolsLogs();
All resource services support both Builder pattern (fluent API with fine-grained control) and Direct methods (simple, concise calls). Below we use a generic object service instead of asset/workorder-specific APIs.
const recordService = client.getGenericOslcService('mxapicustomobj');
| Approach | Method |
|---|---|
| Builder | fetchById(id).properties(...).execute() |
| Direct | getById(id, select?) or findById(id, properties?) |
// Builder: Full control over request options
const record = await recordService
.fetchById('12345')
.properties(['customid', 'description', 'status'])
.inlineDoc(true)
.dropNulls(false)
.execute();
// Direct: Simple and concise
const recordDirect = await recordService.getById('12345', ['customid', 'description']);
| Approach | Method |
|---|---|
| Builder | query().where(...).select(...); executeQuery(query) |
| Direct | findAll(where?, select?, orderBy?) or findPaginated(...) |
// Builder: Full query capabilities
const query = recordService
.query()
.select(['customid', 'description', 'status'])
.where('status="ACTIVE"')
.orderBy('customid')
.page(1, 50)
.withCount();
const results = await recordService.executeQuery(query);
// Direct: Quick queries
const records = await recordService.findAll('status="ACTIVE"', ['customid', 'status']);
const page = await recordService.findPaginated('status="ACTIVE"', 1, 50, ['customid']);
| Approach | Method |
|---|---|
| Builder | createResource(data).returnProperties(...).execute() |
| Direct | create(data, returnProperties?, getCreatedResult?, options?) |
// Builder: Control headers and response options
const newRecord = await recordService
.createResource({ customid: 'C001', description: 'New Record', siteid: 'BEDFORD' })
.returnProperties(['customid', 'status'])
.withTransactionId('unique-tx-id')
.suppressEvents()
.execute();
// Direct: Simple creation
const newRecordDirect = await recordService.create(
{ customid: 'C001', description: 'New Record', siteid: 'BEDFORD' },
['customid', 'status']
);
| Approach | Method |
|---|---|
| Builder | updateResource(id, data).useMergePatch().execute() |
| Direct | update(id, data, returnProperties?, options?) or merge(...) |
// Builder: Full control including patch type
const result = await recordService
.updateResource('12345', { description: 'Updated Description' })
.useMergePatch() // Use MERGE patch for child objects
.returnProperties(['customid', 'description'])
.suppressEvents()
.execute();
// Direct: Simple update
await recordService.update('12345', { description: 'Updated Description' });
// Direct with MERGE (preserves unspecified child objects)
await recordService.merge('12345', {
childcollection: [{ childid: 'LINE1', qty: 100 }]
});
| Approach | Method |
|---|---|
| Builder | deleteById(id).suppressEvents().execute() |
| Direct | delete(idOrResource, options?) |
// Builder: Control deletion options
await recordService
.deleteById('12345')
.withTransactionId('tx-delete-123')
.suppressEvents()
.execute();
// Direct: Simple deletion
await recordService.delete('12345');
Creates if not exists, updates if exists (based on primary keys).
// Direct method only (no builder)
await recordService.sync(
{ customid: 'C001', siteid: 'BEDFORD', description: 'Synced Record' },
['customid', 'status'], // returnProperties
'MERGE' // patchType: 'MERGE' or 'UPDATE'
);
autoScript is one of the built-in domain services in this library, alongside services such as schema, scriptHandlerService, and getSystemService().
// Get built-in automation script service
const autoscriptService = client.autoScript;
// Create a script
await autoscriptService.createScript(
{
autoscript: 'MXAPI_HELLO',
language: 'python',
source: 'service.log("Hello from MXAPI_HELLO")',
description: 'Sample script created from API client',
status: 'ACTIVE'
},
'autoscript,status,scriptlanguage'
);
// Find active scripts
const activeScripts = await autoscriptService.findByStatus('ACTIVE');
// Execute script handler endpoint
const result = await autoscriptService.execute('MXAPI_HELLO', { name: 'Maximo' });
// Update script source
await autoscriptService.uploadScript(
'MXAPI_HELLO',
'service.log("Updated source")',
'autoscript,status'
);
Other available built-in services on MaximoClient include:
asset, workOrder, locationautoScript, scriptHandlerService, oslcInfoService, mmischema, appXml, objectStructService, conditionExpression, toolsLoggetSystemService(), getLoggingService(), getApiKeyService(), getConditionExpressionService()// System service (method-based accessor)
const systemService = client.getSystemService();
const isManage = await systemService.isManage();
const serverDate = await systemService.getDate();
const authEnabled = await systemService.isOsSecurityOn();
// Schema service (getter accessor)
const schema = client.schema;
const assetSchema = await schema.getObjectStructureSchema(
'mxapiasset',
'properties,required,title'
);
// Manage Monitoring (MMI) APIs
const mmi = client.mmi;
const members = await mmi.getMembers();
const dbPing = await mmi.pingDb('thisserver');
const heapDump = await mmi.getAppServicesHeapDump('thisserver');
// API metadata / endpoint discovery
const oslcInfo = client.oslcInfoService;
const apiHome = await oslcInfo.fetchApiHomeDocument();
// Script handler endpoint service
const scriptHandler = client.scriptHandlerService;
const pong = await scriptHandler.executeScript('MXAPI_PING', {
method: 'GET',
queryParams: { ping: '1' }
});
Every resource service can list and generate BIRT reports for its object structure.
const woService = client.getGenericOslcService('MXAPIWODETAIL');
// List reports associated with the object structure
const reports = await woService.listReports();
console.log(reports.map(r => r.reportname));
// Generate a PDF for a single work order
const pdf = await woService.generateReport('woprint.rptdesign', {
where: 'wonum="1000"'
});
console.log(pdf.contentType, pdf.data.length);
// Generate in another supported format
const xls = await woService.generateReport('woprint.rptdesign', {
format: 'xls',
where: 'wonum="1000"'
});
// Include attachments in the generated report
const withAttachments = await woService.generateReport('woprint.rptdesign', {
attachment: true
});
// Write the report directly to a file
await woService.generateReportToFile(
'woprint.rptdesign',
'/tmp/work-order-report.pdf',
{ where: 'wonum="1000"' }
);
Supported formats include pdf, html, xls, xlsx, xls_spudsoft, and csv. Any other format string is also accepted so that newer server formats do not require a client update.
Every resource service can call the Maximo getlist~<attribute> lookup endpoints. Lookups are paginated collection queries, so they support oslc.select, oslc.where, oslc.orderBy, and paging.
const woService = client.getGenericOslcService('MXAPIWODETAIL');
// Lookup possible statuses without a real work order (zombie MBO)
const statuses = await woService.getLookupForZombie('status', {
select: '*',
pageSize: 10
});
console.log(statuses.items);
// Lookup statuses for a specific work order
const recordStatuses = await woService.getLookupForRecord('123', 'status');
// Lookup on a child object
const childStatuses = await woService.getLookupForChild(
'WO1', 'wplabor', 'WPL1', 'craft'
);
// Lookup with transient context on a new MBO
const locations = await woService.getLookupForNewMbo(
'location',
{ siteid: 'BEDFORD' },
{ select: 'location,description', pageSize: 5 }
);
// Paginate through results
if (locations.hasNextPage) {
const next = await locations.nextPage();
}
When creating or updating resources, Maximo normally returns after the first validation error. Set batcherror: 1 to receive all validation errors at once — useful for showing multiple field errors in a UI.
Use the builder (most convenient):
const assetService = client.getGenericOslcService('MXAPIASSET');
try {
await assetService
.createResource({
assetnum: 'A100',
siteid: 'BEDFORD',
assettype: 'BLAH',
location: 'BAH',
})
.withBatchErrors()
.execute();
} catch (error) {
if (error instanceof OslcError && error.attrerrors) {
for (const item of error.attrerrors) {
console.log(`${item.errorattrname}: ${item.message}`);
}
}
}
Or via the direct method with the batchErrors flag:
await assetService.create(
{ assetnum: 'A100', siteid: 'BEDFORD', assettype: 'BLAH', location: 'BAH' },
undefined,
undefined,
true // batchErrors
);
The OslcError.attrerrors property contains an array of BatchErrorItem with errorattrname, errorobjpath, reasonCode, message, and statusCode.
When creating, updating, or deleting resources, you can set a transactionid header. Maximo stores this ID for 5 minutes (configurable) and rejects duplicate IDs with HTTP 409 Conflict — useful for preventing double-submission when the server committed but the client got a timeout.
Use the builder (most convenient):
const assetService = client.getGenericOslcService('MXAPIASSET');
await assetService
.createResource({ assetnum: 'A100', siteid: 'BEDFORD' })
.withTransactionId('my-unique-tx-id')
.execute();
Or via the direct method with the transactionId flag:
await assetService.create(
{ assetnum: 'A100', siteid: 'BEDFORD' },
undefined,
undefined,
undefined,
'my-unique-tx-id' // transactionId
);
If the server detects a duplicate, an OslcError with status 409 is thrown. Use the isDuplicateRequest property to handle it:
try {
await assetService.create({ ... }, undefined, undefined, undefined, 'same-tx-id');
} catch (error) {
if (error instanceof OslcError && error.isDuplicateRequest) {
console.log('Duplicate request detected — the original was already processed.');
}
}
Use getGenericOslcService (or getService) when there is no dedicated service class for an object structure.
// Create a generic service for any object structure
const customObjectService = client.getGenericOslcService('mxapicustomobj');
// Query with full builder support
const query = customObjectService
.query()
.select(['customid', 'description', 'status'])
.where('status="ACTIVE"')
.orderBy('customid')
.page(1, 20)
.withCount();
const results = await customObjectService.executeQuery(query);
You can extend ResourceService for reusable business-specific methods and instantiate it through client.getService(...).
import {
MaximoClient,
HttpClient,
OslcResource,
ResourceService
} from '@maximomize/maximo-api-client';
interface InspectionForm extends OslcResource {
inspformnum: string;
description?: string;
status?: string;
siteid?: string;
}
class InspectionFormService extends ResourceService<InspectionForm> {
constructor(httpClient: HttpClient, objectStructure: string = 'mxapiinspform') {
super(httpClient, objectStructure);
}
async findActiveBySite(siteId: string): Promise<InspectionForm[]> {
return this.findAll(`status="ACTIVE" and siteid="${siteId}"`);
}
}
const client = new MaximoClient({
baseUrl: 'https://your-maximo-instance.com',
apiKey: 'your-api-key'
});
// Create your custom service instance via client factory
const inspectionFormService = client.getService<InspectionFormService>(
'mxapiinspform',
InspectionFormService
);
const forms = await inspectionFormService.findActiveBySite('BEDFORD');
In a clustered Manage deployment, the load balancer chooses which JVM
serves each call. ClusterService discovers the topology and pinTo()
returns a child client that lands every call on a chosen JVM — useful
for tailing one pod's logs, reproducing JVM-local issues, or following
a specific user's session.
const here = await client.cluster.here(); // which JVM answered
const nodes = await client.cluster.listNodes(); // every JVM in the cluster
const target = await client.cluster.findUserJvm('alice'); // which JVM has alice's session
if (target) {
const pinned = await client.pinTo(target);
const wo = await pinned.workOrder.fetch({ wonum: '1001' }); // lands on alice's JVM
}
Discovery is native (uses /mmi/members + MXAPIMAXSESSION). Arbitrary
pinning of OSLC traffic requires either a per-pod ingress URL on the node
entry, or the optional MAXIMOMIZE.CLUSTER Jython relay autoscript installed
on the deployment. The client picks whichever is available; if neither is,
it throws a structured ClusterError. Full design, install, and Jython
constraints in CLUSTER_ROUTING.md.
We have a Public Repository for:
This project is licensed under the MIT License - see the LICENSE file for details.