Ofelia provides a RESTful API for job management, monitoring, and configuration. All API endpoints are available when the web UI is enabled with --enable-web.
The API uses JWT tokens for authentication when security is enabled.
POST /api/login
Content-Type: application/json
{
"username": "admin",
"password": "secret"
}Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires": "2024-01-01T00:00:00Z"
}Include the JWT token in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...POST /api/refresh
Authorization: Bearer <current-token>Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expires": "2024-01-01T00:00:00Z"
}GET /api/jobsResponse:
{
"jobs": [
{
"name": "backup",
"type": "exec",
"schedule": "@daily",
"command": "backup.sh",
"container": "myapp",
"lastRun": "2024-01-01T00:00:00Z",
"nextRun": "2024-01-02T00:00:00Z",
"status": "success",
"origin": "config",
"enabled": true
},
{
"name": "cleanup",
"type": "run",
"schedule": "0 2 * * *",
"image": "alpine:latest",
"command": "cleanup.sh",
"lastRun": "2024-01-01T02:00:00Z",
"nextRun": "2024-01-02T02:00:00Z",
"status": "running",
"origin": "docker-label",
"enabled": true
}
],
"total": 2,
"running": 1
}GET /api/job/{name}Response:
{
"name": "backup",
"type": "exec",
"schedule": "@daily",
"command": "backup.sh",
"container": "myapp",
"user": "nobody",
"environment": ["BACKUP_DIR=/data"],
"tty": false,
"lastRun": "2024-01-01T00:00:00Z",
"nextRun": "2024-01-02T00:00:00Z",
"status": "success",
"origin": "config",
"enabled": true,
"history": [
{
"executionId": "550e8400-e29b-41d4-a716-446655440000",
"startTime": "2024-01-01T00:00:00Z",
"endTime": "2024-01-01T00:05:00Z",
"duration": 300000000000,
"status": "success",
"output": "Backup completed successfully",
"error": null
}
],
"metrics": {
"totalRuns": 30,
"successfulRuns": 29,
"failedRuns": 1,
"averageDuration": 285000000000,
"lastSuccess": "2024-01-01T00:00:00Z",
"lastFailure": "2023-12-15T00:00:00Z"
}
}POST /api/job/{name}/runResponse:
{
"executionId": "550e8400-e29b-41d4-a716-446655440001",
"job": "backup",
"startTime": "2024-01-01T12:00:00Z",
"status": "running"
}PUT /api/job/{name}
Content-Type: application/json
{
"schedule": "0 3 * * *",
"command": "backup.sh --verbose",
"environment": ["BACKUP_DIR=/data", "COMPRESSION=gzip"]
}Response:
{
"name": "backup",
"updated": true,
"message": "Job configuration updated successfully"
}DELETE /api/job/{name}Response:
{
"name": "backup",
"deleted": true,
"message": "Job deleted successfully"
}PATCH /api/job/{name}/toggleResponse:
{
"name": "backup",
"enabled": false,
"message": "Job disabled"
}GET /api/execution/{executionId}Response:
{
"executionId": "550e8400-e29b-41d4-a716-446655440001",
"job": "backup",
"startTime": "2024-01-01T12:00:00Z",
"endTime": "2024-01-01T12:05:00Z",
"duration": 300000000000,
"status": "success",
"output": "Backup completed\n1000 files processed",
"error": null,
"exitCode": 0
}POST /api/execution/{executionId}/stopResponse:
{
"executionId": "550e8400-e29b-41d4-a716-446655440001",
"stopped": true,
"message": "Execution stopped"
}GET /api/execution/{executionId}/logsResponse:
{
"executionId": "550e8400-e29b-41d4-a716-446655440001",
"logs": [
{
"timestamp": "2024-01-01T12:00:00Z",
"level": "info",
"message": "Starting backup process"
},
{
"timestamp": "2024-01-01T12:00:01Z",
"level": "info",
"message": "Scanning files..."
},
{
"timestamp": "2024-01-01T12:05:00Z",
"level": "info",
"message": "Backup completed successfully"
}
]
}GET /api/scheduler/statusResponse:
{
"running": true,
"startTime": "2024-01-01T00:00:00Z",
"uptime": 86400,
"totalJobs": 15,
"activeJobs": 12,
"runningJobs": 2,
"nextJob": {
"name": "cleanup",
"scheduledTime": "2024-01-02T02:00:00Z"
}
}POST /api/scheduler/start
POST /api/scheduler/stopResponse:
{
"running": true,
"message": "Scheduler started"
}POST /api/scheduler/reloadResponse:
{
"reloaded": true,
"jobsAdded": 2,
"jobsUpdated": 1,
"jobsRemoved": 0,
"message": "Configuration reloaded successfully"
}GET /health/livenessResponse:
{
"status": "healthy",
"timestamp": "2024-01-01T12:00:00Z"
}GET /health/readinessResponse:
{
"ready": true,
"scheduler": "running",
"docker": "connected",
"database": "n/a"
}GET /metricsResponse:
# HELP ofelia_jobs_total Total number of jobs executed
# TYPE ofelia_jobs_total counter
ofelia_jobs_total 1234
# HELP ofelia_jobs_failed_total Total number of failed jobs
# TYPE ofelia_jobs_failed_total counter
ofelia_jobs_failed_total 12
# HELP ofelia_jobs_running Number of currently running jobs
# TYPE ofelia_jobs_running gauge
ofelia_jobs_running 2
# HELP ofelia_job_duration_seconds Job execution duration in seconds
# TYPE ofelia_job_duration_seconds histogram
ofelia_job_duration_seconds_bucket{le="0.1"} 100
ofelia_job_duration_seconds_bucket{le="0.5"} 500
...
GET /api/configResponse:
{
"global": {
"dockerHost": "unix:///var/run/docker.sock",
"dockerPollInterval": 30,
"dockerEvents": true,
"slackURL": "https://hooks.slack.com/...",
"emailFrom": "ofelia@example.com",
"emailTo": "admin@example.com"
},
"jobs": {
"exec": [...],
"run": [...],
"local": [...],
"service": [...]
}
}PATCH /api/config/global
Content-Type: application/json
{
"dockerPollInterval": 60,
"slackChannel": "#alerts"
}const ws = new WebSocket('ws://localhost:8080/api/ws');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Event:', data
4C1E
.type, data.payload);
};Event Types:
job.started: Job execution startedjob.completed: Job execution completedjob.failed: Job execution failedjob.added: New job addedjob.updated: Job configuration updatedjob.removed: Job removedscheduler.started: Scheduler startedscheduler.stopped: Scheduler stopped
All endpoints return consistent error responses:
{
"error": {
"code": "JOB_NOT_FOUND",
"message": "Job 'backup' not found",
"details": {
"job": "backup",
"timestamp": "2024-01-01T12:00:00Z"
}
}
}UNAUTHORIZED: Missing or invalid authenticationFORBIDDEN: Insufficient permissionsJOB_NOT_FOUND: Job does not existINVALID_SCHEDULE: Invalid cron expressionDOCKER_ERROR: Docker API errorEXECUTION_ERROR: Job execution failedVALIDATION_ERROR: Invalid input dataINTERNAL_ERROR: Server error
API requests are rate-limited to prevent abuse:
- Default limit: 100 requests per minute
- Burst capacity: 10 requests
Rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1704067200Cross-Origin Resource Sharing (CORS) can be configured:
cors:
allowed_origins:
- http://localhost:3000
- https://app.example.com
allowed_methods:
- GET
- POST
- PUT
- DELETE
allowed_headers:
- Authorization
- Content-Type
max_age: 86400See also: Web Package | Configuration Guide | Project Index