SageOx runs API

The runs API from SageOx — 7 operation(s) for runs.

OpenAPI Specification

sageox-runs-api-openapi.yml Raw ↑
openapi: 3.1.0
info:
  title: SageOx Admin runs API
  version: 1.0.0
  description: "# API Reference\n\n## Overview\n\nSageOx is a platform that captures team knowledge from discussions, decisions, and work context into a Ledger (per-repo historical record) and Team Context (team-wide shared knowledge). The SageOx API provides programmatic access to manage repositories, recordings, notifications, and team data with high performance and reliability.\n\n## Base URLs\n\n| Environment | URL |\n|---|---|\n| Local Development | `http://localhost:3000` |\n| Test | `https://test.sageox.ai` |\n| Production | `https://sageox.ai` |\n\n## Authentication\n\nAll requests to the SageOx API require authentication via JWT tokens issued by the Better Auth service.\n\nInclude your token in the `Authorization` header:\n```\nAuthorization: Bearer <token>\n```\n\n**Initial Auth Flow (CLI)**\n- CLI initiates device flow to obtain initial credentials\n- Exchange device code for JWT token\n- Store token securely for subsequent API requests\n- Refresh token when expired\n\n**Authenticated Endpoints**\n- Pass JWT in `Authorization: Bearer <token>` header\n- Tokens contain user identity and team membership\n- Invalid or expired tokens return 401 Unauthorized\n\n## Response Format\n\n### Success\nStandard response format with optional pagination metadata:\n```json\n{\n  \"success\": true,\n  \"data\": { ... }\n}\n```\n\n### Error\nAll errors follow a consistent format:\n```json\n{\n  \"success\": false,\n  \"error\": \"Human-readable error description\"\n}\n```\n\n**Status Codes**\n- `400` — Bad Request: Invalid parameters or malformed request\n- `401` — Unauthorized: Missing or invalid authentication token\n- `403` — Forbidden: Insufficient permissions for this resource\n- `404` — Not Found: Resource does not exist\n- `409` — Conflict: Request conflicts with current state (e.g., duplicate)\n- `429` — Rate Limited: Too many requests; retry with backoff\n- `500` — Internal Error: Server error; contact support if persistent\n\n## Pagination\n\nList endpoints support cursor-based pagination via query parameters:\n\n**Query Parameters**\n- `limit` — Number of results per page (default: 20, max: 100)\n- `offset` — Number of results to skip (default: 0)\n\n**Response Metadata**\nList responses include pagination info:\n```json\n{\n  \"success\": true,\n  \"data\": [ ... ],\n  \"meta\": {\n    \"total\": 150,\n    \"limit\": 20,\n    \"offset\": 0,\n    \"hasMore\": true\n  }\n}\n```\n\n## ID Formats\n\nResources use standardized ID formats for easy identification:\n\n| Resource | Format | Length | Example |\n|---|---|---|---|\n| Team | `team_<cuid2>` | 10 chars | `team_abc1234567` |\n| User | `usr_<cuid2>` | 10 chars | `usr_xyz9876543` |\n| Repository | `repo_<uuidv7>` | 36 chars | `repo_01934f5a-8b9c-7def-abcd-0123456789ab` |\n| Recording | `rec_<uuidv7>` | 36 chars | `rec_01934f5a-8b9c-7def-abcd-0123456789ab` |\n| Image | `img_<uuidv7>` | 36 chars | `img_01934f5a-8b9c-7def-abcd-0123456789ab` |\n| Notification | `notif_<cuid2>` | 14 chars | `notif_ab1cd2ef3g` |\n\nUse these IDs for all API operations. IDs are unique across environments.\n\n## Rate Limiting\n\nThe API applies rate limiting to protect against abuse and ensure fair access:\n\n**Authenticated Endpoints**\n- Limited per user: depends on subscription tier\n- Quota resets hourly\n\n**Unauthenticated Endpoints**\n- Limited per IP address\n- More restrictive than authenticated limits\n\nWhen rate limited, the API returns `429 Too Many Requests`. Retry requests with exponential backoff:\n```\nwait = min(2^attempt * 100ms, 10s)\n```\n\n## Timestamps\n\nAll timestamps in API responses use RFC 3339 / ISO 8601 format in UTC:\n```\n2025-12-03T10:30:00Z\n```\n\nUse this format when providing timestamps in requests as well. The API rejects timestamps in other formats.\n\n## SDKs & Tools\n\n- **OpenAPI Spec** — Full schema available at `/api/openapi.json`\n- **Postman Collection** — Import from `/api/postman.json` for interactive exploration\n- **CLI** — Use `ox` CLI for command-line access\n"
  contact:
    name: SageOx Team
  license:
    name: MIT
servers:
- url: http://localhost:3000
  description: Devcontainer
- url: https://test.sageox.ai
  description: Test
- url: https://sageox.ai
  description: Production
security:
- bearerAuth: []
tags:
- name: runs
paths:
  /api/v1/runs/prepare:
    post:
      operationId: prepareRun
      summary: Prepare a new terraform run
      description: 'Initialize a run before streaming terraform apply events.


        This endpoint creates a pending run that is ready to receive JSONL events from a terraform apply.

        Once created, the CLI streams terraform output to `/api/v1/runs/{run_id}/events` and calls

        `/api/v1/runs/{run_id}/complete` when terraform finishes.


        **Required sequence:**

        1. Call `/runs/prepare` to create run

        2. Stream events via `/runs/{run_id}/events`

        3. Call `/runs/{run_id}/complete` with exit code


        See [ADR-012](https://github.com/sageox/sageox/blob/main/docs/adr/012-run-event-processing.md) for architecture details.

        '
      tags:
      - runs
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - workspace_id
              - git_commit
              - terraform_version
              - plan_path
              properties:
                workspace_id:
                  type: string
                  description: Workspace ID (ws_ prefix)
                  example: ws_abc123def456
                git_commit:
                  type: string
                  description: Git commit SHA for this run
                  example: abc123deadbeef1234567890abcdef123456789
                terraform_version:
                  type: string
                  description: Terraform version (e.g., "1.9.5")
                  example: 1.9.5
                plan_path:
                  type: string
                  description: Path to plan.json in repository (relative to workspace)
                  example: workspaces/acme-corp/prod/plan.json
      responses:
        '201':
          description: Run created successfully
          content:
            application/json:
              schema:
                type: object
                required:
                - run_id
                properties:
                  run_id:
                    type: string
                    format: uuid
                    description: UUIDv7 identifier for the created run
                    example: 018c5e72-1b3a-7def-8abc-123456789012
        '400':
          description: Invalid request (missing required fields, invalid workspace_id format)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: 'Missing required field: workspace_id'
        '401':
          description: Unauthorized (invalid or missing authentication token)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Workspace not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Run already in progress for this workspace (conflict)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/runs/{run_id}/events:
    post:
      operationId: streamRunEvents
      summary: Stream terraform apply events
      description: 'Stream terraform apply JSONL events. Connection stays open until terraform completes or disconnects.


        **Event Format:**

        Send terraform output as newline-delimited JSON (NDJSON). Each line is a single Terraform event.


        Example events:

        ```

        {"@level":"info","type":"apply_start","@timestamp":"2025-01-01T00:00:00Z"}

        {"@level":"info","type":"apply_progress","hook":{"resource":{"addr":"aws_s3_bucket.main"},"action":"create"}}

        {"@level":"info","type":"apply_complete","changes":{"add":3,"change":0,"remove":0}}

        ```


        **Connection Lifecycle:**

        - Backend validates run exists and is in `pending` or `running` status

        - First connection signals Temporal workflow `run_started`

        - Each event line updates `last_event_at` timestamp

        - Important events (progress, complete) update DB directly

        - Connection drop without calling `/complete` signals `connection_lost` to workflow

        - Reconnection within 2 minutes cancels the connection_lost timeout


        **Storage:**

        - Backend does NOT store raw events (would be high write volume)

        - CLI must locally write events to `apply.jsonl` file

        - CLI commits `apply.jsonl` to git after terraform exits

        - Backend queries important events from database, not stored JSONL


        See [ADR-012](https://github.com/sageox/sageox/blob/main/docs/adr/012-run-event-processing.md) for full event specifications.

        '
      tags:
      - runs
      security:
      - bearerAuth: []
      parameters:
      - name: run_id
        in: path
        required: true
        description: UUIDv7 run identifier from `/runs/prepare`
        schema:
          type: string
          format: uuid
          example: 018c5e72-1b3a-7def-8abc-123456789012
      requestBody:
        required: true
        description: Newline-delimited JSON stream of terraform events
        content:
          application/x-ndjson:
            schema:
              type: string
              description: 'Terraform JSON output streamed as NDJSON (one JSON object per line).

                See Terraform documentation for event schema.

                '
              example: '{"@level":"info","type":"apply_start","@timestamp":"2025-01-01T00:00:00Z"}

                {"@level":"info","type":"apply_progress","hook":{"resource":{"addr":"aws_s3_bucket.main"},"action":"create"}}

                '
      responses:
        '202':
          description: 'Accepted - connection established and will remain open.

            Backend processes events as they arrive. Connection closes when:

            - Terraform process exits and CLI calls `/complete`

            - Network connection drops (triggers grace timer)

            - 30-minute inactivity timeout (no events received)

            - 4-hour absolute TTL exceeded

            '
        '400':
          description: Invalid JSONL format or malformed JSON in stream
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized (invalid or missing authentication token)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Run not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Run already completed or failed (cannot accept new events)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/runs/{run_id}/complete:
    post:
      operationId: completeRun
      summary: Complete a terraform run
      description: 'Signal terraform apply has finished (success or failure).


        **CLI Sequence (before calling this endpoint):**

        1. Terraform apply exits (with exit code 0 or non-zero)

        2. CLI writes all events to local `apply.jsonl` file (via streaming from terraform)

        3. CLI commits `apply.jsonl` to git repository

        4. CLI pushes commit to remote

        5. CLI calls this endpoint with exit code and commit SHA


        **Important:** CLI MUST commit and push `apply.jsonl` even on failure.

        Failed runs need logs for debugging and analysis.


        **Backend Behavior:**

        - Stores `apply_commit` in run metadata for audit trail

        - Sets `apply_path` to `workspaces/{team}/{workspace}/apply.jsonl`

        - If exit_code=0: Sets status=`succeeded`, signals Temporal `run_completed`

        - If exit_code!=0: Sets status=`failed`, stores error message, signals Temporal `run_failed`


        See [ADR-012](https://github.com/sageox/sageox/blob/main/docs/adr/012-run-event-processing.md) for workflow details.

        '
      tags:
      - runs
      security:
      - bearerAuth: []
      parameters:
      - name: run_id
        in: path
        required: true
        description: UUIDv7 run identifier
        schema:
          type: string
          format: uuid
          example: 018c5e72-1b3a-7def-8abc-123456789012
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - exit_code
              - apply_commit
              properties:
                exit_code:
                  type: integer
                  description: Terraform exit code (0=success, non-zero=failure)
                  example: 0
                apply_commit:
                  type: string
                  description: Git commit SHA containing `apply.jsonl`
                  example: abc123def456789abc123def456789abc123def4
                error_message:
                  type: string
                  description: Error details if exit_code is non-zero (optional)
                  example: 'Error: creating S3 bucket: AccessDenied'
      responses:
        '204':
          description: Run completed successfully (no content in response)
        '400':
          description: Invalid request (missing exit_code or apply_commit)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: 'Missing required field: exit_code'
        '401':
          description: Unauthorized (invalid or missing authentication token)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Run not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: Run already completed (conflict - cannot complete twice)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/runs/{run_id}:
    get:
      operationId: getRun
      summary: Get run status and resources
      description: "Retrieve run status and resource states. Frontend polls this endpoint to display progress.\n\n**Response includes:**\n- Run metadata (workspace, status, timestamps)\n- `metadata` object with resource counts, git commit, and timing info\n- `resources` array with per-resource status and duration\n\n**Frontend polling (from ADR-012):**\n```typescript\n// Poll every 2 seconds while running\nconst { data: run } = useQuery({\n  queryKey: ['run', runId],\n  queryFn: () => fetchRun(runId),\n  refetchInterval: (query) => {\n    const status = query.state.data?.status;\n    return status === 'running' || status === 'pending' ? 2000 : false;\n  },\n});\n```\n\n**Supported status values:**\n- `pending` - Run created, waiting for first event\n- `running` - Receiving events from CLI\n- `succeeded` - Terraform apply exit code 0\n- `failed` - Terraform apply exit code != 0\n- `timeout` - No events within 30 min, or total TTL (4 hours) exceeded\n- `disconnected` - CLI connection lost, didn't reconnect within 2-min grace period\n\n**Resource status values:**\n- `pending` - Waiting to execute\n- `running` - Currently executing\n- `succeeded` - Completed successfully\n- `failed` - Execution failed\n\nSee [ADR-012](https://github.com/sageox/sageox/blob/main/docs/adr/012-run-event-processing.md) for full schema.\n"
      tags:
      - runs
      security:
      - bearerAuth: []
      parameters:
      - name: run_id
        in: path
        required: true
        description: UUIDv7 run identifier
        schema:
          type: string
          format: uuid
          example: 018c5e72-1b3a-7def-8abc-123456789012
      responses:
        '200':
          description: Run details retrieved successfully
          content:
            application/json:
              schema:
                type: object
                required:
                - id
                - workspace_id
                - status
                - plan_path
                - created_at
                - metadata
                - resources
                properties:
                  id:
                    type: string
                    format: uuid
                    description: Run identifier
                    example: 018c5e72-1b3a-7def-8abc-123456789012
                  workspace_id:
                    type: string
                    description: Workspace identifier
                    example: ws_abc123def456
                  status:
                    type: string
                    enum:
                    - pending
                    - running
                    - succeeded
                    - failed
                    - timeout
                    - disconnected
                    description: Current run status
                    example: running
                  plan_path:
                    type: string
                    description: Path to plan.json in repository
                    example: workspaces/acme-corp/prod/plan.json
                  created_at:
                    type: string
                    format: date-time
                    description: When the run was created
                    example: '2025-01-01T00:00:00Z'
                  completed_at:
                    type: string
                    format: date-time
                    nullable: true
                    description: When the run completed (null if still running)
                    example: '2025-01-01T00:05:32Z'
                  metadata:
                    type: object
                    required:
                    - git_commit
                    - resource_count
                    - resources_completed
                    description: Run metadata stored in JSONB
                    properties:
                      git_commit:
                        type: string
                        description: Git commit SHA at run start
                        example: abc123deadbeef1234567890abcdef123456789
                      terraform_version:
                        type: string
                        description: Terraform version used
                        example: 1.9.5
                      resource_count:
                        type: integer
                        description: Total resources in the plan
                        example: 12
                      resources_completed:
                        type: integer
                        description: Number of resources that have completed
                        example: 5
                      last_event_at:
                        type: string
                        format: date-time
                        nullable: true
                        description: Timestamp of last event received
                        example: '2025-01-01T00:04:50Z'
                      error_message:
                        type: string
                        nullable: true
                        description: Error message if run failed
                        example: null
                      apply_commit:
                        type: string
                        nullable: true
                        description: Git commit SHA containing apply.jsonl
                        example: def456abc123def456abc123def456abc123def4
                  resources:
                    type: array
                    description: Per-resource status and timing information
                    items:
                      type: object
                      required:
                      - address
                      - resource_type
                      - status
                      properties:
                        address:
                          type: string
                          description: Terraform resource address
                          example: aws_s3_bucket.main
                        resource_type:
                          type: string
                          description: Resource type for timing aggregation
                          example: aws_s3_bucket
                        status:
                          type: string
                          enum:
                          - pending
                          - running
                          - succeeded
                          - failed
                          description: Resource execution status
                          example: succeeded
                        started_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: When resource execution started
                          example: '2025-01-01T00:00:05Z'
                        completed_at:
                          type: string
                          format: date-time
                          nullable: true
                          description: When resource execution completed
                          example: '2025-01-01T00:00:10Z'
                        duration_ms:
                          type: integer
                          nullable: true
                          description: Execution duration in milliseconds
                          example: 2340
                        metadata:
                          type: object
                          description: Resource metadata
                          properties:
                            name:
                              type: string
                              description: Resource name
                              example: main
                            provider:
                              type: string
                              description: Cloud provider
                              example: aws
                            category:
                              type: string
                              enum:
                              - network
                              - compute
                              - storage
                              - security
                              - iam
                              - gpu
                              - dns
                              - cdn
                              - monitoring
                              description: Resource category for UI grouping
                              example: storage
                            lifecycle:
                              type: string
                              enum:
                              - create
                              - update
                              - delete
                              - replace
                              - no-op
                              description: Terraform lifecycle action
                              example: create
                            run_index:
                              type: integer
                              description: Execution order
                              example: 0
                            dependencies:
                              type: array
                              items:
                                type: string
                              description: Resource addresses this depends on
                              example:
                              - aws_vpc.main
                            error_message:
                              type: string
                              nullable: true
                              description: Error details if resource failed
                              example: null
              example:
                id: 018c5e72-1b3a-7def-8abc-123456789012
                workspace_id: ws_abc123def456
                status: running
                plan_path: workspaces/acme-corp/prod/plan.json
                created_at: '2025-01-01T00:00:00Z'
                completed_at: null
                metadata:
                  git_commit: abc123deadbeef1234567890abcdef123456789
                  terraform_version: 1.9.5
                  resource_count: 12
                  resources_completed: 5
                  last_event_at: '2025-01-01T00:04:50Z'
                  error_message: null
                  apply_commit: null
                resources:
                - address: aws_s3_bucket.main
                  resource_type: aws_s3_bucket
                  status: succeeded
                  started_at: '2025-01-01T00:00:05Z'
                  completed_at: '2025-01-01T00:00:10Z'
                  duration_ms: 2340
                  metadata:
                    name: main
                    provider: aws
                    category: storage
                    lifecycle: create
                    run_index: 0
                    dependencies: []
                    error_message: null
                - address: aws_vpc.main
                  resource_type: aws_vpc
                  status: running
                  started_at: '2025-01-01T00:00:12Z'
                  completed_at: null
                  duration_ms: null
                  metadata:
                    name: main
                    provider: aws
                    category: network
                    lifecycle: create
                    run_index: 1
                    dependencies: []
                    error_message: null
        '401':
          description: Unauthorized (invalid or missing authentication token)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Run not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/env/{env_id}/run/init:
    post:
      operationId: initRun
      summary: Initialize new run (deprecated)
      description: 'DEPRECATED: Use `POST /api/v1/runs/prepare` instead.


        Creates a new run for the specified environment with infrastructure files.

        This endpoint is maintained for backwards compatibility only.

        '
      tags:
      - runs
      security:
      - bearerAuth: []
      deprecated: true
      parameters:
      - name: env_id
        in: path
        required: true
        description: Environment identifier
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - description
              - infrastructure_files
              properties:
                description:
                  type: string
                  description: Description of the run
                infrastructure_files:
                  type: object
                  additionalProperties:
                    type: string
                  description: Map of infrastructure file names to their contents
      responses:
        '201':
          description: Run initialized successfully
          content:
            application/json:
              schema:
                type: object
                required:
                - id
                properties:
                  id:
                    type: string
                    format: uuid
                    description: UUIDv7 identifier for the newly created run
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Environment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/env/{env_id}/run/{run_id}:
    post:
      operationId: updateRun
      summary: Update run with step data (deprecated)
      description: 'DEPRECATED: Use `POST /api/v1/runs/{run_id}/events` for streaming instead.


        Updates an existing run with data from a specific step.

        This endpoint is maintained for backwards compatibility only.

        '
      tags:
      - runs
      security:
      - bearerAuth: []
      deprecated: true
      parameters:
      - name: env_id
        in: path
        required: true
        description: Environment identifier
        schema:
          type: string
      - name: run_id
        in: path
        required: true
        description: Run identifier
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - step
              - data
              properties:
                step:
                  type: string
                  description: Name or identifier of the step
                data:
                  type: object
                  description: Step-specific data
      responses:
        '204':
          description: Run updated successfully (no content)
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Environment or run not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
  /api/v1/env/{env_id}/run/{run_id}/status:
    post:
      operationId: completeRunLegacy
      summary: Complete or fail run (deprecated)
      description: 'DEPRECATED: Use `POST /api/v1/runs/{run_id}/complete` instead.


        Marks a run as completed or failed with an optional message.

        This endpoint is maintained for backwards compatibility only.

        '
      tags:
      - runs
      security:
      - bearerAuth: []
      deprecated: true
      parameters:
      - name: env_id
        in: path
        required: true
        description: Environment identifier
        schema:
          type: string
      - name: run_id
        in: path
        required: true
        description: Run identifier
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - status
              properties:
                status:
                  type: string
                  enum:
                  - completed
                  - failed
                  description: Final status of the run
                message:
                  type: string
                  description: Optional message providing additional context
      responses:
        '204':
          description: Run status updated successfully (no content)
        '400':
          description: Invalid request body
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: Environment or run not found
          content:
            application/json:
              schema:
          

# --- truncated at 32 KB (32 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/sageox/refs/heads/main/openapi/sageox-runs-api-openapi.yml