Documentation

Actions & Execution

Actions are start/stop operations executed against cloud resources — either triggered automatically by schedules or manually through the API.

How Execution Works

  1. An action intent is created (by scheduler or manual API call)
  2. The intent is queued in Redis for processing
  3. The executor picks up the intent and calls the cloud provider API
  4. The result (success/failure) is recorded as an execution log
  5. State transitions are tracked in the state history

Idempotent Actions

All start/stop operations are idempotent. Starting an already-running resource or stopping an already-stopped resource will succeed without error. This makes retries safe.

Action Statuses

StatusDescription
pendingAction is queued, waiting to be processed
runningAction is being executed against the cloud provider
succeededAction completed successfully
failedAction failed — check errorMessage for details

Manual Action

Trigger a start or stop action on a specific resource outside of any schedule.

POST
/actions

Submit a manual start or stop action.

Requestbash
curl -X POST https://zopnight.com/api/actions \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "stop",
    "resourceUID": "i-0abc123def456",
    "provider": "aws",
    "resourceType": "aws-ec2",
    "cloudAccountID": "ca_abc123",
    "region": "us-east-1"
  }'
Responsejson
{
  "data": {
    "actionID": "act_xyz789",
    "status": "queued"
  }
}

Bulk Action

Start or stop all resources in a resource group with a single request. Resources are processed according to their execution tier and order.

POST
/actions/bulk

Submit a bulk action for a resource group.

Requestbash
curl -X POST https://zopnight.com/api/actions/bulk \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "start",
    "groupID": "grp_abc123"
  }'
Responsejson
{
  "data": {
    "bulkActionID": "bulk_xyz789",
    "status": "queued",
    "total": 4
  }
}

Get Action Status

GET
/actions/{actionID}

Check the status of a submitted action.

Responsejson
{
  "data": {
    "id": "act_xyz789",
    "resourceUID": "i-0abc123def456",
    "provider": "aws",
    "cloudAccountID": "ca_abc123",
    "region": "us-east-1",
    "action": "stop",
    "reason": "manual",
    "status": "succeeded",
    "startedAt": "2025-01-20T10:00:05Z",
    "completedAt": "2025-01-20T10:00:12Z",
    "createdAt": "2025-01-20T10:00:00Z"
  }
}

Get Bulk Action Status

GET
/actions/bulk/{bulkActionID}

Check the status of a bulk action.

Responsejson
{
  "data": {
    "id": "bulk_xyz789",
    "groupID": "grp_abc123",
    "type": "start",
    "status": "completed",
    "total": 4,
    "completed": 4,
    "failed": 0,
    "createdAt": "2025-01-20T10:00:00Z"
  }
}

Latest Bulk Action

Returns the most recent bulk action for the org. Useful for "is there still something running?" indicators that don't already have a bulk ID to look up.

GET
/actions/bulk/latest

Get the most recent bulk action submitted by this org.

Responsejson
{
  "data": {
    "id": "bulk_xyz789",
    "groupID": "grp_abc123",
    "type": "start",
    "status": "running",
    "total": 12,
    "completed": 7,
    "failed": 0,
    "createdAt": "2026-04-29T10:00:00Z"
  }
}

State History

Query the state transition history for resources. This provides an audit trail of when resources were started or stopped and why.

GET
/state-history

Get resource state transition history with pagination.

Query Parameters

ParameterDescription
resource_uidFilter by specific resource
changed_by_typeFilter by source: schedule, manual, bulk, override, override_expired
pagePage number
limitItems per page
Responsejson
{
  "data": {
    "items": [
      {
        "id": "sh_001",
        "resourceUid": "i-0abc123def456",
        "state": "stopped",
        "changedAt": "2025-01-20T18:00:00Z",
        "changedBy": "schedule:nightly-shutdown:cron_xyz",
        "scheduleId": "sch_abc123",
        "executionState": { "step": "stop-instance", "status": "succeeded" }
      },
      {
        "id": "sh_002",
        "resourceUid": "i-0abc123def456",
        "state": "failed",
        "changedAt": "2025-01-21T08:00:00Z",
        "changedBy": "schedule:nightly-shutdown:cron_xyz",
        "scheduleId": "sch_abc123",
        "errorMessage": "InsufficientCapacity: m6i.large not available in us-east-1a"
      }
    ],
    "total": 2,
    "page": 1,
    "limit": 50
  }
}

changedBy Values

The changedBy field is a structured colon-delimited string:

  • schedule:<name>:<cronId> — triggered by a schedule
  • manual:<email> — manually triggered via API or UI
  • bulk:<email>:<bulkId> — bulk start/stop action
  • override:<action> — triggered by an active override
  • override_expired:<action> — triggered when an override expired

errorMessage is populated only on failures; executionState is a free-form map of step-level status that the executor records during multi-step transitions (e.g. for k8s namespaces or container apps).