-
-
Notifications
You must be signed in to change notification settings - Fork 15
API Reference
This document provides comprehensive documentation for ncSender's REST API and WebSocket events. Use this reference to build integrations, custom clients, or automation tools.
-
Base URL:
http://<host>:<port>/api - Default Port: 8090 (configurable in settings)
- Authentication: None (local network access only)
-
Content-Type:
application/json(unless specified otherwise)
GET /api/cnc/serial-ports
Returns available USB serial ports.
Response:
[
{ "path": "/dev/ttyUSB0", "manufacturer": "FTDI" },
{ "path": "/dev/ttyACM0", "manufacturer": "Arduino" }
]POST /api/cnc/connect
Connect to CNC controller via USB or Ethernet.
Request Body:
// USB Connection
{ "port": "/dev/ttyUSB0", "baudRate": 115200 }
// Ethernet Connection
{ "ip": "192.168.1.100", "ethernetPort": 23 }
// Auto-connect (uses saved settings)
{}Response:
{ "success": true, "message": "Connected to CNC" }POST /api/cnc/disconnect
Response:
{ "success": true }GET /api/cnc/status
Response:
{
"connected": true,
"port": "/dev/ttyUSB0",
"baudRate": 115200,
"machineState": { /* see Machine State section */ }
}POST /api/cnc/send-command
Send G-code or system command to CNC controller.
Request Body:
{
"command": "G0 X10 Y20",
"commandId": "optional-unique-id",
"displayCommand": "G0 X10 Y20 (optional display text)",
"meta": { "sourceId": "my-integration" }
}Hex Commands: Use \xHH format (e.g., \x18 for soft reset)
Response:
{ "success": true, "commandId": "cmd-123456" }GET /api/gcode
Response:
{
"tree": [
{ "name": "part.gcode", "type": "file", "size": 12345 },
{ "name": "projects", "type": "folder", "children": [...] }
]
}POST /api/gcode
Content-Type: multipart/form-data
Form Data:
-
file: Binary file (.gcode, .nc, .tap, .txt)
Response:
{ "success": true, "filename": "part.gcode" }POST /api/gcode-files/load-temp
Request Body:
{
"content": "G0 X0 Y0\nG1 X10 F1000\n...",
"filename": "generated.gcode",
"sourceFile": "original.gcode"
}GET /api/gcode/file?path=part.gcode
Response:
{ "filename": "part.gcode", "content": "G0 X0 Y0\n..." }POST /api/gcode/file/save
Request Body:
{ "path": "part.gcode", "content": "G0 X0 Y0\n..." }POST /api/gcode/file/delete
Request Body:
{ "path": "part.gcode" }POST /api/gcode/folders
Request Body:
{ "path": "projects/new-folder" }POST /api/gcode/move
Request Body:
{ "sourcePath": "old-name.gcode", "destinationPath": "new-name.gcode" }GET /api/gcode/current/download
Returns binary stream of currently loaded G-code.
POST /api/gcode-job
Request Body:
{ "filename": "part.gcode" }POST /api/gcode-job/stop
Response:
{ "success": true, "pauseBeforeStop": 500 }POST /api/gcode-job/analyze-line
Request Body:
{ "lineNumber": 150 }Response:
{
"state": {
"tool": 2,
"spindleSpeed": 12000,
"feedRate": 1000,
"position": { "x": 10.5, "y": 20.3, "z": -5.0 }
},
"lineNumber": 150,
"totalLines": 5000,
"warnings": ["Machine is not homed"],
"willPerformToolChange": true,
"expectedTool": 2,
"currentTool": 1,
"resumeSequence": ["G21", "G90", "G54", "M6 T2", "S12000 M3", "G0 Z10"]
}POST /api/gcode-job/start-from-line
Request Body:
{
"filename": "part.gcode",
"startLine": 150,
"spindleDelaySec": 3,
"approachHeight": 10,
"plungeFeedRate": 500
}GET /api/settings
GET /api/settings/:name
PATCH /api/settings
Request Body:
{
"connection": {
"type": "ethernet",
"ip": "192.168.1.100"
}
}POST /api/settings
GET /api/macros
Response:
[
{
"id": "macro-123",
"name": "Home All",
"description": "Home all axes",
"commands": "$H"
}
]POST /api/macros
Request Body:
{
"name": "Park",
"description": "Move to park position",
"commands": "G53 G0 Z-5\nG53 G0 X0 Y0"
}POST /api/macros/:id/execute
Response:
{ "success": true, "commandsExecuted": 3 }PUT /api/macros/:id
DELETE /api/macros/:id
GET /api/tools
GET /api/tools/:id
POST /api/tools
PUT /api/tools/:id
DELETE /api/tools/:id
PUT /api/tools
Request Body: Array of tool objects
GET /api/plugins
GET /api/plugins/loaded
POST /api/plugins/:pluginId/enable
POST /api/plugins/:pluginId/disable
GET /api/plugins/:pluginId/settings
PUT /api/plugins/:pluginId/settings
POST /api/plugins/install
Content-Type: multipart/form-data
Form field: plugin (ZIP file)
POST /api/plugins/install-from-url
Request Body:
{ "url": "https://github.com/user/repo/releases/download/v1.0/plugin.zip" }GET /api/plugins/:pluginId/check-update
Response:
{
"hasUpdate": true,
"currentVersion": "1.0.0",
"latestVersion": "1.1.0",
"downloadUrl": "https://..."
}GET /api/firmware
GET /api/firmware?refresh=true
Response:
{
"version": "1.0",
"firmwareVersion": "1.1f.20250407",
"groups": {
"0": { "id": 0, "name": "General" }
},
"settings": {
"130": { "id": 130, "name": "$130", "value": "400", "unit": "mm" }
}
}POST /api/firmware/flash
Request Body:
{
"hex": "base64-encoded-hex-file",
"port": "/dev/ttyUSB0",
"isDFU": false
}Progress is broadcast via WebSocket events.
GET /api/system/health
Response:
{ "status": "ok", "timestamp": "2024-01-15T10:30:00.000Z" }GET /api/system/server-state
GET /api/system/logs
GET /api/system/logs/:filename
GET /api/system/logs/:filename/download
POST /api/probe/start
Request Body:
{
"probingAxis": "Z",
"probingDistance": -50,
"probingFeedRate": 100,
"probingType": "tool-length"
}POST /api/probe/stop
GET /api/alarms/alarm/:id
Response:
{ "id": 1, "description": "Hard limit triggered" }Connect to WebSocket at: ws://<host>:<port>
On connect, client receives:
{ "type": "client-id", "data": { "clientId": "client-123..." } }
{ "type": "server-state-updated", "data": { /* full state */ } }
{ "type": "gcode-updated", "data": { "filename": "...", "totalLines": 5000 } }{
"type": "server-state-updated",
"data": {
"machineState": {
"connected": true,
"status": "idle",
"tool": 1,
"spindleSpeed": 0,
"feedRateOverride": 100,
"spindleSpeedOverride": 100,
"MPos": { "x": 0, "y": 0, "z": 0 },
"WPos": { "x": 0, "y": 0, "z": 0 },
"homed": true,
"alarms": []
},
"jobLoaded": {
"filename": "part.gcode",
"currentLine": 150,
"totalLines": 5000,
"status": "running",
"progressPercent": 3.0,
"estimatedSec": 1800,
"remainingSec": 1746
}
}
}{
"type": "cnc-command",
"data": {
"id": "cmd-123",
"command": "G0 X10",
"displayCommand": "G0 X10",
"status": "pending",
"timestamp": "2024-01-15T10:30:00.000Z"
}
}{
"type": "cnc-command-result",
"data": {
"id": "cmd-123",
"command": "G0 X10",
"status": "success",
"timestamp": "2024-01-15T10:30:01.000Z"
}
}{
"type": "gcode-updated",
"data": {
"filename": "part.gcode",
"totalLines": 5000,
"size": 125000,
"isTemporary": false
}
}{
"type": "settings-changed",
"data": { /* changed fields */ }
}{
"type": "plugins:tools-changed",
"data": {
"pluginId": "com.example.plugin",
"action": "enabled"
}
}{ "type": "flash:message", "data": { "message": "Erasing..." } }
{ "type": "flash:progress", "data": { "value": 50, "total": 100 } }
{ "type": "flash:error", "data": { "error": "Connection lost" } }
{ "type": "flash:end", "data": {} }{
"type": "cnc:command",
"data": {
"command": "G0 X10 Y20",
"commandId": "optional-id",
"meta": { "sourceId": "my-app" }
}
}{
"type": "jog:start",
"data": {
"axis": "X",
"direction": 1,
"distance": 10,
"feedRate": 5000
}
}{ "type": "jog:stop", "data": {} }{ "type": "job:progress:close" }The machineState object contains:
| Property | Type | Description |
|---|---|---|
connected |
boolean | CNC connection status |
status |
string |
idle, run, hold, alarm, door, check
|
tool |
number | Current tool number |
spindleSpeed |
number | Current spindle RPM |
feedRateCommanded |
number | Commanded feed rate |
feedRateOverride |
number | Feed rate override % (0-200) |
spindleSpeedOverride |
number | Spindle override % (0-200) |
MPos |
object | Machine position { x, y, z, a?, b?, c? }
|
WPos |
object | Work position { x, y, z, a?, b?, c? }
|
Ln |
number | Currently executing line number (grblHAL) |
homed |
boolean | Homing status |
isProbing |
boolean | Probe operation in progress |
isToolChanging |
boolean | Tool change in progress |
alarms |
array | Active alarm codes |
All endpoints return errors in this format:
{
"error": "Error message description"
}HTTP Status Codes:
-
200- Success -
400- Bad Request (invalid parameters) -
404- Not Found -
500- Server Error
No rate limits are enforced. However, for optimal performance:
- Batch commands when possible
- Use WebSocket for real-time updates instead of polling
- Limit status polling to 100ms intervals minimum
const WebSocket = require('ws');
// Connect to ncSender
const ws = new WebSocket('ws://localhost:8090');
ws.on('open', () => {
console.log('Connected to ncSender');
// Send a G-code command
ws.send(JSON.stringify({
type: 'cnc:command',
data: { command: 'G0 X10 Y10' }
}));
});
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'server-state-updated') {
console.log('Position:', msg.data.machineState.WPos);
}
if (msg.type === 'cnc-command-result') {
console.log('Command result:', msg.data.status);
}
});