openapi: 3.1.0
info:
  title: Coralbeat Workflows API
  version: 1.0.0-preview
  description: Durable, project-scoped workflow definitions, runs, commands, and fenced activity leases.
servers:
  - url: http://127.0.0.1:8089
security:
  - projectKey: []
paths:
  /v1/health:
    get:
      security: []
      operationId: health
      responses:
        "200": { description: Ready, content: { application/json: { schema: { type: object, required: [ok, ready, service], properties: { ok: { type: boolean }, ready: { type: boolean }, service: { const: workflows } } } } } }
  /v1/capabilities:
    get:
      security: []
      operationId: capabilities
      responses:
        "200": { description: Supported nodes and delivery semantics, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
  /v1/schema:
    get:
      security: []
      operationId: schema
      responses:
        "200": { description: Current format versions, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
  /v1/skills:
    get:
      security: []
      operationId: skills
      responses:
        "200": { description: Installed workflow skill metadata, content: { application/json: { schema: { type: object, additionalProperties: true } } } }
  /v1/workflows:
    post:
      operationId: createWorkflow
      description: Requires write scope.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [definition]
              properties: { name: { type: string, maxLength: 120 }, definition: { $ref: '#/components/schemas/WorkflowDefinition' } }
              additionalProperties: false
      responses:
        "201": { description: Draft created, content: { application/json: { schema: { $ref: '#/components/schemas/WorkflowEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
    get:
      operationId: listWorkflows
      description: Requires read scope. List entries omit draft bodies.
      responses:
        "200":
          description: Workflow summaries
          content:
            application/json:
              schema:
                type: object
                required: [workflows]
                properties: { workflows: { type: array, items: { $ref: '#/components/schemas/WorkflowSummary' } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/project-keys:
    get:
      operationId: listProjectKeys
      description: Requires admin scope. Key values are never returned.
      responses:
        "200": { description: Project key metadata, content: { application/json: { schema: { type: object, required: [keys], properties: { keys: { type: array, items: { $ref: '#/components/schemas/ProjectKey' } } } } } } }
        default: { $ref: '#/components/responses/Error' }
    post:
      operationId: createProjectKey
      description: Requires admin scope. The value is returned once and scopes cannot exceed the caller's effective scopes.
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [scopes, allowedActivityKinds], properties: { scopes: { type: array, items: { $ref: '#/components/schemas/ProjectKeyScope' }, minItems: 1, uniqueItems: true }, allowedActivityKinds: { type: array, items: { type: string }, uniqueItems: true, description: Empty grants no activity lease access; '*' grants all project-allowed kinds when the caller may delegate it. }, expiresAt: { type: string, format: date-time } }, additionalProperties: false } } }
      responses:
        "201": { description: Project key created, content: { application/json: { schema: { type: object, required: [key], properties: { key: { allOf: [{ $ref: '#/components/schemas/ProjectKey' }, { type: object, required: [value], properties: { value: { type: string } } }] } } } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/project-keys/{keyId}:
    delete:
      operationId: revokeProjectKey
      description: Requires admin scope.
      parameters: [{ name: keyId, in: path, required: true, schema: { type: string } }]
      responses:
        "200": { description: Revoked key metadata, content: { application/json: { schema: { type: object, required: [key], properties: { key: { $ref: '#/components/schemas/ProjectKey' } } } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/project/config:
    put:
      operationId: configureProject
      description: Requires admin scope. Activity kinds form the admission allowlist for new definitions.
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [activityKinds], properties: { activityKinds: { type: array, items: { type: string }, uniqueItems: true } }, additionalProperties: false } } }
      responses:
        "200": { description: Project configuration, content: { application/json: { schema: { type: object, required: [config], properties: { config: { type: object, required: [activityKinds], properties: { activityKinds: { type: array, items: { type: string } } } } } } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/workflows/{workflowId}:
    parameters: [{ $ref: '#/components/parameters/WorkflowId' }]
    get:
      operationId: getWorkflow
      description: Requires read scope.
      responses:
        "200": { description: Workflow, content: { application/json: { schema: { $ref: '#/components/schemas/WorkflowEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
    put:
      operationId: updateWorkflow
      description: Requires write scope and an exact draft revision.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [definition, expectedRevision]
              properties: { name: { type: string, maxLength: 120 }, definition: { $ref: '#/components/schemas/WorkflowDefinition' }, expectedRevision: { type: integer, minimum: 1 } }
              additionalProperties: false
      responses:
        "200": { description: Updated draft, content: { application/json: { schema: { $ref: '#/components/schemas/WorkflowEnvelope' } } } }
        "409": { $ref: '#/components/responses/Error' }
        default: { $ref: '#/components/responses/Error' }
  /v1/workflows/{workflowId}/publish:
    parameters: [{ $ref: '#/components/parameters/WorkflowId' }]
    post:
      operationId: publishWorkflow
      description: Requires write scope. Creates an immutable numeric version.
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [expectedRevision], properties: { expectedRevision: { type: integer, minimum: 1 } }, additionalProperties: false } } }
      responses:
        "200": { description: Published, content: { application/json: { schema: { type: object, required: [workflow, version], properties: { workflow: { $ref: '#/components/schemas/Workflow' }, version: { $ref: '#/components/schemas/PublishedVersion' } } } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/runs:
    post:
      operationId: startRun
      description: Requires run scope. An idempotency key may be retried only with the same request.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [workflowId, input, idempotencyKey]
              properties: { workflowId: { type: string }, version: { type: integer, minimum: 1 }, input: { $ref: '#/components/schemas/JsonValue' }, idempotencyKey: { $ref: '#/components/schemas/IdempotencyKey' } }
              additionalProperties: false
      responses:
        "200": { description: Replayed start, content: { application/json: { schema: { $ref: '#/components/schemas/RunEnvelope' } } } }
        "201": { description: Run started, content: { application/json: { schema: { $ref: '#/components/schemas/RunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
    get:
      operationId: listRuns
      description: Requires read scope.
      parameters: [{ name: workflowId, in: query, schema: { type: string } }]
      responses:
        "200": { description: Run summaries, content: { application/json: { schema: { type: object, required: [runs], properties: { runs: { type: array, items: { $ref: '#/components/schemas/RunSummary' } } } } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/runs/{runId}:
    parameters: [{ $ref: '#/components/parameters/RunId' }]
    get:
      operationId: getRun
      description: Requires read scope and advances elapsed waits.
      responses:
        "200": { description: Durable run snapshot, content: { application/json: { schema: { $ref: '#/components/schemas/RunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/runs/{runId}/commands:
    parameters: [{ $ref: '#/components/parameters/RunId' }]
    post:
      operationId: commandRun
      description: Requires approve or run scope according to command type. Lease commands use only the specialized task endpoints.
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [command, idempotencyKey], properties: { command: { $ref: '#/components/schemas/RunCommand' }, idempotencyKey: { $ref: '#/components/schemas/IdempotencyKey' } }, additionalProperties: false } } }
      responses:
        "200": { description: Command applied or replayed, content: { application/json: { schema: { $ref: '#/components/schemas/RunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/tasks:
    get:
      operationId: listActivityTasks
      description: Requires work scope. Returns ready intents without executing them.
      parameters: [{ name: limit, in: query, schema: { type: integer, minimum: 1, maximum: 100 } }]
      responses:
        "200": { description: Ready activity tasks, content: { application/json: { schema: { type: object, required: [tasks], properties: { tasks: { type: array, items: { $ref: '#/components/schemas/ActivityTask' } } } } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/tasks/{runId}/{activityId}/claim:
    post:
      operationId: claimActivityTask
      parameters: [{ $ref: '#/components/parameters/RunId' }, { $ref: '#/components/parameters/ActivityId' }]
      requestBody: { $ref: '#/components/requestBodies/LeaseRequest' }
      responses:
        "200": { description: Fenced lease, content: { application/json: { schema: { $ref: '#/components/schemas/TaskRunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/tasks/{runId}/{activityId}/heartbeat:
    post:
      operationId: heartbeatActivityTask
      parameters: [{ $ref: '#/components/parameters/RunId' }, { $ref: '#/components/parameters/ActivityId' }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [leaseToken, idempotencyKey], properties: { leaseToken: { type: string }, leaseDurationMs: { type: integer, minimum: 1, maximum: 900000 }, idempotencyKey: { $ref: '#/components/schemas/IdempotencyKey' } }, additionalProperties: false } } }
      responses:
        "200": { description: Renewed fenced lease, content: { application/json: { schema: { $ref: '#/components/schemas/TaskRunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/tasks/{runId}/{activityId}/complete:
    post:
      operationId: completeActivityTask
      parameters: [{ $ref: '#/components/parameters/RunId' }, { $ref: '#/components/parameters/ActivityId' }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [leaseToken, output, idempotencyKey], properties: { leaseToken: { type: string }, output: { $ref: '#/components/schemas/JsonValue' }, idempotencyKey: { $ref: '#/components/schemas/IdempotencyKey' } }, additionalProperties: false } } }
      responses:
        "200": { description: Fenced completion, content: { application/json: { schema: { $ref: '#/components/schemas/RunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
  /v1/tasks/{runId}/{activityId}/fail:
    post:
      operationId: failActivityTask
      parameters: [{ $ref: '#/components/parameters/RunId' }, { $ref: '#/components/parameters/ActivityId' }]
      requestBody:
        required: true
        content: { application/json: { schema: { type: object, required: [leaseToken, error, idempotencyKey], properties: { leaseToken: { type: string }, error: { $ref: '#/components/schemas/RunError' }, idempotencyKey: { $ref: '#/components/schemas/IdempotencyKey' } }, additionalProperties: false } } }
      responses:
        "200": { description: Fenced failure, content: { application/json: { schema: { $ref: '#/components/schemas/RunEnvelope' } } } }
        default: { $ref: '#/components/responses/Error' }
components:
  securitySchemes:
    projectKey: { type: http, scheme: bearer, bearerFormat: wf_live project key }
  parameters:
    WorkflowId: { name: workflowId, in: path, required: true, schema: { type: string, minLength: 1, maxLength: 128 } }
    RunId: { name: runId, in: path, required: true, schema: { type: string, minLength: 1, maxLength: 128 } }
    ActivityId: { name: activityId, in: path, required: true, schema: { type: string, minLength: 1, maxLength: 256 } }
  requestBodies:
    LeaseRequest:
      required: true
      content: { application/json: { schema: { type: object, required: [workerId, idempotencyKey], properties: { workerId: { type: string }, leaseDurationMs: { type: integer, minimum: 1, maximum: 900000 }, idempotencyKey: { $ref: '#/components/schemas/IdempotencyKey' } }, additionalProperties: false } } }
  responses:
    Error:
      description: Structured API error
      content: { application/json: { schema: { type: object, required: [error], properties: { error: { type: object, required: [code, message], properties: { code: { type: string }, message: { type: string } }, additionalProperties: true } } } } }
  schemas:
    JsonValue: {}
    IdempotencyKey: { type: string, minLength: 1, maxLength: 160 }
    ProjectKeyScope: { enum: [read, write, run, work, approve, admin] }
    ProjectKey:
      type: object
      required: [id, scopes, allowedActivityKinds, createdAt]
      properties: { id: { type: string }, scopes: { type: array, items: { $ref: '#/components/schemas/ProjectKeyScope' } }, allowedActivityKinds: { type: array, items: { type: string } }, expiresAt: { type: string, format: date-time }, createdAt: { type: string, format: date-time }, revokedAt: { type: string, format: date-time } }
    Expr:
      description: JSON literal, reference, or bounded declarative expression.
    WorkflowEdge:
      type: object
      required: [source, target, port]
      properties: { source: { type: string }, target: { type: string }, port: { type: string } }
      additionalProperties: false
    WorkflowGraph:
      type: object
      required: [entry, nodes, edges]
      properties:
        entry: { type: string }
        nodes: { type: array, items: { $ref: '#/components/schemas/WorkflowNode' } }
        edges: { type: array, items: { $ref: '#/components/schemas/WorkflowEdge' } }
    WorkflowNode:
      type: object
      required: [id, type]
      properties:
        id: { type: string, pattern: '^[A-Za-z][A-Za-z0-9_-]{0,63}$' }
        type: { enum: [condition, switch, transform, foreach, parallel, repeat, wait, approval, activity, end] }
        condition: { $ref: '#/components/schemas/Expr' }
        expression: { $ref: '#/components/schemas/Expr' }
        value: { $ref: '#/components/schemas/Expr' }
        items: { $ref: '#/components/schemas/Expr' }
        body: { $ref: '#/components/schemas/WorkflowGraph' }
        branches: { type: object, additionalProperties: { $ref: '#/components/schemas/WorkflowGraph' } }
        cases: { type: array, items: { type: object, required: [value, port], properties: { value: {}, port: { type: string } } } }
        maxItems: { type: integer, minimum: 1 }
        maxIterations: { type: integer, minimum: 1 }
        durationMs: { type: integer, minimum: 0 }
        until: { $ref: '#/components/schemas/Expr' }
        prompt: { $ref: '#/components/schemas/Expr' }
        activityKind: { type: string }
        input: { $ref: '#/components/schemas/Expr' }
        mode: { enum: [read, write, idempotent] }
        maxAttempts: { type: integer, minimum: 1 }
        timeoutMs: { type: integer, minimum: 1, maximum: 3600000, description: Activity deadline duration; defaults to 900000ms. }
        output: { $ref: '#/components/schemas/Expr' }
      additionalProperties: false
    WorkflowDefinition:
      allOf:
        - { $ref: '#/components/schemas/WorkflowGraph' }
        - type: object
          required: [formatVersion, id, version]
          properties:
            formatVersion: { const: 1 }
            id: { type: string }
            version: { type: string }
            limits: { type: object, additionalProperties: { type: integer, minimum: 1 } }
    PublishedVersion:
      type: object
      required: [version, definition, publishedAt]
      properties: { version: { type: integer, minimum: 1 }, definition: { $ref: '#/components/schemas/WorkflowDefinition' }, publishedAt: { type: string, format: date-time } }
    WorkflowSummary:
      type: object
      required: [id, projectId, name, revision, publishedVersions, createdAt, updatedAt]
      properties: { id: { type: string }, projectId: { type: string }, name: { type: string }, revision: { type: integer }, publishedVersions: { type: array, items: { type: object } }, createdAt: { type: string, format: date-time }, updatedAt: { type: string, format: date-time } }
    Workflow:
      allOf:
        - { $ref: '#/components/schemas/WorkflowSummary' }
        - type: object
          required: [draft]
          properties: { draft: { $ref: '#/components/schemas/WorkflowDefinition' }, publishedVersions: { type: array, items: { $ref: '#/components/schemas/PublishedVersion' } } }
    WorkflowEnvelope: { type: object, required: [workflow], properties: { workflow: { $ref: '#/components/schemas/Workflow' } } }
    RunError:
      type: object
      required: [code, message]
      properties: { code: { type: string }, message: { type: string }, details: { $ref: '#/components/schemas/JsonValue' } }
      additionalProperties: false
    RunCommand:
      type: object
      required: [type]
      description: One engine command. See the schema endpoint and SDK union for command-specific fields.
      properties:
        type: { enum: [approve, reject, reconcileUnknown, cancel] }
        approvalId: { type: string }
        activityId: { type: string }
        actor: { type: string, minLength: 1, description: Required for approve and reject attribution. }
        decision: { $ref: '#/components/schemas/JsonValue' }
        outcome: { enum: [completed, failed, retry] }
        output: { $ref: '#/components/schemas/JsonValue' }
        error: { $ref: '#/components/schemas/RunError' }
        reason: { type: string }
      additionalProperties: true
    RunState:
      type: object
      required: [formatVersion, runId, revision, status, activities, approvals, waits, events]
      properties:
        formatVersion: { const: 1 }
        runId: { type: string }
        revision: { type: integer }
        status: { enum: [running, waiting, awaiting_approval, activity, unknown, completed, failed, cancelled] }
        activities: { type: array, items: { type: object, additionalProperties: true } }
        approvals: { type: array, items: { type: object, additionalProperties: true } }
        waits: { type: array, items: { type: object, additionalProperties: true } }
        events: { type: array, items: { type: object, additionalProperties: true } }
      additionalProperties: true
    Run:
      type: object
      required: [id, projectId, workflowId, workflowVersion, state, createdAt, updatedAt]
      properties: { id: { type: string }, projectId: { type: string }, workflowId: { type: string }, workflowVersion: { type: integer }, state: { $ref: '#/components/schemas/RunState' }, createdAt: { type: string, format: date-time }, updatedAt: { type: string, format: date-time } }
    RunSummary:
      type: object
      required: [id, projectId, workflowId, workflowVersion, status, revision, createdAt, updatedAt]
      properties: { id: { type: string }, projectId: { type: string }, workflowId: { type: string }, workflowVersion: { type: integer }, status: { type: string }, revision: { type: integer }, createdAt: { type: string, format: date-time }, updatedAt: { type: string, format: date-time } }
    RunEnvelope: { type: object, required: [run], properties: { run: { $ref: '#/components/schemas/Run' } } }
    ActivityTask:
      type: object
      required: [runId, activityId, nodeId, attempt, kind, mode, input, status, deadlineAt]
      properties: { runId: { type: string }, workflowId: { type: string }, workflowVersion: { type: integer }, activityId: { type: string }, nodeId: { type: string }, attempt: { type: integer }, kind: { type: string }, mode: { enum: [read, write, idempotent] }, input: { $ref: '#/components/schemas/JsonValue' }, status: { enum: [ready, claimed, unknown, completed, failed] }, deadlineAt: { type: string, format: date-time }, leaseToken: { type: string }, leaseExpiresAt: { type: string, format: date-time } }
      additionalProperties: true
    TaskRunEnvelope: { type: object, required: [task, run], properties: { task: { $ref: '#/components/schemas/ActivityTask' }, run: { $ref: '#/components/schemas/Run' } } }
