API Documentation
Overview
Section titled “Overview”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.
[!TIP] Looking for automated integrations? See our MCP Server Guide or our API Sync Strategy.
Table of Contents
Section titled “Table of Contents”- REST Standards & Best Practices
- HTTP Status Codes
- Response Formats
- Error Handling
- Validation Rules
- Authentication
- Core REST APIs
- Job Lifecycle APIs
- Operation Lifecycle APIs
- NCR (Non-Conformance Report) APIs
- Webhook Events
- MCP Server Integration
- Database Indexing
REST Standards & Best Practices
Section titled “REST Standards & Best Practices”Eryxon Flow API follows REST best practices with comprehensive validation, clear error messages, and proper HTTP semantics.
Key Principles
Section titled “Key Principles”✅ Proper HTTP Status Codes - Meaningful status codes for every response ✅ Standardized Responses - Consistent success/error formats ✅ Comprehensive Validation - Field-level validation before database operations ✅ Batch Operations - Efficient foreign key validation ✅ Clear Error Messages - Actionable error details ✅ Type Safety - Schema validation for all requests
HTTP Status Codes
Section titled “HTTP Status Codes”All API endpoints use proper REST status codes:
Success Codes (2xx)
Section titled “Success Codes (2xx)”| Code | Meaning | Usage |
|---|---|---|
| 200 OK | Success | GET requests, successful PATCH/DELETE |
| 201 Created | Resource created | Successful POST requests |
| 204 No Content | Success with no body | Alternative for DELETE (not currently used) |
Client Error Codes (4xx)
Section titled “Client Error Codes (4xx)”| Code | Meaning | Usage | Example |
|---|---|---|---|
| 400 Bad Request | Malformed request | Invalid JSON, malformed query params | {"error": "Invalid JSON in request body"} |
| 401 Unauthorized | Authentication failed | Invalid/missing API key | {"error": "Invalid or missing API key"} |
| 402 Payment Required | Quota exceeded | Plan limits reached | {"error": "Job limit exceeded (50/50)"} |
| 403 Forbidden | Access denied | Tenant isolation violation | {"error": "Access denied to this resource"} |
| 404 Not Found | Resource doesn’t exist | Job/part/operation ID not found | {"error": "Job with ID xxx not found"} |
| 409 Conflict | Resource conflict | Duplicate job_number, state violations | {"error": "Job number JOB-001 already exists"} |
| 422 Unprocessable Entity | Validation error | Well-formed but invalid data | See Validation Errors below |
| 429 Too Many Requests | Rate limit exceeded | Too many API calls | {"error": "Rate limit exceeded"} |
Server Error Codes (5xx)
Section titled “Server Error Codes (5xx)”| Code | Meaning | Usage |
|---|---|---|
| 500 Internal Server Error | Server error | Unexpected errors, database failures |
Status Code Decision Tree
Section titled “Status Code Decision Tree”Request received├─ Malformed JSON/query params? → 400 Bad Request├─ Invalid/missing API key? → 401 Unauthorized├─ Quota exceeded? → 402 Payment Required├─ Wrong tenant? → 403 Forbidden├─ Resource not found? → 404 Not Found├─ Duplicate/conflict? → 409 Conflict├─ Validation failed? → 422 Unprocessable Entity├─ Rate limit hit? → 429 Too Many Requests├─ Server error? → 500 Internal Server Error└─ Success ├─ Created resource? → 201 Created └─ Otherwise → 200 OKResponse Formats
Section titled “Response Formats”Success Response
Section titled “Success Response”All successful API responses follow this structure:
{ "success": true, "data": { ... }, "meta": { "pagination": { "limit": 100, "offset": 0, "total": 250 } }}Fields:
success- Alwaystruefor successful responsesdata- The response payload (object or array)meta- Optional metadata (pagination, filters, etc.)
Error Response
Section titled “Error Response”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- Alwaysfalsefor errorserror.code- Machine-readable error codeerror.message- Human-readable summaryerror.details- Array of specific errors (for validation)error.statusCode- HTTP status code
Error Handling Reference
Section titled “Error Handling Reference”Validation Error Format
Section titled “Validation Error Format”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 }}Validation Constraint Types
Section titled “Validation Constraint Types”| Constraint | Meaning | Example |
|---|---|---|
NOT_NULL | Required field missing | job_number is required |
FK_CONSTRAINT | Foreign key violation | cell_id references non-existent cell |
FK_REQUIRED | Required foreign key missing | part_id is required |
UUID_FORMAT | Invalid UUID format | id must be a valid UUID |
TYPE_MISMATCH | Wrong data type | Expected number, got string |
MIN_VALUE | Value too small | quantity must be >= 1 |
MAX_VALUE | Value too large | priority must be <= 100 |
MIN_LENGTH | String too short | job_number must be at least 1 character |
MAX_LENGTH | String too long | job_number must be at most 255 characters |
PATTERN_MISMATCH | Doesn’t match pattern | Invalid format |
ENUM_CONSTRAINT | Invalid enum value | status must be one of: not_started, in_progress, completed |
DATE_FORMAT | Invalid date format | due_date must be ISO 8601 format |
UNIQUE_CONSTRAINT | Duplicate value | Duplicate part numbers found |
CIRCULAR_REFERENCE | Self-referential FK | Part cannot be its own parent |
Common Error Codes
Section titled “Common Error Codes”| Code | HTTP | Description | Example |
|---|---|---|---|
VALIDATION_ERROR | 422 | Field validation failed | Missing required field |
UNAUTHORIZED | 401 | Authentication failed | Invalid API key |
NOT_FOUND | 404 | Resource doesn’t exist | Job ID not found |
CONFLICT | 409 | Resource conflict | Duplicate job_number |
QUOTA_EXCEEDED | 402 | Plan limit reached | Job limit: 50/50 |
BAD_REQUEST | 400 | Malformed request | Invalid JSON |
FORBIDDEN | 403 | Access denied | Wrong tenant |
METHOD_NOT_ALLOWED | 405 | HTTP method not supported | POST to GET-only endpoint |
INTERNAL_ERROR | 500 | Server error | Database connection failed |
Validation Rules
Section titled “Validation Rules”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…)
Operations
Section titled “Operations”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
Issues/NCRs
Section titled “Issues/NCRs”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)
Authentication
Section titled “Authentication”All API endpoints require an API key in the Authorization header:
Authorization: Bearer ery_live_xxxxxxxxxxAPI keys are managed through the admin interface and come in two types:
ery_live_*- Production keysery_test_*- Testing keys
Core REST APIs
Section titled “Core REST APIs”Jobs API
Section titled “Jobs API”Base URL: /functions/v1/api-jobs
GET - List Jobs
Section titled “GET - List Jobs”GET /api-jobs?status=in_progress&customer=ACME&limit=100&offset=0Query Parameters:
status- Filter by status:not_started,in_progress,completed,on_holdcustomer- 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-2024-001", "customer": "ACME Corp", "status": "in_progress", "due_date": "2024-12-31", "started_at": "2024-01-15T10:00:00Z", "notes": "Rush order", "parts": [...] } ], "pagination": { "limit": 100, "offset": 0, "total": 150 } }}POST - Create Job
Section titled “POST - Create Job”POST /api-jobsContent-Type: application/json
{ "job_number": "JOB-2024-001", "customer": "ACME Corp", "due_date": "2024-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-2024-001", "parts": [ { "part_id": "uuid", "part_number": "PART-001", "operations": [ { "operation_id": "uuid", "operation_name": "CNC Milling" } ] } ] }}Webhook Triggered: job.created
PATCH - Update Job
Section titled “PATCH - Update Job”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
DELETE - Delete Job
Section titled “DELETE - Delete Job”DELETE /api-jobs?id=<job-id>Parts API
Section titled “Parts API”Base URL: /functions/v1/api-parts
GET - List Parts
Section titled “GET - List Parts”GET /api-parts?job_id=<uuid>&status=in_progress&material=AluminumQuery Parameters:
job_id- Filter by jobpart_number- Filter by part number (partial match)material- Filter by materialstatus- Filter by statuslimit,offset- Pagination
POST - Create Part
Section titled “POST - Create Part”POST /api-parts{ "job_id": "uuid", "part_number": "PART-002", "material": "Steel 4140", "quantity": 5, "parent_part_number": "PART-001", "notes": "Sub-assembly"}Operations API
Section titled “Operations API”Base URL: /functions/v1/api-operations
GET - List Operations
Section titled “GET - List Operations”GET /api-operations?part_id=<uuid>&status=in_progress&cell_name=MillQuery Parameters:
part_id- Filter by partjob_id- Filter by job (finds parts first, then operations)cell_id- Filter by cellcell_name- Filter by cell name (partial match)status- Filter by statusassigned_operator_id- Filter by assigned operatorsearch- Search operation namessort_by- Sort field:sequence,created_at,estimated_time,actual_time,statussort_order-ascordescinclude_count- Include total count (true/false)
POST - Create Operation
Section titled “POST - Create Operation”POST /api-operations{ "part_id": "uuid", "cell_id": "uuid", "operation_name": "Welding", "estimated_time": 60, "sequence": 2, "notes": "TIG weld only"}Job Lifecycle APIs
Section titled “Job Lifecycle APIs”Base URL: /functions/v1/api-job-lifecycle
Start Job
Section titled “Start Job”POST /api-job-lifecycle/start?id=<job-id>What it does:
- Changes status from
not_startedoron_hold→in_progress - Sets
started_attimestamp (first time only) - Clears
paused_at - Triggers
job.startedwebhook
Stop/Pause Job
Section titled “Stop/Pause Job”POST /api-job-lifecycle/stop?id=<job-id>What it does:
- Changes status from
in_progress→on_hold - Sets
paused_attimestamp - Triggers
job.stoppedwebhook
Complete Job
Section titled “Complete Job”POST /api-job-lifecycle/complete?id=<job-id>What it does:
- Changes status from
in_progress→completed - Sets
completed_attimestamp - Calculates and stores
actual_duration(in minutes) - Triggers
job.completedwebhook
Resume Job
Section titled “Resume Job”POST /api-job-lifecycle/resume?id=<job-id>What it does:
- Changes status from
on_hold→in_progress - Sets
resumed_attimestamp - Clears
paused_at - Triggers
job.resumedwebhook
Example Response:
{ "success": true, "data": { "job": { "id": "uuid", "job_number": "JOB-2024-001", "status": "in_progress", "started_at": "2024-01-15T10:00:00Z", "completed_at": null }, "operation": "start", "previous_status": "not_started", "new_status": "in_progress" }}Operation Lifecycle APIs
Section titled “Operation Lifecycle APIs”Base URL: /functions/v1/api-operation-lifecycle
Start Operation
Section titled “Start Operation”POST /api-operation-lifecycle/start?id=<operation-id>&user_id=<user-id>What it does:
- Changes status to
in_progress - Sets
started_attimestamp - Creates time entry with
start_time - Triggers
operation.startedwebhook
Pause Operation
Section titled “Pause Operation”POST /api-operation-lifecycle/pause?id=<operation-id>What it does:
- Changes status to
on_hold - Sets
paused_attimestamp - Ends active time entries (calculates duration)
- Updates
actual_timewith cumulative time - Triggers
operation.pausedwebhook
Resume Operation
Section titled “Resume Operation”POST /api-operation-lifecycle/resume?id=<operation-id>&user_id=<user-id>What it does:
- Changes status to
in_progress - Sets
resumed_attimestamp - Creates new time entry
- Triggers
operation.resumedwebhook
Complete Operation
Section titled “Complete Operation”POST /api-operation-lifecycle/complete?id=<operation-id>What it does:
- Changes status to
completed - Sets
completed_attimestamp - Sets
completion_percentageto 100 - Ends all active time entries
- Calculates final
actual_time - Triggers
operation.completedwebhook
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-2024-001" } } }, "operation_type": "complete", "previous_status": "in_progress", "new_status": "completed", "time_entry_ended": true }}NCR APIs
Section titled “NCR APIs”Create NCR (Non-Conformance Report)
Section titled “Create NCR (Non-Conformance Report)”POST /api-issuesContent-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-2024-0001", "title": "Dimensional Out of Tolerance", "severity": "high", "status": "open", "issue_type": "ncr", "ncr_category": "process", "disposition": "rework", "created_at": "2024-01-15T14:30:00Z" } }}Webhook Triggered: ncr.created
NCR Fields
Section titled “NCR Fields”Required:
operation_id- Where the non-conformance occurredtitle- Short summaryseverity-low,medium,high,critical
NCR-Specific:
issue_type- Set to"ncr"(auto-generates NCR number)ncr_category-material,process,equipment,design,supplier,documentation,otheraffected_quantity- Number of parts affecteddisposition-use_as_is,rework,repair,scrap,return_to_supplierroot_cause- Root cause analysiscorrective_action- Immediate action takenpreventive_action- Long-term preventionverification_required- Requires verification after corrective actionverified_by_id- User who verified (auto-setsverified_at)
Update NCR
Section titled “Update NCR”PATCH /api-issues?id=<ncr-id>{ "status": "resolved", "resolution_notes": "All parts re-machined and inspected. Tool tracking implemented.", "verified_by_id": "uuid"}List NCRs
Section titled “List NCRs”GET /api-issues?issue_type=ncr&severity=high&status=openSubsteps API
Section titled “Substeps API”Base URL: /functions/v1/api-substeps
Add Substep
Section titled “Add Substep”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
Complete Substep
Section titled “Complete Substep”PATCH /api-substeps?id=<substep-id>{ "completed": true}Webhook Triggered: step.completed
Webhook Events
Section titled “Webhook Events”Eryxon automatically sends webhooks to registered endpoints for the following events:
Job Events
Section titled “Job Events”job.created- New job created via APIjob.started- Job startedjob.stopped- Job paused/stoppedjob.resumed- Job resumed from pausejob.completed- Job completedjob.updated- Job fields updated
Part Events
Section titled “Part Events”part.created- New part createdpart.updated- Part fields updatedpart.started- Work started on partpart.completed- Part completed
Operation Events
Section titled “Operation Events”operation.started- Operation started (operator begins work)operation.paused- Operation pausedoperation.resumed- Operation resumedoperation.completed- Operation completed
Issue/NCR Events
Section titled “Issue/NCR Events”issue.created- General issue createdncr.created- NCR (Non-Conformance Report) createdncr.verified- NCR corrective action verified
Step Events
Section titled “Step Events”step.added- Substep added to operationstep.completed- Substep marked as completed
Webhook Payload Example
Section titled “Webhook Payload Example”{ "event_type": "job.completed", "timestamp": "2024-01-15T16:30:00Z", "tenant_id": "uuid", "data": { "job_id": "uuid", "job_number": "JOB-2024-001", "customer": "ACME Corp", "previous_status": "in_progress", "new_status": "completed", "started_at": "2024-01-15T10:00:00Z", "completed_at": "2024-01-15T16:30:00Z", "actual_duration": 390 }}Webhook Security
Section titled “Webhook Security”All webhooks include HMAC-SHA256 signatures for authenticity verification:
Headers:
X-Eryxon-Signature- HMAC-SHA256 signature of the payloadX-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) );}Managing Webhooks
Section titled “Managing Webhooks”Create Webhook:
POST /api-webhooks{ "url": "https://your-erp.com/webhooks/eryxon", "events": ["job.created", "job.completed", "ncr.created"], "active": true}List Webhooks:
GET /api-webhooks?event_type=job.completed&active=trueUpdate Webhook:
PATCH /api-webhooks?id=<webhook-id>{ "active": false}MCP Server Integration
Section titled “MCP Server Integration”The Model Context Protocol (MCP) server enables AI assistants and automation tools to interact with Eryxon Flow.
Available MCP Tools
Section titled “Available MCP Tools”Fetch Operations
Section titled “Fetch Operations”fetch_jobs- List jobs with filtersfetch_parts- List parts with filtersfetch_tasks- List tasks with filtersfetch_issues- List issues with filtersfetch_ncrs- List NCRs with filtersget_dashboard_stats- Get aggregated statistics
Job Lifecycle
Section titled “Job Lifecycle”start_job- Start a jobstop_job- Stop/pause a jobcomplete_job- Complete a jobresume_job- Resume a paused jobupdate_job- Update job fieldscreate_job- Create new job
Operation Lifecycle
Section titled “Operation Lifecycle”start_operation- Start an operationpause_operation- Pause an operationcomplete_operation- Complete an operationupdate_operation- Update operation fields
NCR Management
Section titled “NCR Management”create_ncr- Create Non-Conformance Reportfetch_ncrs- List NCRs with filtering
Substep Management
Section titled “Substep Management”add_substep- Add substep to operationcomplete_substep- Mark substep as completed
MCP Server Setup
Section titled “MCP Server Setup”cd mcp-servernpm installnpm run build
export SUPABASE_URL="https://your-project.supabase.co"export SUPABASE_SERVICE_KEY="your-service-key"
npm startExample MCP Tool Usage
Section titled “Example MCP Tool Usage”// 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\": \"2024-01-15T10:00:00Z\"\n}" } ]}Database Indexing
Section titled “Database Indexing”All critical queries are optimized with database indexes:
Performance Indexes
Section titled “Performance Indexes”Jobs:
(tenant_id, status)- Status filtering(tenant_id, created_at DESC)- Recent jobs(tenant_id, due_date)- Upcoming due dates(tenant_id, customer)- Customer filtering- Full-text search on job_number, customer, notes
Parts:
(tenant_id, status)- Status filtering(tenant_id, job_id)- Parts by job(tenant_id, material)- Material filtering(parent_part_id)- Sub-assemblies- Full-text search on part_number, material, notes
Operations:
(tenant_id, status)- Status filtering(tenant_id, part_id)- Operations by part(tenant_id, cell_id)- Operations by cell(part_id, sequence)- Operation sequence(assigned_operator_id)- Operator assignments- Full-text search on operation_name, notes
Issues/NCRs:
(tenant_id, status)- Status filtering(tenant_id, severity)- Severity filtering(tenant_id, issue_type)- NCR filtering(tenant_id, ncr_number)- Unique NCR number(operation_id)- Issues by operation- Full-text search on description, resolution_notes
Time Entries:
(operation_id)- Entries by operation(user_id)- Entries by user(operation_id, end_time)- Active entries
Webhooks:
(tenant_id, active)- Active webhooks(webhook_id, created_at DESC)- Webhook logs(event_type, created_at DESC)- Event filtering
Error Handling
Section titled “Error Handling”All API endpoints return consistent error responses:
{ "success": false, "error": { "code": "VALIDATION_ERROR", "message": "Job ID is required in query string (?id=xxx)" }}Common Error Codes:
UNAUTHORIZED- Invalid or missing API keyVALIDATION_ERROR- Missing or invalid request parametersNOT_FOUND- Resource not foundINVALID_STATE_TRANSITION- Illegal status change (e.g., completing a not-started job)CONFLICT- Resource conflict (e.g., duplicate job number)INTERNAL_ERROR- Server error