Ga naar inhoud

REST API Reference

Deze inhoud is nog niet vertaald.

Eryxon Flow is a 100% API-driven manufacturing execution system. Your ERP system pushes jobs, parts, and tasks via REST API. Eryxon sends completion events back via webhooks. The MCP server enables AI/automation integration.

Most integration endpoints use Eryxon API keys (ery_live_... or ery_test_...). Admin browser endpoints such as api-export use the signed-in Supabase user session token.


All API endpoints use proper REST status codes:

CodeMeaningUsage
200 OKSuccessGET requests, successful PATCH/DELETE
201 CreatedResource createdSuccessful POST requests
204 No ContentSuccess with no bodyAlternative for DELETE (not currently used)
CodeMeaningUsageExample
400 Bad RequestMalformed requestInvalid JSON, malformed query params{"error": "Invalid JSON in request body"}
401 UnauthorizedAuthentication failedInvalid/missing API key{"error": "Invalid or missing API key"}
402 Payment RequiredQuota exceededPlan limits reached{"error": "Job limit exceeded (50/50)"}
403 ForbiddenAccess deniedTenant isolation violation{"error": "Access denied to this resource"}
404 Not FoundResource doesn’t existJob/part/operation ID not found{"error": "Job with ID xxx not found"}
409 ConflictResource conflictDuplicate job_number, state violations{"error": "Job number JOB-001 already exists"}
422 Unprocessable EntityValidation errorWell-formed but invalid dataSee Validation Errors below
429 Too Many RequestsRate limit exceededToo many API calls{"error": "Rate limit exceeded"}
CodeMeaningUsage
500 Internal Server ErrorServer errorUnexpected errors, database failures

All successful API responses follow this structure:

{
"success": true,
"data": { ... },
"meta": {
"pagination": {
"limit": 100,
"offset": 0,
"total": 250
}
}
}

Fields:

  • success - Always true for successful responses
  • data - The response payload (object or array)
  • meta - Optional metadata (pagination, filters, etc.)

All error responses follow this structure:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "job_number",
"message": "Job number is required",
"constraint": "NOT_NULL",
"entityType": "job",
"entityIndex": 0
}
],
"statusCode": 422
}
}

Fields:

  • success - Always false for errors
  • error.code - Machine-readable error code
  • error.message - Human-readable summary
  • error.details - Array of specific errors (for validation)
  • error.statusCode - HTTP status code

When validation fails (422), you’ll receive detailed field-level errors:

{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "✗ 3 validation error(s) in job",
"details": [
{
"field": "job_number",
"message": "Missing required field: job_number",
"value": null,
"constraint": "NOT_NULL",
"entityType": "job",
"entityIndex": 0
},
{
"field": "parts[0].quantity",
"message": "Part quantity must be >= 1",
"value": 0,
"constraint": "MIN_VALUE",
"entityType": "part",
"entityIndex": 0
},
{
"field": "parts[0].operations[0].cell_id",
"message": "Foreign key cell_id references non-existent record: abc-123",
"value": "abc-123",
"constraint": "FK_CONSTRAINT",
"entityType": "operation",
"entityIndex": 0
}
],
"statusCode": 422
}
}
ConstraintMeaningExample
NOT_NULLRequired field missingjob_number is required
FK_CONSTRAINTForeign key violationcell_id references non-existent cell
FK_REQUIREDRequired foreign key missingpart_id is required
UUID_FORMATInvalid UUID formatid must be a valid UUID
TYPE_MISMATCHWrong data typeExpected number, got string
MIN_VALUEValue too smallquantity must be >= 1
MAX_VALUEValue too largepriority must be <= 100
MIN_LENGTHString too shortjob_number must be at least 1 character
MAX_LENGTHString too longjob_number must be at most 255 characters
PATTERN_MISMATCHDoesn’t match patternInvalid format
ENUM_CONSTRAINTInvalid enum valuestatus must be one of: not_started, in_progress, completed
DATE_FORMATInvalid date formatdue_date must be ISO 8601 format
UNIQUE_CONSTRAINTDuplicate valueDuplicate part numbers found
CIRCULAR_REFERENCESelf-referential FKPart cannot be its own parent
CodeHTTPDescriptionExample
VALIDATION_ERROR422Field validation failedMissing required field
UNAUTHORIZED401Authentication failedInvalid API key
NOT_FOUND404Resource doesn’t existJob ID not found
CONFLICT409Resource conflictDuplicate job_number
QUOTA_EXCEEDED402Plan limit reachedJob limit: 50/50
BAD_REQUEST400Malformed requestInvalid JSON
FORBIDDEN403Access deniedWrong tenant
METHOD_NOT_ALLOWED405HTTP method not supportedPOST to GET-only endpoint
INTERNAL_ERROR500Server errorDatabase connection failed

Required Fields:

  • job_number (string, 1-255 chars, unique per tenant)
  • parts (array, min 1 part)

Optional Fields:

  • customer_name (string, max 255 chars)
  • due_date (ISO 8601 date string)
  • priority (integer, >= 0)
  • current_cell_id (UUID, must exist in cells)
  • status (enum: not_started, in_progress, on_hold, completed)
  • description (string)
  • metadata (JSON object)

Business Rules:

  • Job number must be unique within tenant
  • At least one part required
  • Status transitions must be valid (not_started → in_progress → completed)
  • All foreign keys must reference existing records in same tenant

Required Fields:

  • job_id (UUID, must exist)
  • part_number (string, unique within job)
  • quantity (integer, >= 1)
  • operations (array, min 1 operation)

Optional Fields:

  • material (string)
  • parent_part_id (UUID, must exist in same job, cannot be self)
  • current_cell_id (UUID, must exist)
  • material_id (UUID, must exist)
  • description (string)
  • drawing_url (string)
  • step_file_url (string)

Business Rules:

  • Part number unique within job
  • Parent part must belong to same job
  • Cannot be own parent (circular reference check)
  • Operations must have sequential sequence numbers (1, 2, 3…)

Required Fields:

  • part_id (UUID, must exist)
  • operation_name (string, 1-255 chars)
  • sequence (integer, >= 1, unique within part)

Optional Fields:

  • cell_id (UUID, must exist)
  • assigned_operator_id (UUID, must exist in profiles)
  • estimated_time_minutes (number, >= 0)
  • setup_time_minutes (number, >= 0)
  • instructions (string)
  • status (enum: not_started, in_progress, paused, completed)

Business Rules:

  • Sequence must be positive integer
  • Sequence unique within part
  • All FKs must belong to same tenant

Required Fields:

  • operation_id (UUID, must exist)
  • title (string, 1-255 chars)
  • description (string, min 1 char)

Optional Fields:

  • severity (enum: low, medium, high, critical)
  • status (enum: open, in_progress, resolved, closed)
  • issue_type (enum: general, ncr)
  • reported_by_id (UUID, must exist)
  • resolved_by_id (UUID, must exist)
  • verified_by_id (UUID, must exist)

NCR-Specific Fields (when issue_type = "ncr"):

  • ncr_number (auto-generated if not provided)
  • ncr_category (enum: material, process, equipment, design, supplier, documentation, other)
  • ncr_disposition (enum: use_as_is, rework, repair, scrap, return_to_supplier)
  • root_cause (string)
  • corrective_action (string)
  • preventive_action (string)
  • affected_quantity (integer)
  • verification_required (boolean)

Most integration endpoints require an API key passed as a Bearer token:

Required header

Authorization: Bearer ery_live_xxxxxxxxxx

API keys are managed through the admin interface and come in two types:

  • ery_live_* - Production keys
  • ery_test_* - Testing keys

Security: API keys are verified using SHA-256 hashing against the api_keys table and compared with constant-time matching in the edge auth helper. Never expose your API keys in client-side code or public repositories.

Admin-only endpoints used from the web app, such as api-export, use the signed-in Supabase user session token instead of an Eryxon API key.


Base URL: /functions/v1/api-jobs

Terminal window
GET /api-jobs?status=in_progress&customer=ACME&limit=100&offset=0

Query Parameters:

  • status - Filter by status: not_started, in_progress, completed, on_hold
  • customer - Filter by customer name (partial match)
  • job_number - Filter by job number (partial match)
  • limit - Results per page (default: 100, max: 1000)
  • offset - Pagination offset (default: 0)

Response:

{
"success": true,
"data": {
"jobs": [
{
"id": "uuid",
"job_number": "JOB-2026-001",
"customer": "ACME Corp",
"status": "in_progress",
"due_date": "2026-12-31",
"started_at": "2026-01-15T10:00:00Z",
"notes": "Rush order",
"parts": [...]
}
],
"pagination": {
"limit": 100,
"offset": 0,
"total": 150
}
}
}
Terminal window
POST /api-jobs
Content-Type: application/json
{
"job_number": "JOB-2026-001",
"customer": "ACME Corp",
"due_date": "2026-12-31",
"notes": "Rush order",
"metadata": {"po_number": "PO-12345"},
"parts": [
{
"part_number": "PART-001",
"material": "Aluminum 6061",
"quantity": 10,
"file_paths": ["s3://drawings/part-001.pdf"],
"operations": [
{
"operation_name": "CNC Milling",
"cell_name": "Mill-01",
"estimated_time": 120,
"sequence": 1,
"notes": "Use 0.5\" end mill"
}
]
}
]
}

Response:

{
"success": true,
"data": {
"job_id": "uuid",
"job_number": "JOB-2026-001",
"parts": [
{
"part_id": "uuid",
"part_number": "PART-001",
"operations": [
{
"operation_id": "uuid",
"operation_name": "CNC Milling"
}
]
}
]
}
}

Webhook Triggered: job.created

Terminal window
PATCH /api-jobs?id=<job-id>
Content-Type: application/json
{
"status": "completed",
"notes": "Finished ahead of schedule"
}

Allowed Fields: status, customer, due_date, due_date_override, notes, metadata

Terminal window
DELETE /api-jobs?id=<job-id>

Base URL: /functions/v1/api-parts

Terminal window
GET /api-parts?job_id=<uuid>&status=in_progress&material=Aluminum

Query Parameters:

  • job_id - Filter by job
  • part_number - Filter by part number (partial match)
  • material - Filter by material
  • status - Filter by status
  • limit, offset - Pagination
Terminal window
POST /api-parts
{
"job_id": "uuid",
"part_number": "PART-002",
"material": "Steel 4140",
"quantity": 5,
"parent_part_number": "PART-001",
"notes": "Sub-assembly"
}

Base URL: /functions/v1/api-operations

Terminal window
GET /api-operations?part_id=<uuid>&status=in_progress&cell_name=Mill

Query Parameters:

  • part_id - Filter by part
  • job_id - Filter by job (finds parts first, then operations)
  • cell_id - Filter by cell
  • cell_name - Filter by cell name (partial match)
  • status - Filter by status
  • assigned_operator_id - Filter by assigned operator
  • search - Search operation names
  • sort_by - Sort field: sequence, created_at, estimated_time, actual_time, status
  • sort_order - asc or desc
  • include_count - Include total count (true/false)
Terminal window
POST /api-operations
{
"part_id": "uuid",
"cell_id": "uuid",
"operation_name": "Welding",
"estimated_time": 60,
"sequence": 2,
"notes": "TIG weld only"
}

Base URL: /functions/v1/api-job-lifecycle

Terminal window
POST /api-job-lifecycle/start?id=<job-id>

What it does:

  • Changes status from not_started or on_holdin_progress
  • Sets started_at timestamp (first time only)
  • Clears paused_at
  • Triggers job.started webhook
Terminal window
POST /api-job-lifecycle/stop?id=<job-id>

What it does:

  • Changes status from in_progresson_hold
  • Sets paused_at timestamp
  • Triggers job.stopped webhook
Terminal window
POST /api-job-lifecycle/complete?id=<job-id>

What it does:

  • Changes status from in_progresscompleted
  • Sets completed_at timestamp
  • Calculates and stores actual_duration (in minutes)
  • Triggers job.completed webhook
Terminal window
POST /api-job-lifecycle/resume?id=<job-id>

What it does:

  • Changes status from on_holdin_progress
  • Sets resumed_at timestamp
  • Clears paused_at
  • Triggers job.resumed webhook

Example Response:

{
"success": true,
"data": {
"job": {
"id": "uuid",
"job_number": "JOB-2026-001",
"status": "in_progress",
"started_at": "2026-01-15T10:00:00Z",
"completed_at": null
},
"operation": "start",
"previous_status": "not_started",
"new_status": "in_progress"
}
}

Base URL: /functions/v1/api-operation-lifecycle

Terminal window
POST /api-operation-lifecycle/start?id=<operation-id>&user_id=<user-id>

What it does:

  • Changes status to in_progress
  • Sets started_at timestamp
  • Creates time entry with start_time
  • Triggers operation.started webhook
Terminal window
POST /api-operation-lifecycle/pause?id=<operation-id>

What it does:

  • Changes status to on_hold
  • Sets paused_at timestamp
  • Ends active time entries (calculates duration)
  • Updates actual_time with cumulative time
  • Triggers operation.paused webhook
Terminal window
POST /api-operation-lifecycle/resume?id=<operation-id>&user_id=<user-id>

What it does:

  • Changes status to in_progress
  • Sets resumed_at timestamp
  • Creates new time entry
  • Triggers operation.resumed webhook
Terminal window
POST /api-operation-lifecycle/complete?id=<operation-id>

What it does:

  • Changes status to completed
  • Sets completed_at timestamp
  • Sets completion_percentage to 100
  • Ends all active time entries
  • Calculates final actual_time
  • Triggers operation.completed webhook

Example Response:

{
"success": true,
"data": {
"operation": {
"id": "uuid",
"operation_name": "CNC Milling",
"status": "completed",
"estimated_time": 120,
"actual_time": 135,
"completion_percentage": 100,
"part": {
"part_number": "PART-001",
"job": {
"job_number": "JOB-2026-001"
}
}
},
"operation_type": "complete",
"previous_status": "in_progress",
"new_status": "completed",
"time_entry_ended": true
}
}

Base URL: /functions/v1/api-batches

Terminal window
GET /api-batches?status=ready&batch_type=laser_nesting&limit=50

Query Parameters:

  • status - Filter by draft, ready, in_progress, completed, or cancelled
  • batch_type - Filter by laser_nesting, tube_batch, saw_batch, finishing_batch, or general
  • cell_id - Filter by production cell
  • material - Filter by material label
  • search - Search batch_number and material
Terminal window
POST /api-batches
{
"batch_number": "NEST-2026-001",
"batch_type": "laser_nesting",
"cell_id": "uuid",
"material": "SS304",
"thickness_mm": 2,
"operation_ids": ["uuid", "uuid"]
}

cell_id, parent_batch_id, and operation_ids are validated against the authenticated tenant. Operations must not already be assigned to another batch.

Terminal window
PATCH /api-batches?id=<batch-id>
{
"status": "ready",
"notes": "Released to production"
}

Use api-batch-lifecycle/add-operations to append operations.

Base URL: /functions/v1/api-batch-lifecycle

Terminal window
POST /api-batch-lifecycle/start?id=<batch-id>
POST /api-batch-lifecycle/stop?id=<batch-id>
POST /api-batch-lifecycle/add-operations?id=<batch-id>

Start moves a draft or ready batch to in_progress. Stop completes the batch and distributes active tracked time across operations. Add-operations appends tenant-validated operations while the batch is still draft or ready.

Webhook Triggered: batch.started, batch.completed


Terminal window
POST /api-issues
Content-Type: application/json
{
"operation_id": "uuid",
"title": "Dimensional Out of Tolerance",
"description": "Part hole diameter measured 0.505\", spec is 0.500\" ±0.002\"",
"severity": "high",
"issue_type": "ncr",
"ncr_category": "process",
"affected_quantity": 5,
"disposition": "rework",
"root_cause": "Tool wear - end mill exceeded replacement interval",
"corrective_action": "Replaced tool, re-machined 5 parts",
"preventive_action": "Implemented tool life tracking in system",
"verification_required": true,
"reported_by_id": "uuid"
}

Response:

{
"success": true,
"data": {
"issue": {
"id": "uuid",
"ncr_number": "NCR-2026-0001",
"title": "Dimensional Out of Tolerance",
"severity": "high",
"status": "open",
"issue_type": "ncr",
"ncr_category": "process",
"disposition": "rework",
"created_at": "2026-01-15T14:30:00Z"
}
}
}

Webhook Triggered: ncr.created

Required:

  • operation_id - Where the non-conformance occurred
  • title - Short summary
  • severity - low, medium, high, critical

NCR-Specific:

  • issue_type - Set to "ncr" (auto-generates NCR number)
  • ncr_category - material, process, equipment, design, supplier, documentation, other
  • affected_quantity - Number of parts affected
  • disposition - use_as_is, rework, repair, scrap, return_to_supplier
  • root_cause - Root cause analysis
  • corrective_action - Immediate action taken
  • preventive_action - Long-term prevention
  • verification_required - Requires verification after corrective action
  • verified_by_id - User who verified (auto-sets verified_at)
Terminal window
PATCH /api-issues?id=<ncr-id>
{
"status": "resolved",
"resolution_notes": "All parts re-machined and inspected. Tool tracking implemented.",
"verified_by_id": "uuid"
}
Terminal window
GET /api-issues?issue_type=ncr&severity=high&status=open

Base URL: /functions/v1/api-substeps

Terminal window
POST /api-substeps
{
"operation_id": "uuid",
"description": "Measure hole diameter with caliper",
"sequence": 1
}

Response:

{
"success": true,
"data": {
"substep": {
"id": "uuid",
"operation_id": "uuid",
"description": "Measure hole diameter with caliper",
"sequence": 1,
"completed": false
}
}
}

Webhook Triggered: step.added

Terminal window
PATCH /api-substeps?id=<substep-id>
{
"completed": true
}

Webhook Triggered: step.completed


Eryxon automatically sends webhooks to registered endpoints for the following events:

  • job.created - New job created via API
  • job.started - Job started
  • job.stopped - Job paused/stopped
  • job.resumed - Job resumed from pause
  • job.completed - Job completed
  • job.updated - Job fields updated
  • part.created - New part created
  • part.updated - Part fields updated
  • part.started - Work started on part
  • part.completed - Part completed
  • operation.started - Operation started (operator begins work)
  • operation.paused - Operation paused
  • operation.resumed - Operation resumed
  • operation.completed - Operation completed
  • batch.started - Batch started
  • batch.completed - Batch completed
  • issue.created - General issue created
  • ncr.created - NCR (Non-Conformance Report) created
  • ncr.verified - NCR corrective action verified
  • step.added - Substep added to operation
  • step.completed - Substep marked as completed
{
"event_type": "job.completed",
"timestamp": "2026-01-15T16:30:00Z",
"tenant_id": "uuid",
"data": {
"job_id": "uuid",
"job_number": "JOB-2026-001",
"customer": "ACME Corp",
"previous_status": "in_progress",
"new_status": "completed",
"started_at": "2026-01-15T10:00:00Z",
"completed_at": "2026-01-15T16:30:00Z",
"actual_duration": 390
}
}

All webhooks include HMAC-SHA256 signatures for authenticity verification:

Headers:

  • X-Eryxon-Signature - HMAC-SHA256 signature of the payload
  • X-Eryxon-Event - Event type (e.g., job.completed)
  • Content-Type: application/json

Verification (Node.js example):

const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const hmac = crypto.createHmac('sha256', secret);
const digest = hmac.update(payload).digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}

Create Webhook:

Terminal window
POST /api-webhooks
{
"url": "https://your-erp.com/webhooks/eryxon",
"events": ["job.created", "job.completed", "ncr.created"],
"active": true
}

List Webhooks:

Terminal window
GET /api-webhooks?event_type=job.completed&active=true

Update Webhook:

Terminal window
PATCH /api-webhooks?id=<webhook-id>
{
"active": false
}

The Model Context Protocol (MCP) server enables AI assistants and automation tools to interact with Eryxon Flow.

  • fetch_jobs - List jobs with filters
  • fetch_parts - List parts with filters
  • fetch_tasks - List tasks with filters
  • fetch_issues - List issues with filters
  • fetch_ncrs - List NCRs with filters
  • get_dashboard_stats - Get aggregated statistics
  • start_job - Start a job
  • stop_job - Stop/pause a job
  • complete_job - Complete a job
  • resume_job - Resume a paused job
  • update_job - Update job fields
  • create_job - Create new job
  • start_operation - Start an operation
  • pause_operation - Pause an operation
  • complete_operation - Complete an operation
  • update_operation - Update operation fields
  • create_ncr - Create Non-Conformance Report
  • fetch_ncrs - List NCRs with filtering
  • add_substep - Add substep to operation
  • complete_substep - Mark substep as completed
Terminal window
cd mcp-server
npm install
npm run build
# Set environment variables
export SUPABASE_URL="https://your-project.supabase.co"
export SUPABASE_SERVICE_KEY="your-service-key"
# Run the server
npm start
// Using Claude Desktop or other MCP client
{
"name": "start_job",
"arguments": {
"id": "uuid-of-job"
}
}
// Response
{
"content": [
{
"type": "text",
"text": "Job started successfully:\n{\n \"id\": \"uuid\",\n \"status\": \"in_progress\",\n \"started_at\": \"2026-01-15T10:00:00Z\"\n}"
}
]
}

Self-hosted: No usage limits. You control the infrastructure.

Cloud (eryxon.eu): API requests are rate-limited per day based on your plan:

PlanRequests per day
free100
pro1,000
premium10,000
enterpriseunlimited

When you exceed your rate limit, the API returns 429 Too Many Requests.