Deploy Actions
Action endpoints trigger async operations on environments (deploy, restart, start, stop). All action endpoints return 202 Accepted immediately with a task_id. The actual work happens in the background.
Base URL: https://api.oec.sh/api/public/v1
All action endpoints require a full_access API key. Read-only keys return 403 Forbidden.
Async Pattern
All action endpoints follow the same pattern:
- POST the action → receive a
task_idandpoll_urlimmediately (202) - GET the
poll_urlto check progress - Poll until
statusreaches a terminal state:completedorfailed
Task Status Values
| Status | Terminal? | Meaning |
|---|---|---|
pending | No | Task created, not yet queued. |
queued | No | Waiting in the work queue. |
running | No | Worker is actively executing. |
completed | Yes | Task finished successfully. |
failed | Yes | Task finished with an error. |
cancelled | Yes | Task was cancelled before completion. |
Idempotency
All action endpoints support the Idempotency-Key header. Supply a unique string to safely retry on network errors without triggering duplicate operations. Idempotency results are cached for 24 hours.
Concurrency Protection
Only one action may be queued or running for an environment at a time. If another task is already active, the endpoint returns 409 Conflict with error code task_in_progress.
Trigger a Deployment
POST /environments/{env_id}/deployTriggers a full redeployment (pull latest Git code, update containers, run database migrations).
Path Parameters
| Parameter | Type | Description |
|---|---|---|
env_id | UUID | The environment ID. |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | No | Unique string to deduplicate retries. |
Response — 202 Accepted
{
"task_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"status": "queued",
"environment_id": "YOUR_ENV_ID",
"poll_url": "/api/public/v1/deployments/a1b2c3d4-5678-90ab-cdef-1234567890ab"
}Response Fields
| Field | Type | Description |
|---|---|---|
task_id | UUID | Use this to poll for status. |
status | string | Initial task status, always "queued". |
environment_id | UUID | The environment being deployed. |
poll_url | string | Relative URL to GET for task status. |
Example
curl -X POST "https://api.oec.sh/api/public/v1/environments/YOUR_ENV_ID/deploy" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Idempotency-Key: deploy-$(date +%s)"Restart Environment
POST /environments/{env_id}/restartRestarts the environment's containers without redeploying code. The environment must be in running status.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
env_id | UUID | The environment ID. |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | No | Unique string to deduplicate retries. |
Response — 202 Accepted
Returns the same action response shape as deploy.
Error Cases
| Status | Error Code | Reason |
|---|---|---|
409 | environment_not_running | Environment is not in running status. |
409 | task_in_progress | Another task is already active. |
Example
curl -X POST "https://api.oec.sh/api/public/v1/environments/YOUR_ENV_ID/restart" \
-H "Authorization: Bearer YOUR_API_KEY"Start Environment
POST /environments/{env_id}/startStarts a stopped environment. The environment must be in stopped status.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
env_id | UUID | The environment ID. |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | No | Unique string to deduplicate retries. |
Response — 202 Accepted
Returns the same action response shape as deploy.
Error Cases
| Status | Error Code | Reason |
|---|---|---|
409 | environment_not_stopped | Environment is not in stopped status. |
409 | task_in_progress | Another task is already active. |
Example
curl -X POST "https://api.oec.sh/api/public/v1/environments/YOUR_ENV_ID/start" \
-H "Authorization: Bearer YOUR_API_KEY"Quick Update Environment
POST /environments/{env_id}/quick-updatePulls the latest Git code into a running environment and optionally upgrades or reinitializes specific Odoo modules. Faster than a full deploy when you just need to apply code or module changes to an already-running environment. The environment must be in running status.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
env_id | UUID | The environment ID. |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | No | Unique string to deduplicate retries. |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
mode | string | Yes | One of: pull_restart, update_all, update_specific, reinitialize_specific. |
modules | string[] | Conditional | List of Odoo module technical names. Required for update_specific and reinitialize_specific. |
Modes
| Mode | What it does |
|---|---|
pull_restart | git pull + container restart. Use when only Python / template / asset changes are involved (hot reload). |
update_all | git pull + Odoo -u all. Updates every installed module. Slowest of the modes. |
update_specific | git pull + Odoo -u <modules>. Targets just the modules you list. |
reinitialize_specific | git pull + Odoo -i <modules>. Reinstalls the modules (drops + recreates module data). Destructive — use with care. |
Response — 202 Accepted
Returns the same action response shape as deploy. Poll poll_url until the task reaches a terminal state.
Error Cases
| Status | Error Code | Reason |
|---|---|---|
400 | unsupported_mode | mode is not one of the four supported values (e.g. restart_only — use /restart instead). |
400 | modules_required | update_specific or reinitialize_specific was requested without a modules list. |
409 | environment_not_running | Environment is not in running status. |
409 | environment_unassigned | Environment has no server assigned. |
409 | task_in_progress | Another task is already active. |
Examples
# Pull latest code and restart (no module upgrade)
curl -X POST "https://api.oec.sh/api/public/v1/environments/YOUR_ENV_ID/quick-update" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "pull_restart"}'
# Pull latest code and upgrade specific modules
curl -X POST "https://api.oec.sh/api/public/v1/environments/YOUR_ENV_ID/quick-update" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode": "update_specific", "modules": ["sale", "stock"]}'reinitialize_specific drops and recreates the module's data tables. Only use it for development environments where data loss is acceptable.
Stop Environment
POST /environments/{env_id}/stopStops a running environment's containers. Data is preserved. The environment must be in running status.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
env_id | UUID | The environment ID. |
Headers
| Header | Required | Description |
|---|---|---|
Idempotency-Key | No | Unique string to deduplicate retries. |
Response — 202 Accepted
Returns the same action response shape as deploy.
Error Cases
| Status | Error Code | Reason |
|---|---|---|
409 | environment_not_running | Environment is not in running status. |
409 | task_in_progress | Another task is already active. |
Example
curl -X POST "https://api.oec.sh/api/public/v1/environments/YOUR_ENV_ID/stop" \
-H "Authorization: Bearer YOUR_API_KEY"List Deployments
GET /environments/{env_id}/deploymentsReturns the deployment history for an environment, newest first. Only includes deploy and redeploy task types.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
env_id | UUID | The environment ID. |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
limit | integer | Results per page. Min 1, max 100. Default 20. |
cursor | string | Pagination cursor from previous response. |
Response — 200 OK
{
"data": [
{
"id": "a1b2c3d4-...",
"type": "deploy",
"status": "completed",
"environment_id": "YOUR_ENV_ID",
"progress_percent": 100,
"current_step": null,
"steps_completed": 12,
"total_steps": 12,
"started_at": "2026-03-01T12:00:00Z",
"completed_at": "2026-03-01T12:05:00Z",
"duration_seconds": 300,
"error_message": null,
"triggered_by": null
}
],
"pagination": {
"has_more": false,
"next_cursor": null,
"total": 5
}
}Deployment Object Fields
| Field | Type | Description |
|---|---|---|
id | UUID | Task ID. Use with Get Deployment Status. |
type | string | Task type: "deploy" or "redeploy". |
status | string | Current or final status. See Task Status Values. |
environment_id | UUID|null | The environment this task ran on. |
progress_percent | integer | Progress 0–100. |
current_step | string|null | Human-readable description of the current step. |
steps_completed | integer | Number of steps that have finished. |
total_steps | integer | Total number of steps. |
started_at | ISO 8601|null | When the worker started executing. |
completed_at | ISO 8601|null | When the task reached a terminal state. |
duration_seconds | integer|null | Total execution time in seconds. |
error_message | string|null | Human-readable error if status is "failed". |
triggered_by | string|null | User ID if triggered by a user, null if triggered by API key. |
Get Deployment Status
GET /deployments/{task_id}Returns the current status of a single deployment task. Use this to poll after triggering an action.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
task_id | UUID | The task ID returned by any action endpoint. |
Response — 200 OK
Returns the Deployment object.
Complete Polling Example
Deploy an environment and wait for it to finish:
import requests
import time
BASE_URL = "https://api.oec.sh/api/public/v1"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
ENV_ID = "YOUR_ENV_ID"
# Step 1: Trigger deploy
response = requests.post(
f"{BASE_URL}/environments/{ENV_ID}/deploy",
headers=HEADERS,
)
response.raise_for_status()
data = response.json()
task_id = data["task_id"]
print(f"Deploy queued. Task ID: {task_id}")
# Step 2: Poll until complete
while True:
status_response = requests.get(
f"{BASE_URL}/deployments/{task_id}",
headers=HEADERS,
)
task = status_response.json()
print(f"Status: {task['status']} ({task['progress_percent']}%)")
if task["status"] in ("completed", "failed", "cancelled"):
break
time.sleep(5)
# Step 3: Check result
if task["status"] == "completed":
print("Deploy succeeded!")
else:
print(f"Deploy failed: {task['error_message']}")For production workflows, consider using webhooks instead of polling. Subscribe to deploy.completed and deploy.failed events to receive a push notification when the deployment finishes.