The shape of it
Ncore has four things in it, and everything else is a view onto them.
- Equipment
- the machines, and the sites and departments they stand in. Every record in the system eventually points at one of these.
- Templates
- a maintenance procedure written once: the steps, the readings to take, the checks to tick, and how often it comes round.
- Work orders
- a template on a particular day, for a particular machine, in front of a particular group. This is what an operator actually fills in.
- Records
- what a completed work order leaves behind — readings, faults, photographs, inspection entries and service history, all attached to the machine.
Setting a plant up is therefore: enter the equipment, write the templates, point them at the equipment, and let the schedule run. The rest of this page is that in order.
Equipment first
Start with the register, because templates bind to it. Under Equipment, add each machine with a code, a name and a type. The code is the identifier you will use everywhere else, including from the API — make it the one already painted on the machine or held in your other systems.
Sites and departments group machines. A site is a physical plant; a department is what it belongs to organizationally. Both are filters everywhere in the product, so it is worth getting them right at the start.
Each machine gets an asset passport: its identity, documents, readings over time, faults, and service history in one place. Open it from the register by clicking the plate.
Writing a procedure
A template is the SOP that used to live in a binder. Under Templates, create one and build it on the canvas as a flow of steps.
A step is one of a few kinds:
- a reading — a number, with a unit and optionally a spec range it is judged against;
- a check — a yes/no or pass/fail the operator ticks;
- a choice — one of a fixed set of answers;
- free text — for a description of a fault or a note;
- a photograph — for the things a number cannot record.
Each step can be bound to a piece of equipment, which is what makes the resulting record attach to the right machine rather than to the round as a whole. A round that walks four machines produces readings against four machines.
Steps can be grouped into sections, and a section can be bound to equipment as a whole — the usual shape for a round that repeats the same six checks on each of five pumps.
How the day’s work appears
A template carries its own schedule: daily, weekly, monthly or yearly, at a time you set.
At the plant’s own midnight — its timezone, not ours — the work orders for that day are generated and put in front of the group the template is bound to. Nobody types a schedule.
If a day is missed because the system was unreachable, catch-up generation fills it in rather than leaving a hole in the record.
Ad-hoc work orders can be created by hand at any time, from Manage work orders, for the jobs that were not planned.
Doing the round
An operator signs in on whatever browser they have, sees the work orders for their group today, and works down them step by step.
Readings that fall outside their spec range are flagged as the number is entered, not discovered later in a report. A step can raise a fault, which marks the machine and opens a record against it.
What is signed is kept: each completed step carries who did it and when. That attribution is the point of the product — it is what makes the record evidence rather than a note.
Inspection records
Statutory periodic inspections are kept separately from routine maintenance, because they are asked for separately.
An inspection record carries the finding, the evidence, the dates, and who carried it out. Under Inspection records you can filter by machine, by site and by period, and export the result.
This is the screen to open when an inspector asks what was done to a machine and when.
Reports
Reports answer the questions a plant is actually asked: what was done on time, what is still open, how late it is, what is down, how long it has been down, and how often a given machine fails.
- Compliance — completion against what was scheduled.
- Overdue — what is late, and by how much.
- Faults and availability — downtime and MTBF, per machine and per site.
- Out of spec — readings that fell outside their range.
- Data quality — where records are thin, so you know how much to trust the rest.
- Company and departments — the same figures rolled up across sites.
Every report can be read for one site or across the whole company, and every one exports.
People, groups and access
There are two roles: administrator and operator. Administrators configure the system; operators do the work in front of them.
Operators do not need email addresses. An administrator creates the account, and the operator signs in with a username plus the organization’s handle. Only people who need invitations and password resets need an address.
Groups are how work is routed. A template is bound to a group — Maintenance, Quality Control, a particular shift — and the work orders it generates appear for the people in it.
An organization’s data is fenced off in the database itself, not just in the application: a query cannot return another organization’s rows even if application code forgets a filter.
Getting your data out
Work orders, equipment, readings and inspection records all export as CSV from inside the application, on every plan, without asking us.
This is deliberate. A maintenance history that can only be read through the tool that made it is a liability, not an asset.
Keys and scopes
The integration API is how a SCADA system, a historian or a PLC gateway writes readings in without anybody retyping them.
Create a key under Integrations. A key is shown once, at creation — we store only a short prefix in clear and a hash of the rest, so a lost key is replaced rather than recovered.
Every key is scoped to what the integration actually needs, and can be revoked at any moment:
- assets:read — list machines and metric names
- readings:write — submit sensor values and counters
- events:write — submit alarms raised by the control system
- service:write — submit service and repair records
Send the key as a bearer token. Requests are rate limited per key; a key being used in a way that threatens the service may be revoked.
/api/v1/whoamiany keyCheck a key
Returns what the key is called and what it may do. Writes nothing — the safest first call when setting an integration up.
curl http://localhost:60872/api/v1/whoami \
-H "Authorization: Bearer $SOP_API_KEY"{
"name": "Boiler house gateway",
"prefix": "5a75b874",
"scopes": [
"readings:write",
"events:write"
],
"expiresAt": null,
"serverTime": "2026-08-04T09:30:00.000Z"
}/api/v1/assetsassets:readList assets
Every machine you may write to. Address assets by their code (TGV-CHP1) rather than by id — the code is what is written on the machine and in your other systems.
curl http://localhost:60872/api/v1/assets \
-H "Authorization: Bearer $SOP_API_KEY"{
"assets": [
{
"id": "eq-tgv-chp1",
"code": "TGV-CHP1",
"name": "CHP 1 V",
"type": "Other",
"department": "Toplana",
"location": "Gornja Vežica",
"status": "operational",
"parentId": null
}
]
}/api/v1/metricsassets:readList metric names
The metric names this installation understands, and the range each is judged against. Send one of these names and your readings are charted with a spec band and flagged when they fall outside it.
curl http://localhost:60872/api/v1/metrics \
-H "Authorization: Bearer $SOP_API_KEY"{
"metrics": [
{
"metric": "pressure",
"label": "Discharge pressure",
"unit": "bar",
"min": 4,
"max": 7,
"hasLimits": true,
"templates": [
"pump-round"
],
"conflicting": false
}
]
}/api/v1/readingsreadings:writeSubmit readings and counters
Sensor values, in batches. Set kind to "counter" for totals that only ever rise, such as running hours or kWh — these are reported as a latest value and a daily rate rather than as a trend.
| Field | Type | Required | Meaning |
|---|---|---|---|
| asset | string | yes | Asset code or id, from GET /assets. |
| metric | string | yes | Metric name. Use one from GET /metrics to get limits. |
| value | number | yes | The measured number. |
| recordedAt | string | yes | When the machine took the reading. ISO 8601. |
| unit | string | optional | Unit as you record it, e.g. "bar". Shown as-is. |
| kind | "reading" | "counter" | optional | Defaults to "reading". Use "counter" for cumulative totals. |
| id | string | optional | Your own id for this item. Resending the same id updates it instead of duplicating. |
curl -X POST http://localhost:60872/api/v1/readings \
-H "Authorization: Bearer $SOP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"readings": [
{
"asset": "TGV-CHP1",
"metric": "pressure",
"value": 6.4,
"unit": "bar",
"recordedAt": "2026-08-04T09:00:00Z"
},
{
"asset": "TGV-CHP1",
"metric": "pressure",
"value": 6.9,
"unit": "bar",
"recordedAt": "2026-08-04T10:00:00Z"
},
{
"asset": "TGV-CHP1",
"metric": "running_hours",
"value": 18412,
"unit": "h",
"kind": "counter",
"recordedAt": "2026-08-04T10:00:00Z"
}
]
}'{
"accepted": 3,
"unknownMetrics": [
"running_hours"
]
}/api/v1/eventsevents:writeSubmit machine alarms
Faults and alarms raised by the control system rather than noticed by a person.
| Field | Type | Required | Meaning |
|---|---|---|---|
| asset | string | yes | Asset code or id, from GET /assets. |
| code | string | yes | The alarm or fault code from the control system. |
| startedAt | string | yes | When the alarm was raised. ISO 8601. |
| severity | info | warning | alarm | critical | optional | Defaults to "warning". |
| message | string | optional | Human-readable description of the alarm. |
| endedAt | string | optional | When the alarm cleared. Leave out while it is still standing. |
| data | object | optional | Anything else you want kept with the event. |
| id | string | optional | Your own id for this item. Resending the same id updates it instead of duplicating. |
curl -X POST http://localhost:60872/api/v1/events \
-H "Authorization: Bearer $SOP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [
{
"asset": "TGV-CHP1",
"code": "E-4021",
"severity": "alarm",
"message": "Bearing temperature high",
"startedAt": "2026-08-04T04:10:00Z",
"id": "alarm-991"
}
]
}'{
"accepted": 1,
"open": 1
}/api/v1/service-recordsservice:writeSubmit external maintenance
Work carried out by a contractor, or recorded in another maintenance system. Without this, an asset passport shows a machine as untouched when in fact it was serviced.
| Field | Type | Required | Meaning |
|---|---|---|---|
| asset | string | yes | Asset code or id, from GET /assets. |
| performedAt | string | yes | When the work was done. ISO 8601. |
| kind | maintenance | repair | inspection | calibration | installation | other | optional | Defaults to "maintenance". |
| organisation | string | optional | The firm that did the work. |
| performedBy | string | optional | The engineer who did it. |
| description | string | optional | What was done. |
| cost | number | optional | What it cost. |
| currency | string | optional | Three-letter currency code, e.g. EUR. |
| downtimeHours | number | optional | How long the machine was out of service for it. |
| id | string | optional | Your own id for this item. Resending the same id updates it instead of duplicating. |
curl -X POST http://localhost:60872/api/v1/service-records \
-H "Authorization: Bearer $SOP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"asset": "TGV-CHP1",
"performedAt": "2026-07-15T09:00:00Z",
"kind": "repair",
"organisation": "Tehnix d.o.o.",
"performedBy": "M. Horvat",
"description": "Replaced main bearing",
"cost": 1450,
"currency": "EUR",
"downtimeHours": 6
}
]
}'{
"accepted": 1
}Errors
- 400
- Something in the batch is wrong. The response names the offending item by index and the field, and nothing in the batch was written.
- 401
- The key is missing, malformed, unrecognised, revoked or expired. The response says which.
- 403
- The key is valid but lacks the scope this endpoint needs. The response lists the scopes it does have.
- 413
- The batch exceeds the item limit. Split it.
- 429
- Too many requests for this key. Send fewer, larger batches.
Limits
- Up to 1000 items per request.
- 120 requests per 60 seconds, counted per key.
- Timestamps are required, must be ISO 8601, and should be the time the machine took the reading — not the time you sent it.
