{
  "components": {
    "responses": {},
    "schemas": {
      "User": {
        "description": "The authenticated Harmont user.",
        "properties": {
          "email": {
            "description": "The user's email address.",
            "type": "string"
          },
          "name": {
            "description": "Display name.",
            "nullable": true,
            "type": "string"
          },
          "uuid": {
            "description": "Stable user id.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "uuid",
          "email"
        ],
        "title": "User",
        "type": "object"
      },
      "Pipeline": {
        "description": "A repeatable CI workflow belonging to an organization.",
        "properties": {
          "allow_manual": {
            "description": "Whether manual (e.g. `hm run`) builds are permitted.",
            "type": "boolean"
          },
          "created_at": {
            "description": "When the pipeline was created.",
            "format": "date-time",
            "type": "string"
          },
          "default_branch": {
            "description": "The branch built by default.",
            "type": "string"
          },
          "description": {
            "description": "Optional human description.",
            "nullable": true,
            "type": "string"
          },
          "name": {
            "description": "Display name.",
            "type": "string"
          },
          "repo_name": {
            "description": "The pipeline's repo as `owner/repo` — from the mirrored GitHub repo when known, else parsed from the clone URL. `null` when unknown.",
            "nullable": true,
            "type": "string"
          },
          "repository": {
            "description": "The source repository this pipeline builds.",
            "type": "string"
          },
          "slug": {
            "description": "URL-safe slug, unique within the organization. Derived from the name.",
            "type": "string"
          },
          "visibility": {
            "description": "Whether the pipeline is private or public.",
            "enum": [
              "private",
              "public"
            ],
            "type": "string"
          }
        },
        "required": [
          "slug",
          "name",
          "repository",
          "default_branch",
          "visibility",
          "allow_manual",
          "created_at"
        ],
        "title": "Pipeline",
        "type": "object"
      },
      "CreateBuildRequest": {
        "description": "Creates a build for a pipeline. Two paths: supply pre-rendered v0 IR JSON in `pipeline_ir` (the `hm run` / API path), or omit `pipeline_ir` to have the engine render the registered pipeline's IR in a sandbox VM (decision #5 — rendering never happens on the API host). The IR is then parsed and planned in-process; on a plan/render rejection the build row is created with its error fields set and the request returns 422.",
        "properties": {
          "branch": {
            "description": "Source branch.",
            "type": "string"
          },
          "commit": {
            "description": "Source commit SHA.",
            "type": "string"
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Build-level environment variables.",
            "nullable": true,
            "type": "object"
          },
          "message": {
            "description": "Optional build/commit message.",
            "nullable": true,
            "type": "string"
          },
          "pipeline_ir": {
            "description": "The pre-rendered v0 IR JSON the engine materialises into jobs. When absent/blank the engine renders the pipeline's IR in a sandbox VM instead.",
            "nullable": true,
            "type": "string"
          },
          "source": {
            "description": "How the build was triggered (e.g. `api`, `ui`). Defaults to `api`.",
            "nullable": true,
            "type": "string"
          },
          "source_b64": {
            "description": "Base64-encoded source tarball (expected to be gzipped; the API validates only that it is valid base64, not the archive format) — the `hm run` local-code upload path. When present, the API stores it at the build's key and derives the internal, runner-token-authenticated `source_url`; it takes precedence over any caller-supplied `source_url`.",
            "nullable": true,
            "type": "string"
          },
          "source_sha256": {
            "description": "SHA-256 of the source archive, verified by the sandbox before rendering (in-sandbox-render path). Defaults to empty when omitted.",
            "nullable": true,
            "type": "string"
          },
          "source_url": {
            "description": "URL to the build's source archive, fetched by the sandbox. Ignored when `source_b64` is supplied (the API derives this internally).",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "branch",
          "commit"
        ],
        "title": "CreateBuildRequest",
        "type": "object"
      },
      "BitbucketWorkspace": {
        "description": "A connected Bitbucket workspace.",
        "example": {
          "name": "Acme Inc",
          "slug": "acme"
        },
        "properties": {
          "name": {
            "description": "Display name of the workspace.",
            "type": "string"
          },
          "slug": {
            "description": "Unique workspace slug.",
            "type": "string"
          }
        },
        "required": [
          "slug"
        ],
        "title": "BitbucketWorkspace",
        "type": "object"
      },
      "OrgMember": {
        "properties": {
          "email": {
            "type": "string"
          },
          "name": {
            "nullable": true,
            "type": "string"
          },
          "role": {
            "enum": [
              "owner",
              "admin",
              "member"
            ],
            "type": "string"
          },
          "user_uuid": {
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "user_uuid",
          "email",
          "role"
        ],
        "title": "OrgMember",
        "type": "object"
      },
      "RecoverOptionsRequest": {
        "description": "Validates a magic-link token (without consuming it) and returns WebAuthn credential-creation options to enroll a fresh passkey.",
        "properties": {
          "magic_link_token": {
            "description": "The raw token from the recovery email.",
            "type": "string"
          }
        },
        "required": [
          "magic_link_token"
        ],
        "title": "RecoverOptionsRequest",
        "type": "object"
      },
      "Transaction": {
        "description": "One append-only ledger entry — a credit (positive) or debit (negative).",
        "properties": {
          "amount_cents": {
            "description": "Signed amount in cents: positive for credits, negative for debits.",
            "type": "integer"
          },
          "created_at": {
            "description": "When the entry was recorded.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "Optional human-readable note.",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "description": "The entry's id.",
            "format": "uuid",
            "type": "string"
          },
          "source": {
            "description": "What produced the entry.",
            "enum": [
              "stripe_topup",
              "coupon_redemption",
              "admin_grant",
              "vm_lease_debit",
              "refund"
            ],
            "type": "string"
          }
        },
        "required": [
          "id",
          "amount_cents",
          "source",
          "created_at"
        ],
        "title": "Transaction",
        "type": "object"
      },
      "CliClaimRequest": {
        "description": "The CLI polls with its nonce to claim the session token the SPA transferred.",
        "example": {
          "nonce": "a7Kp...random"
        },
        "properties": {
          "nonce": {
            "description": "The nonce the CLI generated for this loopback handoff.",
            "type": "string"
          }
        },
        "required": [
          "nonce"
        ],
        "title": "CliClaimRequest",
        "type": "object"
      },
      "UsageBreakdownJob": {
        "description": "A single job's VM lease: the VM that ran it, its shape, how long it ran, and what it cost.",
        "properties": {
          "amount_cents": {
            "description": "The debit for this lease, in cents (negative).",
            "type": "integer"
          },
          "cpu_count": {
            "description": "vCPUs leased.",
            "type": "integer"
          },
          "disk_gb": {
            "description": "GB of disk leased.",
            "type": "integer"
          },
          "duration_seconds": {
            "description": "Lease duration in seconds (null while still running).",
            "nullable": true,
            "type": "integer"
          },
          "finished_at": {
            "description": "When the lease finished (null if still running).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "job_id": {
            "description": "The job's id.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "job_name": {
            "description": "The job's human-readable name.",
            "nullable": true,
            "type": "string"
          },
          "memory_gb": {
            "description": "GB of RAM leased.",
            "type": "integer"
          },
          "started_at": {
            "description": "When the lease started.",
            "format": "date-time",
            "type": "string"
          },
          "step_key": {
            "description": "The job's DAG step key.",
            "nullable": true,
            "type": "string"
          },
          "vm_handle": {
            "description": "The backend VM identifier that ran the job.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "cpu_count",
          "memory_gb",
          "disk_gb",
          "amount_cents",
          "started_at"
        ],
        "title": "UsageBreakdownJob",
        "type": "object"
      },
      "BitbucketRepoList": {
        "description": "Bitbucket repositories visible within the requested workspace.",
        "example": {
          "repos": []
        },
        "properties": {
          "repos": {
            "description": "The repositories.",
            "items": {
              "$ref": "#/components/schemas/BitbucketRepo"
            },
            "type": "array"
          }
        },
        "required": [
          "repos"
        ],
        "title": "BitbucketRepoList",
        "type": "object"
      },
      "RepoRegistration": {
        "description": "A single registration channel for a repository. `provider` is the channel the repo was connected through: `github` (the GitHub App), `bitbucket` (Bitbucket OAuth), or `cli` (reserved for direct CLI registration — not yet emitted). `account` is the connecting account/workspace login.",
        "properties": {
          "account": {
            "description": "The account/workspace login the repo was registered under.",
            "type": "string"
          },
          "provider": {
            "description": "The registration channel: `github`, `bitbucket`, or `cli`.",
            "type": "string"
          }
        },
        "required": [
          "provider",
          "account"
        ],
        "title": "RepoRegistration",
        "type": "object"
      },
      "AuthGithubRequest": {
        "description": "GitHub OAuth callback payload from the SPA.",
        "example": {
          "code": "abc123def456",
          "redirect_uri": "https://app.harmont.dev/auth/callback"
        },
        "properties": {
          "code": {
            "description": "The authorization code returned by GitHub to the SPA.",
            "type": "string"
          },
          "redirect_uri": {
            "description": "The redirect URI the SPA used on the authorize step; Assent must send the same value on the code exchange.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "redirect_uri"
        ],
        "title": "AuthGithubRequest",
        "type": "object"
      },
      "GithubInstallation": {
        "description": "A GitHub App installation the organization has connected. The numeric `installation_id` is GitHub's installation id (used to address sync/unbind), while `id` is Harmont's internal row id.",
        "properties": {
          "account_login": {
            "description": "The GitHub account (org or user) the App is installed on.",
            "type": "string"
          },
          "account_type": {
            "description": "`Organization` or `User`.",
            "type": "string"
          },
          "created_at": {
            "description": "When the installation row was first mirrored.",
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "description": "Harmont's internal installation row id.",
            "type": "integer"
          },
          "installation_id": {
            "description": "GitHub's numeric installation id.",
            "type": "integer"
          },
          "updated_at": {
            "description": "When the installation row was last updated.",
            "format": "date-time",
            "type": "string"
          }
        },
        "required": [
          "id",
          "installation_id",
          "account_login",
          "account_type",
          "created_at",
          "updated_at"
        ],
        "title": "GithubInstallation",
        "type": "object"
      },
      "PasskeyChallengeResponse": {
        "description": "The server-side challenge id (correlate it on finalize) and the PublicKeyCredential*Options the browser WebAuthn API consumes.",
        "properties": {
          "challenge_id": {
            "description": "Opaque id of the stored, single-use challenge. Echo it back on finalize.",
            "format": "uuid",
            "type": "string"
          },
          "options": {
            "additionalProperties": true,
            "description": "The PublicKeyCredentialCreationOptions / RequestOptions for the browser.",
            "type": "object"
          }
        },
        "required": [
          "challenge_id",
          "options"
        ],
        "title": "PasskeyChallengeResponse",
        "type": "object"
      },
      "UsageSeriesResponse": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/UsageSeriesBucket"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "UsageSeriesResponse",
        "type": "object"
      },
      "JobList": {
        "description": "A build's jobs, in DAG creation order.",
        "properties": {
          "data": {
            "description": "The build's jobs.",
            "items": {
              "$ref": "#/components/schemas/Job"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "JobList",
        "type": "object"
      },
      "PasskeyRegisterFinalizeRequest": {
        "description": "Completes adding a passkey by submitting the attestation for the challenge.",
        "properties": {
          "attestation": {
            "additionalProperties": true,
            "description": "The WebAuthn attestation response from navigator.credentials.create.",
            "type": "object"
          },
          "challenge_id": {
            "description": "The challenge id returned by the register-options call.",
            "format": "uuid",
            "type": "string"
          },
          "nickname": {
            "description": "Optional human label for the new passkey.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "challenge_id",
          "attestation"
        ],
        "title": "PasskeyRegisterFinalizeRequest",
        "type": "object"
      },
      "TokenResponse": {
        "description": "A freshly minted session bearer token.",
        "properties": {
          "token": {
            "description": "The raw session bearer token. Send it as `Authorization: Bearer <token>`.",
            "type": "string"
          }
        },
        "required": [
          "token"
        ],
        "title": "TokenResponse",
        "type": "object"
      },
      "CliTokenResponse": {
        "description": "The raw session bearer token handed to the CLI.",
        "properties": {
          "token": {
            "description": "The raw session bearer token. Send it as `Authorization: Bearer <token>`.",
            "type": "string"
          }
        },
        "required": [
          "token"
        ],
        "title": "CliTokenResponse",
        "type": "object"
      },
      "Error": {
        "description": "The Harmont error envelope.",
        "properties": {
          "error": {
            "properties": {
              "code": {
                "description": "Catalog code.",
                "type": "string"
              },
              "doc_url": {
                "description": "Docs link for this error.",
                "type": "string"
              },
              "message": {
                "description": "Human-readable message.",
                "type": "string"
              },
              "request_id": {
                "description": "Correlates with server logs/traces.",
                "type": "string"
              },
              "type": {
                "description": "Stable machine error type.",
                "type": "string"
              }
            },
            "required": [
              "type",
              "code",
              "message"
            ],
            "type": "object"
          }
        },
        "required": [
          "error"
        ],
        "title": "Error",
        "type": "object"
      },
      "UsageBreakdownBuild": {
        "description": "All VM usage attributed to one build, rolled up and broken down per job.",
        "properties": {
          "build_external_id": {
            "description": "The build's public external id.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "build_id": {
            "description": "The build's id (null for leases with no build).",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "build_number": {
            "description": "The per-pipeline build number.",
            "nullable": true,
            "type": "integer"
          },
          "finished_at": {
            "description": "Latest finish among the build's completed leases (null only if no lease has finished yet).",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "job_count": {
            "description": "Number of job leases in the build.",
            "type": "integer"
          },
          "jobs": {
            "description": "Per-job leases, oldest first.",
            "items": {
              "$ref": "#/components/schemas/UsageBreakdownJob"
            },
            "type": "array"
          },
          "pipeline_id": {
            "description": "The pipeline's id.",
            "format": "uuid",
            "nullable": true,
            "type": "string"
          },
          "pipeline_name": {
            "description": "The pipeline's name.",
            "nullable": true,
            "type": "string"
          },
          "pipeline_slug": {
            "description": "The pipeline's routing slug (for linking).",
            "nullable": true,
            "type": "string"
          },
          "started_at": {
            "description": "Earliest lease start in the build.",
            "format": "date-time",
            "type": "string"
          },
          "total_cents": {
            "description": "Sum of this build's lease debits, in cents (negative).",
            "type": "integer"
          }
        },
        "required": [
          "total_cents",
          "started_at",
          "job_count",
          "jobs"
        ],
        "title": "UsageBreakdownBuild",
        "type": "object"
      },
      "RecoverFinalizeRequest": {
        "description": "Completes recovery: consumes the magic-link token + challenge, registers a new passkey, and mints a session token.",
        "properties": {
          "attestation": {
            "additionalProperties": true,
            "description": "The WebAuthn attestation response from navigator.credentials.create.",
            "type": "object"
          },
          "challenge_id": {
            "description": "The challenge id returned by the recover-options call.",
            "format": "uuid",
            "type": "string"
          },
          "magic_link_token": {
            "description": "The raw recovery token (consumed here).",
            "type": "string"
          }
        },
        "required": [
          "magic_link_token",
          "challenge_id",
          "attestation"
        ],
        "title": "RecoverFinalizeRequest",
        "type": "object"
      },
      "BitbucketRepo": {
        "description": "A Bitbucket repository visible to the connected OAuth token.",
        "example": {
          "clone_url": "https://bitbucket.org/acme/widget.git",
          "default_branch": "main",
          "full_name": "acme/widget",
          "name": "widget",
          "private": true
        },
        "properties": {
          "clone_url": {
            "description": "HTTPS clone URL.",
            "nullable": true,
            "type": "string"
          },
          "default_branch": {
            "description": "Default branch name.",
            "nullable": true,
            "type": "string"
          },
          "full_name": {
            "description": "Workspace-qualified repo name, e.g. `acme/widget`.",
            "type": "string"
          },
          "name": {
            "description": "Repository slug.",
            "type": "string"
          },
          "private": {
            "description": "Whether the repository is private.",
            "type": "boolean"
          }
        },
        "required": [
          "full_name",
          "name"
        ],
        "title": "BitbucketRepo",
        "type": "object"
      },
      "RedeemCouponResponse": {
        "description": "The credit granted by the coupon and the organization's resulting balance, both in cents.",
        "properties": {
          "balance_cents": {
            "description": "The organization's balance after the credit, in cents.",
            "type": "integer"
          },
          "credit_cents": {
            "description": "The credit granted by the coupon, in cents.",
            "type": "integer"
          }
        },
        "required": [
          "credit_cents",
          "balance_cents"
        ],
        "title": "RedeemCouponResponse",
        "type": "object"
      },
      "BuildList": {
        "description": "A page of a pipeline's builds (newest first), with an opaque cursor.",
        "properties": {
          "data": {
            "description": "The builds on this page, newest first.",
            "items": {
              "$ref": "#/components/schemas/Build"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Opaque cursor for the next page. Pass it as the `cursor` query parameter; `null` when there are no more pages.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "data",
          "next_cursor"
        ],
        "title": "BuildList",
        "type": "object"
      },
      "InviteList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/Invite"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "InviteList",
        "type": "object"
      },
      "UpdateMemberRoleRequest": {
        "properties": {
          "role": {
            "enum": [
              "owner",
              "admin",
              "member"
            ],
            "type": "string"
          }
        },
        "required": [
          "role"
        ],
        "title": "UpdateMemberRoleRequest",
        "type": "object"
      },
      "CliRedeemRequest": {
        "description": "The CLI redeems a paste code the user typed in, receiving the session token.",
        "example": {
          "code": "K7P2QR9MX3WZ4NTV"
        },
        "properties": {
          "code": {
            "description": "The paste code shown by the SPA.",
            "type": "string"
          }
        },
        "required": [
          "code"
        ],
        "title": "CliRedeemRequest",
        "type": "object"
      },
      "ConnectInstallationRequest": {
        "description": "Binds an existing GitHub App installation (identified by its GitHub numeric installation id) to this organization.",
        "properties": {
          "installation_id": {
            "description": "GitHub's numeric installation id to bind.",
            "type": "integer"
          }
        },
        "required": [
          "installation_id"
        ],
        "title": "ConnectInstallationRequest",
        "type": "object"
      },
      "PasskeyRegisterOptionsResponse": {
        "description": "WebAuthn credential-creation options for adding a passkey to the current user, plus the server-side challenge id to echo back on finalize. `excludeCredentials` lists the user's already-registered authenticators.",
        "properties": {
          "challenge_id": {
            "description": "Opaque id of the stored, single-use challenge. Echo it back on finalize.",
            "format": "uuid",
            "type": "string"
          },
          "options": {
            "additionalProperties": true,
            "description": "The PublicKeyCredentialCreationOptions for the browser.",
            "type": "object"
          }
        },
        "required": [
          "challenge_id",
          "options"
        ],
        "title": "PasskeyRegisterOptionsResponse",
        "type": "object"
      },
      "RecoverBeginRequest": {
        "description": "Starts magic-link account recovery. Always 204 (never leaks account existence).",
        "example": {
          "email": "alice@harmont.dev"
        },
        "properties": {
          "email": {
            "description": "The email to recover access for.",
            "type": "string"
          }
        },
        "required": [
          "email"
        ],
        "title": "RecoverBeginRequest",
        "type": "object"
      },
      "ApiTokenListResponse": {
        "description": "Every personal API key belonging to the current user.",
        "properties": {
          "api_tokens": {
            "description": "The user's personal API keys.",
            "items": {
              "$ref": "#/components/schemas/ApiToken"
            },
            "type": "array"
          }
        },
        "required": [
          "api_tokens"
        ],
        "title": "ApiTokenListResponse",
        "type": "object"
      },
      "Build": {
        "description": "One run of a pipeline, identified by its pipeline-scoped number.",
        "properties": {
          "branch": {
            "description": "Source branch.",
            "nullable": true,
            "type": "string"
          },
          "commit": {
            "description": "Source commit SHA.",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "description": "When the build row was created.",
            "format": "date-time",
            "type": "string"
          },
          "error_code": {
            "description": "Stable build-level error code, if the build failed at the build level.",
            "nullable": true,
            "type": "string"
          },
          "error_message": {
            "description": "Human-readable build-level error message, if any.",
            "nullable": true,
            "type": "string"
          },
          "finished_at": {
            "description": "When the build reached a terminal state.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "message": {
            "description": "Commit/build message.",
            "nullable": true,
            "type": "string"
          },
          "number": {
            "description": "The build's number, sequential and unique within its pipeline.",
            "type": "integer"
          },
          "pipeline_slug": {
            "description": "The global slug of the pipeline this build belongs to.",
            "type": "string"
          },
          "scheduled_at": {
            "description": "When the build was queued.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "source": {
            "description": "How the build was triggered (e.g. `api`, `webhook`, `ui`).",
            "nullable": true,
            "type": "string"
          },
          "started_at": {
            "description": "When the build started running.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "state": {
            "description": "The rolled-up build state.",
            "enum": [
              "scheduled",
              "running",
              "failing",
              "passed",
              "failed",
              "canceling",
              "canceled"
            ],
            "type": "string"
          }
        },
        "required": [
          "number",
          "state",
          "pipeline_slug",
          "created_at"
        ],
        "title": "Build",
        "type": "object"
      },
      "GithubRepoList": {
        "description": "Mirrored repositories for an installation or across an organization.",
        "properties": {
          "data": {
            "description": "The mirrored repositories.",
            "items": {
              "$ref": "#/components/schemas/GithubRepo"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "GithubRepoList",
        "type": "object"
      },
      "TransactionList": {
        "description": "A page of an organization's ledger entries (newest first), with an opaque cursor.",
        "properties": {
          "data": {
            "description": "The ledger entries on this page, newest first.",
            "items": {
              "$ref": "#/components/schemas/Transaction"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Opaque cursor for the next page. Pass it as the `cursor` query parameter; `null` when there are no more pages.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "data",
          "next_cursor"
        ],
        "title": "TransactionList",
        "type": "object"
      },
      "PasskeyListResponse": {
        "description": "Every passkey registered to the current user.",
        "properties": {
          "passkeys": {
            "description": "The user's registered passkeys.",
            "items": {
              "$ref": "#/components/schemas/Passkey"
            },
            "type": "array"
          }
        },
        "required": [
          "passkeys"
        ],
        "title": "PasskeyListResponse",
        "type": "object"
      },
      "CreateOrganizationRequest": {
        "description": "Create a new organization owned by the authenticated user.",
        "properties": {
          "name": {
            "description": "Display name. The slug is derived from this.",
            "minLength": 1,
            "type": "string"
          },
          "url": {
            "description": "Optional website URL.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "CreateOrganizationRequest",
        "type": "object"
      },
      "CreatePipelineRequest": {
        "description": "Creates a pipeline within an organization. The slug is derived from the name; a slug collision within the organization is rejected.",
        "properties": {
          "default_branch": {
            "description": "The branch built by default.",
            "type": "string"
          },
          "description": {
            "description": "Optional human description.",
            "nullable": true,
            "type": "string"
          },
          "name": {
            "description": "Display name.",
            "type": "string"
          },
          "repo_name": {
            "description": "Optional `owner/repo` label. When omitted it is derived from `repository`.",
            "nullable": true,
            "type": "string"
          },
          "repository": {
            "description": "The source repository this pipeline builds.",
            "type": "string"
          }
        },
        "required": [
          "name",
          "repository",
          "default_branch"
        ],
        "title": "CreatePipelineRequest",
        "type": "object"
      },
      "CreateRepoBuildRequest": {
        "description": "Creates a build by addressing the pipeline through its repo-natural identity — `repo_name` (`owner/repo`) plus `source_slug` (the in-repo `@hm.pipeline(\"…\")` name) — rather than the org-global slug. This is the `hm run` path: a repo-local client knows its git remote and its pipeline name but not the namespaced slug. Build semantics are otherwise identical to `createBuild`.",
        "properties": {
          "branch": {
            "description": "Source branch.",
            "type": "string"
          },
          "commit": {
            "description": "Source commit SHA.",
            "type": "string"
          },
          "env": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "Build-level environment variables.",
            "nullable": true,
            "type": "object"
          },
          "message": {
            "description": "Optional build/commit message.",
            "nullable": true,
            "type": "string"
          },
          "pipeline_ir": {
            "description": "The pre-rendered v0 IR JSON the engine materialises into jobs. When absent/blank the engine renders the pipeline's IR in a sandbox VM instead.",
            "nullable": true,
            "type": "string"
          },
          "repo_name": {
            "description": "The worktree's repository as `owner/repo` (from its git remote).",
            "type": "string"
          },
          "source": {
            "description": "How the build was triggered (e.g. `api`, `ui`). Defaults to `api`.",
            "nullable": true,
            "type": "string"
          },
          "source_b64": {
            "description": "Base64-encoded source tarball (the `hm run` local-code upload). When present, the API stores it at the build's key and derives the internal, runner-token-authenticated `source_url`.",
            "nullable": true,
            "type": "string"
          },
          "source_slug": {
            "description": "The in-repo pipeline name — the `@hm.pipeline(\"…\")` slug.",
            "type": "string"
          }
        },
        "required": [
          "repo_name",
          "source_slug",
          "branch",
          "commit"
        ],
        "title": "CreateRepoBuildRequest",
        "type": "object"
      },
      "UsageBreakdownResponse": {
        "description": "An organization's VM usage over a window, grouped by build (newest first).",
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/UsageBreakdownBuild"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "UsageBreakdownResponse",
        "type": "object"
      },
      "CliCodeResponse": {
        "description": "A short, human-typeable code the user re-types into the CLI.",
        "example": {
          "code": "K7P2QR9MX3WZ4NTV"
        },
        "properties": {
          "code": {
            "description": "The single-use paste code. Valid for five minutes.",
            "type": "string"
          }
        },
        "required": [
          "code"
        ],
        "title": "CliCodeResponse",
        "type": "object"
      },
      "AuthTokenResponse": {
        "description": "A freshly minted session bearer token and the authenticated user.",
        "properties": {
          "token": {
            "description": "The raw session bearer token. Send it as `Authorization: Bearer <token>`.",
            "type": "string"
          },
          "user": {
            "$ref": "#/components/schemas/User"
          }
        },
        "required": [
          "token",
          "user"
        ],
        "title": "AuthTokenResponse",
        "type": "object"
      },
      "BitbucketOAuthUrlResponse": {
        "description": "The Bitbucket authorize URL the SPA should open.",
        "example": {
          "url": "https://bitbucket.org/site/oauth2/authorize?client_id=...&response_type=code"
        },
        "properties": {
          "url": {
            "description": "Bitbucket OAuth authorize URL.",
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "title": "BitbucketOAuthUrlResponse",
        "type": "object"
      },
      "PasskeySignupFinalizeRequest": {
        "description": "Completes passkey sign-up by submitting the attestation for the challenge.",
        "properties": {
          "attestation": {
            "additionalProperties": true,
            "description": "The WebAuthn attestation response from navigator.credentials.create.",
            "type": "object"
          },
          "challenge_id": {
            "description": "The challenge id returned by the options call.",
            "format": "uuid",
            "type": "string"
          },
          "verification_token": {
            "description": "The raw verification token (consumed here).",
            "type": "string"
          }
        },
        "required": [
          "challenge_id",
          "verification_token",
          "attestation"
        ],
        "title": "PasskeySignupFinalizeRequest",
        "type": "object"
      },
      "AuthGoogleRequest": {
        "description": "Google OAuth callback payload from the SPA.",
        "example": {
          "code": "4/0Afg...",
          "redirect_uri": "https://app.harmont.dev/auth/google/callback"
        },
        "properties": {
          "code": {
            "description": "The authorization code returned by Google to the SPA.",
            "type": "string"
          },
          "redirect_uri": {
            "description": "The redirect URI the SPA used; must match the OAuth client config.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "redirect_uri"
        ],
        "title": "AuthGoogleRequest",
        "type": "object"
      },
      "LogTokenResponse": {
        "description": "A build-scoped HMAC token the SSE log stream accepts, plus its expiry. Pass the token as the `token` query parameter when opening the log stream.",
        "properties": {
          "expires_at": {
            "description": "When the token expires (~1 hour out).",
            "format": "date-time",
            "type": "string"
          },
          "token": {
            "description": "The opaque, build-scoped log token.",
            "type": "string"
          }
        },
        "required": [
          "token",
          "expires_at"
        ],
        "title": "LogTokenResponse",
        "type": "object"
      },
      "UserUpdateRequest": {
        "description": "Updates the current user's editable profile fields.",
        "example": {
          "name": "Ada Lovelace"
        },
        "properties": {
          "name": {
            "description": "New display name.",
            "maxLength": 255,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "title": "UserUpdateRequest",
        "type": "object"
      },
      "PasskeySignupBeginRequest": {
        "description": "Starts passkey sign-up: runs the access gate and emails a verification link.",
        "example": {
          "email": "alice@harmont.dev",
          "name": "Alice"
        },
        "properties": {
          "email": {
            "description": "The email to sign up with.",
            "type": "string"
          },
          "name": {
            "description": "The new user's display name.",
            "type": "string"
          }
        },
        "required": [
          "email",
          "name"
        ],
        "title": "PasskeySignupBeginRequest",
        "type": "object"
      },
      "CurrentUserResponse": {
        "description": "The authenticated Harmont user and their personal-org slug.",
        "properties": {
          "email": {
            "description": "The user's email address.",
            "type": "string"
          },
          "name": {
            "description": "Display name.",
            "nullable": true,
            "type": "string"
          },
          "personal_org_slug": {
            "description": "Slug of the user's personal organization.",
            "nullable": true,
            "type": "string"
          },
          "uuid": {
            "description": "Stable user id.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "uuid",
          "email"
        ],
        "title": "CurrentUserResponse",
        "type": "object"
      },
      "Job": {
        "description": "One job (DAG node) of a build.",
        "properties": {
          "command": {
            "description": "The shell command the job runs.",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "description": "When the job row was created.",
            "format": "date-time",
            "type": "string"
          },
          "depends_on": {
            "description": "Ids of this job's prerequisite jobs (its DAG in-edges), spanning both `depends_on` and `builds_in` dependency kinds. Empty when the job has no prerequisites.",
            "items": {
              "format": "uuid",
              "type": "string"
            },
            "type": "array"
          },
          "error_code": {
            "description": "Stable job-level error code, if the job failed.",
            "nullable": true,
            "type": "string"
          },
          "error_message": {
            "description": "Human-readable job-level error message, if any.",
            "nullable": true,
            "type": "string"
          },
          "exit_code": {
            "description": "The command's exit status, once finished.",
            "nullable": true,
            "type": "integer"
          },
          "finished_at": {
            "description": "When the job reached a terminal state.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "description": "The job's id.",
            "format": "uuid",
            "type": "string"
          },
          "name": {
            "description": "Human-readable job name.",
            "nullable": true,
            "type": "string"
          },
          "retry_policy": {
            "additionalProperties": true,
            "description": "The job's retry policy, if any.",
            "nullable": true,
            "type": "object"
          },
          "soft_fail_policy": {
            "additionalProperties": true,
            "description": "The job's soft-fail policy, if any.",
            "nullable": true,
            "type": "object"
          },
          "soft_failed": {
            "description": "Whether the job failed but the build was allowed to continue.",
            "type": "boolean"
          },
          "started_at": {
            "description": "When the job started running.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "state": {
            "description": "The job's FSM state.",
            "enum": [
              "pending",
              "scheduled",
              "assigned",
              "running",
              "passed",
              "failed",
              "skipped",
              "canceling",
              "canceled",
              "timing_out",
              "timed_out"
            ],
            "type": "string"
          },
          "step_key": {
            "description": "The job's stable key within the build's DAG.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "id",
          "state",
          "soft_failed",
          "depends_on",
          "created_at"
        ],
        "title": "Job",
        "type": "object"
      },
      "CliTransferRequest": {
        "description": "The SPA hands a fresh session token to a locally-running CLI that generated `nonce` and is polling the claim endpoint.",
        "example": {
          "nonce": "a7Kp...random"
        },
        "properties": {
          "nonce": {
            "description": "The opaque nonce the CLI generated and is polling claim with.",
            "type": "string"
          }
        },
        "required": [
          "nonce"
        ],
        "title": "CliTransferRequest",
        "type": "object"
      },
      "Organization": {
        "description": "A Harmont organization the authenticated user is a member of.",
        "properties": {
          "created_at": {
            "description": "When the organization was created.",
            "format": "date-time",
            "type": "string"
          },
          "name": {
            "description": "Display name.",
            "type": "string"
          },
          "slug": {
            "description": "URL-safe unique slug identifying the organization.",
            "type": "string"
          },
          "url": {
            "description": "The organization's website, if set.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "slug",
          "name",
          "created_at"
        ],
        "title": "Organization",
        "type": "object"
      },
      "UsageSeriesBucket": {
        "properties": {
          "cpu_seconds": {
            "type": "integer"
          },
          "date": {
            "description": "Bucket day (UTC).",
            "format": "date",
            "type": "string"
          },
          "disk_gb_seconds": {
            "type": "integer"
          },
          "memory_gb_seconds": {
            "type": "integer"
          },
          "total_cents": {
            "description": "Rate-card cost for the day, in cents.",
            "type": "integer"
          }
        },
        "required": [
          "date",
          "cpu_seconds",
          "memory_gb_seconds",
          "disk_gb_seconds",
          "total_cents"
        ],
        "title": "UsageSeriesBucket",
        "type": "object"
      },
      "PipelineList": {
        "description": "A page of an organization's pipelines, with an opaque cursor.",
        "properties": {
          "data": {
            "description": "The pipelines on this page.",
            "items": {
              "$ref": "#/components/schemas/Pipeline"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Opaque cursor for the next page. Pass it as the `cursor` query parameter; `null` when there are no more pages.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "data",
          "next_cursor"
        ],
        "title": "PipelineList",
        "type": "object"
      },
      "RepoSummaryList": {
        "description": "All repositories visible to the organization, across all providers.",
        "properties": {
          "data": {
            "description": "The repositories.",
            "items": {
              "$ref": "#/components/schemas/RepoSummary"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "RepoSummaryList",
        "type": "object"
      },
      "GithubRepo": {
        "description": "A repository Harmont mirrors from a connected GitHub installation. `installation_id` is Harmont's internal installation row id (FK), not GitHub's installation id.",
        "properties": {
          "clone_url": {
            "description": "HTTPS clone URL.",
            "type": "string"
          },
          "default_branch": {
            "description": "Default branch name.",
            "type": "string"
          },
          "full_name": {
            "description": "`owner/name` GitHub full name.",
            "type": "string"
          },
          "gh_repo_id": {
            "description": "GitHub's numeric repo id.",
            "type": "integer"
          },
          "id": {
            "description": "Harmont's internal repo row id.",
            "type": "integer"
          },
          "installation_id": {
            "description": "FK onto the internal installation row id.",
            "type": "integer"
          },
          "last_synced_at": {
            "description": "When the repo was last synced from GitHub.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "name": {
            "description": "Short repo name.",
            "type": "string"
          },
          "owner": {
            "description": "GitHub owner login.",
            "type": "string"
          },
          "private": {
            "description": "Whether the repo is private.",
            "type": "boolean"
          }
        },
        "required": [
          "id",
          "installation_id",
          "gh_repo_id",
          "full_name",
          "name",
          "owner",
          "clone_url",
          "default_branch",
          "private"
        ],
        "title": "GithubRepo",
        "type": "object"
      },
      "PasskeyLoginOptionsRequest": {
        "description": "Starts a discoverable-credential (passkey) login. No fields required.",
        "properties": {},
        "title": "PasskeyLoginOptionsRequest",
        "type": "object"
      },
      "StripeWebhookResponse": {
        "description": "Acknowledges that a Stripe webhook event verified and was recorded (idempotently). Returned for any verified event — handled or not — so Stripe stops retrying.",
        "properties": {
          "status": {
            "description": "Always `\"ok\"` on a verified, recorded event.",
            "enum": [
              "ok"
            ],
            "type": "string"
          }
        },
        "required": [
          "status"
        ],
        "title": "StripeWebhookResponse",
        "type": "object"
      },
      "ConnectBitbucketResponse": {
        "description": "The workspaces connected by the OAuth callback, plus the org slug.",
        "example": {
          "org": "acme",
          "workspaces": [
            {
              "name": "Acme Inc",
              "slug": "acme"
            }
          ]
        },
        "properties": {
          "org": {
            "description": "The org slug the workspaces were connected to, recovered from the signed OAuth state. The SPA navigates to this org's repos view.",
            "type": "string"
          },
          "workspaces": {
            "description": "The connected workspaces.",
            "items": {
              "$ref": "#/components/schemas/BitbucketWorkspace"
            },
            "type": "array"
          }
        },
        "required": [
          "workspaces",
          "org"
        ],
        "title": "ConnectBitbucketResponse",
        "type": "object"
      },
      "UsageResponse": {
        "description": "Aggregated VM-lease usage for an organization over the requested `[from, to)` window: resource-seconds per dimension and the total billed cost in cents.",
        "properties": {
          "cpu_seconds": {
            "description": "Σ (cpu_count × duration_seconds) over leases in the window.",
            "type": "integer"
          },
          "disk_gb_seconds": {
            "description": "Σ (disk_gb × duration_seconds) over leases in the window.",
            "type": "integer"
          },
          "memory_gb_seconds": {
            "description": "Σ (memory_gb × duration_seconds) over leases in the window.",
            "type": "integer"
          },
          "total_cents": {
            "description": "Σ rate-card cost of the leases in the window, in cents.",
            "type": "integer"
          }
        },
        "required": [
          "cpu_seconds",
          "memory_gb_seconds",
          "disk_gb_seconds",
          "total_cents"
        ],
        "title": "UsageResponse",
        "type": "object"
      },
      "ApiToken": {
        "description": "A personal API key. The secret is returned only once, at creation.",
        "properties": {
          "created_at": {
            "description": "When the key was created.",
            "format": "date-time",
            "type": "string"
          },
          "description": {
            "description": "Human label for the key.",
            "nullable": true,
            "type": "string"
          },
          "expires_at": {
            "description": "When the key expires, or null if it never expires.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "id": {
            "description": "Stable key id.",
            "format": "uuid",
            "type": "string"
          },
          "last_used_at": {
            "description": "When the key was last used to authenticate, if ever.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "id",
          "created_at"
        ],
        "title": "ApiToken",
        "type": "object"
      },
      "Invite": {
        "properties": {
          "email": {
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "type": "string"
          },
          "id": {
            "format": "uuid",
            "type": "string"
          },
          "role": {
            "enum": [
              "admin",
              "member"
            ],
            "type": "string"
          },
          "token": {
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "id",
          "email",
          "role",
          "expires_at"
        ],
        "title": "Invite",
        "type": "object"
      },
      "CheckoutRequest": {
        "description": "Starts a Stripe Checkout Session crediting the organization identified by the path (`:org`). The body carries only the top-up amount in cents.",
        "properties": {
          "amount_cents": {
            "description": "The credit top-up amount in cents. Must be a positive integer within the supported bounds (100–1,000,000).",
            "maximum": 1000000,
            "minimum": 100,
            "type": "integer"
          }
        },
        "required": [
          "amount_cents"
        ],
        "title": "CheckoutRequest",
        "type": "object"
      },
      "PasskeyLoginFinalizeRequest": {
        "description": "Completes passkey login by submitting the assertion for the challenge.",
        "properties": {
          "assertion": {
            "additionalProperties": true,
            "description": "The WebAuthn assertion response from navigator.credentials.get.",
            "type": "object"
          },
          "challenge_id": {
            "description": "The challenge id returned by the login-options call.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "challenge_id",
          "assertion"
        ],
        "title": "PasskeyLoginFinalizeRequest",
        "type": "object"
      },
      "CheckoutResponse": {
        "description": "The hosted Stripe Checkout URL. Redirect the customer here to complete the top-up; on success Stripe fires a webhook that posts the matching credit.",
        "properties": {
          "checkout_url": {
            "description": "The hosted Stripe Checkout Session URL.",
            "type": "string"
          }
        },
        "required": [
          "checkout_url"
        ],
        "title": "CheckoutResponse",
        "type": "object"
      },
      "RepoSummary": {
        "description": "A repository visible to the organization, identified by its canonical clone URL. One logical repository may be registered through several channels (e.g. the GitHub App and the CLI); each appears in `registrations`.",
        "properties": {
          "clone_url": {
            "description": "HTTPS clone URL (may be null for some providers).",
            "nullable": true,
            "type": "string"
          },
          "default_branch": {
            "description": "Default branch name.",
            "nullable": true,
            "type": "string"
          },
          "full_name": {
            "description": "`owner/name` full name.",
            "type": "string"
          },
          "last_synced_at": {
            "description": "Most recent sync across this repo's registrations.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "name": {
            "description": "Short repo name.",
            "type": "string"
          },
          "owner": {
            "description": "Owner/namespace login.",
            "type": "string"
          },
          "private": {
            "description": "Whether the repo is private.",
            "type": "boolean"
          },
          "registrations": {
            "description": "Every channel through which this repo is registered.",
            "items": {
              "$ref": "#/components/schemas/RepoRegistration"
            },
            "type": "array"
          }
        },
        "required": [
          "full_name",
          "name",
          "owner",
          "private",
          "registrations"
        ],
        "title": "RepoSummary",
        "type": "object"
      },
      "ApiTokenCreateResponse": {
        "description": "The created key and its raw secret. The secret is shown only here, once.",
        "properties": {
          "api_token": {
            "$ref": "#/components/schemas/ApiToken"
          },
          "token": {
            "description": "The raw API key secret. Store it now; it is never shown again.",
            "type": "string"
          }
        },
        "required": [
          "token",
          "api_token"
        ],
        "title": "ApiTokenCreateResponse",
        "type": "object"
      },
      "OrgMemberList": {
        "properties": {
          "data": {
            "items": {
              "$ref": "#/components/schemas/OrgMember"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "OrgMemberList",
        "type": "object"
      },
      "PasskeySignupOptionsRequest": {
        "description": "Exchanges a verification token for WebAuthn credential-creation options.",
        "properties": {
          "verification_token": {
            "description": "The raw token from the verification email.",
            "type": "string"
          }
        },
        "required": [
          "verification_token"
        ],
        "title": "PasskeySignupOptionsRequest",
        "type": "object"
      },
      "BalanceResponse": {
        "description": "The organization's current balance — the sum of every ledger entry (credits positive, debits negative). May be negative.",
        "properties": {
          "balance_cents": {
            "description": "Current balance in cents (credits − debits). May be negative.",
            "type": "integer"
          }
        },
        "required": [
          "balance_cents"
        ],
        "title": "BalanceResponse",
        "type": "object"
      },
      "ApiTokenCreateRequest": {
        "description": "Creates a personal API key.",
        "example": {
          "description": "Laptop CLI",
          "expires_at": null
        },
        "properties": {
          "description": {
            "description": "Human label for the key.",
            "type": "string"
          },
          "expires_at": {
            "description": "Optional expiry; null or omitted means the key never expires.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "description"
        ],
        "title": "ApiTokenCreateRequest",
        "type": "object"
      },
      "CreateInviteRequest": {
        "properties": {
          "email": {
            "type": "string"
          },
          "role": {
            "enum": [
              "admin",
              "member"
            ],
            "type": "string"
          }
        },
        "required": [
          "email",
          "role"
        ],
        "title": "CreateInviteRequest",
        "type": "object"
      },
      "GithubInstallationList": {
        "description": "The organization's connected GitHub App installations.",
        "properties": {
          "data": {
            "description": "The connected installations.",
            "items": {
              "$ref": "#/components/schemas/GithubInstallation"
            },
            "type": "array"
          }
        },
        "required": [
          "data"
        ],
        "title": "GithubInstallationList",
        "type": "object"
      },
      "BitbucketWorkspaceList": {
        "description": "All Bitbucket workspaces the connected OAuth token can access.",
        "example": {
          "workspaces": [
            {
              "name": "Acme Inc",
              "slug": "acme"
            }
          ]
        },
        "properties": {
          "workspaces": {
            "description": "The connected workspaces.",
            "items": {
              "$ref": "#/components/schemas/BitbucketWorkspace"
            },
            "type": "array"
          }
        },
        "required": [
          "workspaces"
        ],
        "title": "BitbucketWorkspaceList",
        "type": "object"
      },
      "Passkey": {
        "description": "A WebAuthn credential registered to the current user.",
        "properties": {
          "aaguid": {
            "description": "The authenticator's AAGUID (model identifier), if reported.",
            "nullable": true,
            "type": "string"
          },
          "created_at": {
            "description": "When the passkey was registered.",
            "format": "date-time",
            "type": "string"
          },
          "last_used_at": {
            "description": "When the passkey was last used to authenticate, if ever.",
            "format": "date-time",
            "nullable": true,
            "type": "string"
          },
          "nickname": {
            "description": "Human label for the passkey.",
            "nullable": true,
            "type": "string"
          },
          "uuid": {
            "description": "Stable passkey id.",
            "format": "uuid",
            "type": "string"
          }
        },
        "required": [
          "uuid",
          "created_at"
        ],
        "title": "Passkey",
        "type": "object"
      },
      "OrganizationList": {
        "description": "A page of the current user's organizations, with an opaque cursor.",
        "properties": {
          "data": {
            "description": "The organizations on this page.",
            "items": {
              "$ref": "#/components/schemas/Organization"
            },
            "type": "array"
          },
          "next_cursor": {
            "description": "Opaque cursor for the next page. Pass it as the `cursor` query parameter; `null` when there are no more pages.",
            "nullable": true,
            "type": "string"
          }
        },
        "required": [
          "data",
          "next_cursor"
        ],
        "title": "OrganizationList",
        "type": "object"
      },
      "RedeemCouponRequest": {
        "description": "Redeems a coupon for the organization identified by the path. The org is taken from the route (`:org`); the body carries only the coupon code.",
        "properties": {
          "code": {
            "description": "The coupon code to redeem.",
            "type": "string"
          }
        },
        "required": [
          "code"
        ],
        "title": "RedeemCouponRequest",
        "type": "object"
      },
      "ConnectBitbucketRequest": {
        "description": "OAuth callback payload from the SPA.",
        "example": {
          "code": "abc123",
          "state": "SFMyNTY..."
        },
        "properties": {
          "code": {
            "description": "Authorization code from Bitbucket.",
            "type": "string"
          },
          "state": {
            "description": "The signed CSRF state nonce echoed back by Bitbucket on the callback. Issued by the `oauth-url` endpoint and bound to the authenticated user.",
            "type": "string"
          }
        },
        "required": [
          "code",
          "state"
        ],
        "title": "ConnectBitbucketRequest",
        "type": "object"
      },
      "AcceptInviteRequest": {
        "properties": {
          "token": {
            "type": "string"
          }
        },
        "required": [
          "token"
        ],
        "title": "AcceptInviteRequest",
        "type": "object"
      }
    },
    "securitySchemes": {
      "bearer": {
        "description": "A Harmont session bearer token.",
        "scheme": "bearer",
        "type": "http"
      },
      "runnerToken": {
        "description": "A build's single-issue runner token, presented as `Authorization: Bearer <runner_token>`. Used only by the in-VM agent for the internal source-archive endpoint; validated against the build's stored hash (non-consuming).",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "Harmont's user-facing REST authentication API.",
    "title": "Harmont API",
    "version": "0"
  },
  "openapi": "3.0.0",
  "paths": {
    "/api/v0/auth/cli/claim": {
      "post": {
        "callbacks": {},
        "description": "The CLI polls with the nonce it generated; on a match within the 60s window it receives the raw session token (single-use).",
        "operationId": "cliClaim",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CliClaimRequest"
              }
            }
          },
          "description": "Claim request",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliTokenResponse"
                }
              }
            },
            "description": "The session token"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No token available for this nonce"
          }
        },
        "summary": "Claim a transferred session token (CLI loopback poll)",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v0/auth/cli/code": {
      "post": {
        "callbacks": {},
        "description": "Mints a fresh session token for the current user and a short paste code (valid 5 minutes) the user re-types into the CLI to redeem it.",
        "operationId": "cliCode",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliCodeResponse"
                }
              }
            },
            "description": "The paste code"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mint a human-typeable CLI paste code",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v0/auth/cli/redeem": {
      "post": {
        "callbacks": {},
        "description": "The CLI submits the paste code the user typed in; on a match within the 5m window it receives the raw session token (single-use).",
        "operationId": "cliRedeem",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CliRedeemRequest"
              }
            }
          },
          "description": "Redeem request",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliTokenResponse"
                }
              }
            },
            "description": "The session token"
          },
          "400": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid or expired paste code"
          }
        },
        "summary": "Redeem a CLI paste code for a session token",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v0/auth/cli/transfer": {
      "post": {
        "callbacks": {},
        "description": "Mints a fresh session token for the current user and parks it under the CLI-supplied nonce for 60 seconds. The CLI claims it via the claim endpoint.",
        "operationId": "cliTransfer",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CliTransferRequest"
              }
            }
          },
          "description": "Transfer request",
          "required": false
        },
        "responses": {
          "204": {
            "description": "Token parked for the CLI to claim"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Hand a session token to a locally-running CLI (loopback)",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v0/auth/logout": {
      "post": {
        "callbacks": {},
        "description": "Revokes the bearer token used for this request. The token can no longer be used to authenticate. Idempotent.",
        "operationId": "logout",
        "parameters": [],
        "responses": {
          "204": {
            "description": "Token revoked"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Log out (revoke the current bearer token)",
        "tags": [
          "auth"
        ]
      }
    },
    "/api/v0/billing/balance/{org}": {
      "get": {
        "callbacks": {},
        "description": "Returns the organization's current balance in cents — the sum of every ledger entry (credits positive, debits negative). May be negative.",
        "operationId": "getBillingBalance",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BalanceResponse"
                }
              }
            },
            "description": "The organization's balance"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get an organization's balance",
        "tags": [
          "billing"
        ]
      }
    },
    "/api/v0/billing/transactions/{org}": {
      "get": {
        "callbacks": {},
        "description": "Returns the organization's ledger entries, newest first, cursor-paginated.",
        "operationId": "listBillingTransactions",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page size (1–100, default 50).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Opaque cursor from a previous page's `next_cursor`.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TransactionList"
                }
              }
            },
            "description": "The organization's ledger entries"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List an organization's ledger entries",
        "tags": [
          "billing"
        ]
      }
    },
    "/api/v0/billing/usage/{org}": {
      "get": {
        "callbacks": {},
        "description": "Aggregates the organization's VM-lease usage over the half-open window `[from, to)` (both ISO-8601 timestamps): resource-seconds per dimension and the total billed cost in cents. Both `from` and `to` are required.",
        "operationId": "getBillingUsage",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Window start (inclusive), ISO-8601.",
            "in": "query",
            "name": "from",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Window end (exclusive), ISO-8601.",
            "in": "query",
            "name": "to",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageResponse"
                }
              }
            },
            "description": "The usage aggregates"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Missing or invalid time window"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get an organization's VM usage",
        "tags": [
          "billing"
        ]
      }
    },
    "/api/v0/billing/usage/{org}/breakdown": {
      "get": {
        "callbacks": {},
        "description": "Returns the organization's VM usage over the half-open `[from, to)` window (both ISO-8601), grouped by build (newest first) and broken down per job lease — pipeline, build number, job, VM handle, resource shape, duration and cost — so a charge can be traced to its source.",
        "operationId": "getBillingUsageBreakdown",
        "parameters": [
          {
            "description": "Organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ISO-8601 window start (inclusive).",
            "in": "query",
            "name": "from",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ISO-8601 window end (exclusive).",
            "in": "query",
            "name": "to",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageBreakdownResponse"
                }
              }
            },
            "description": "Per-build usage breakdown"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid window"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Per-build VM usage breakdown",
        "tags": [
          "billing"
        ]
      }
    },
    "/api/v0/billing/usage/{org}/series": {
      "get": {
        "callbacks": {},
        "operationId": "getBillingUsageSeries",
        "parameters": [
          {
            "description": "Organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ISO-8601 window start (inclusive).",
            "in": "query",
            "name": "from",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "ISO-8601 window end (exclusive).",
            "in": "query",
            "name": "to",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UsageSeriesResponse"
                }
              }
            },
            "description": "Per-day usage buckets"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid window"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Per-day usage time-series",
        "tags": [
          "billing"
        ]
      }
    },
    "/api/v0/invites/accept": {
      "post": {
        "callbacks": {},
        "operationId": "acceptInvite",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AcceptInviteRequest"
              }
            }
          },
          "description": "Token",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "The joined organization"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invite cannot be accepted"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Accept an invite",
        "tags": [
          "organizations"
        ]
      }
    },
    "/api/v0/organizations": {
      "get": {
        "callbacks": {},
        "description": "Returns the organizations the authenticated user is a member of, paginated.",
        "operationId": "listOrganizations",
        "parameters": [
          {
            "description": "Page size (1–100, default 50).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Opaque cursor from a previous page's `next_cursor`.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrganizationList"
                }
              }
            },
            "description": "The user's organizations"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List the current user's organizations",
        "tags": [
          "organizations"
        ]
      },
      "post": {
        "callbacks": {},
        "description": "Creates a new organization with the authenticated user as its owner.",
        "operationId": "createOrganization",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateOrganizationRequest"
              }
            }
          },
          "description": "Organization to create",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Organization"
                }
              }
            },
            "description": "The created organization"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid organization attributes"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create an organization",
        "tags": [
          "organizations"
        ]
      }
    },
    "/api/v0/organizations/{org}/builds": {
      "post": {
        "callbacks": {},
        "description": "Creates a build by addressing the pipeline through its repo-natural identity (`repo_name` + `source_slug`) rather than the org-global slug. This is the `hm run` path. Resolution, manual-build gating, billing, and IR handling are otherwise identical to `createBuild`. A repo/source slug that matches no pipeline yields 404.",
        "operationId": "createBuildBySource",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRepoBuildRequest"
              }
            }
          },
          "description": "Build attributes",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Build"
                }
              }
            },
            "description": "The created build"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The organization's balance is exhausted"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Manual builds are disabled for this pipeline"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No pipeline for that repo + source slug"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The pipeline IR was rejected"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a build by repo + source slug",
        "tags": [
          "builds"
        ]
      }
    },
    "/api/v0/organizations/{org}/invites": {
      "get": {
        "callbacks": {},
        "operationId": "listInvites",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InviteList"
                }
              }
            },
            "description": "Invites"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List pending invites",
        "tags": [
          "organizations"
        ]
      },
      "post": {
        "callbacks": {},
        "operationId": "createInvite",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateInviteRequest"
              }
            }
          },
          "description": "Invite",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Invite"
                }
              }
            },
            "description": "The created invite (with token)"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Insufficient role"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid or duplicate invite"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Invite an email to the organization",
        "tags": [
          "organizations"
        ]
      }
    },
    "/api/v0/organizations/{org}/invites/{id}": {
      "delete": {
        "callbacks": {},
        "operationId": "revokeInvite",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke a pending invite",
        "tags": [
          "organizations"
        ]
      }
    },
    "/api/v0/organizations/{org}/members": {
      "get": {
        "callbacks": {},
        "operationId": "listOrgMembers",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrgMemberList"
                }
              }
            },
            "description": "Members"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such org for this user"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List organization members",
        "tags": [
          "organizations"
        ]
      }
    },
    "/api/v0/organizations/{org}/members/{user_id}": {
      "delete": {
        "callbacks": {},
        "operationId": "removeOrgMember",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Insufficient role"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Member not found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Cannot remove the last owner"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Remove a member",
        "tags": [
          "organizations"
        ]
      },
      "patch": {
        "callbacks": {},
        "operationId": "updateOrgMember",
        "parameters": [
          {
            "description": "",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "",
            "in": "path",
            "name": "user_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateMemberRoleRequest"
              }
            }
          },
          "description": "New role",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OrgMember"
                }
              }
            },
            "description": "Updated member"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Insufficient role"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Member not found"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Cannot demote the last owner"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid role value"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Change a member's role",
        "tags": [
          "organizations"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines": {
      "get": {
        "callbacks": {},
        "description": "Returns the organization's non-archived pipelines, paginated. A slug the user cannot access is reported as 404.",
        "operationId": "listPipelines",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page size (1–100, default 50).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Opaque cursor from a previous page's `next_cursor`.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PipelineList"
                }
              }
            },
            "description": "The organization's pipelines"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization for this user"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List an organization's pipelines",
        "tags": [
          "pipelines"
        ]
      },
      "post": {
        "callbacks": {},
        "description": "Creates a pipeline in the organization. The slug is derived from the name; a colliding slug within the organization yields 422.",
        "operationId": "createPipeline",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePipelineRequest"
              }
            }
          },
          "description": "Pipeline attributes",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pipeline"
                }
              }
            },
            "description": "The created pipeline"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization for this user"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Validation failed (e.g. slug already taken)"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a pipeline",
        "tags": [
          "pipelines"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}": {
      "get": {
        "callbacks": {},
        "description": "Returns the pipeline identified by the path slug within the organization. An unknown slug (or one in another organization) is reported as 404.",
        "operationId": "getPipeline",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Pipeline"
                }
              }
            },
            "description": "The pipeline"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such pipeline in this organization"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a pipeline",
        "tags": [
          "pipelines"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}/builds": {
      "get": {
        "callbacks": {},
        "description": "Returns the pipeline's builds, newest first, paginated.",
        "operationId": "listBuilds",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "Page size (1–100, default 50).",
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "Opaque cursor from a previous page's `next_cursor`.",
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BuildList"
                }
              }
            },
            "description": "The pipeline's builds"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization or pipeline"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List a pipeline's builds",
        "tags": [
          "builds"
        ]
      },
      "post": {
        "callbacks": {},
        "description": "Creates a build for the pipeline and starts execution in-process. When `pipeline_ir` is supplied the IR is materialised directly; when it is absent the engine renders the pipeline's IR in a sandbox VM first (rendering never happens on the API host). A manual build against a pipeline that disallows manual builds yields 403; an IR that fails to render/parse/plan yields 422 (the build row is created with its error fields set).",
        "operationId": "createBuild",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBuildRequest"
              }
            }
          },
          "description": "Build attributes",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Build"
                }
              }
            },
            "description": "The created build"
          },
          "402": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The organization's balance is exhausted"
          },
          "403": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Manual builds are disabled for this pipeline"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such organization or pipeline"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "The pipeline IR was rejected"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a build",
        "tags": [
          "builds"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}/builds/{number}": {
      "get": {
        "callbacks": {},
        "description": "Returns the build identified by its pipeline-scoped number. An unknown number (or one in another pipeline) is reported as 404.",
        "operationId": "getBuild",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The build number.",
            "in": "path",
            "name": "number",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Build"
                }
              }
            },
            "description": "The build"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such build in this pipeline"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a build",
        "tags": [
          "builds"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}/builds/{number}/cancel": {
      "put": {
        "callbacks": {},
        "description": "Cancels an in-flight build in-process (transitions non-terminal jobs and tears down their sandboxes). Idempotent: cancelling an already-terminal build is a no-op. Returns the reloaded build with its updated state.",
        "operationId": "cancelBuild",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The build number.",
            "in": "path",
            "name": "number",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Build"
                }
              }
            },
            "description": "The cancelled build"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such build in this pipeline"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Cancel a build",
        "tags": [
          "builds"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}/builds/{number}/jobs": {
      "get": {
        "callbacks": {},
        "description": "Returns the build's jobs in DAG creation order.",
        "operationId": "listJobs",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The build number.",
            "in": "path",
            "name": "number",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobList"
                }
              }
            },
            "description": "The build's jobs"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such build in this pipeline"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List a build's jobs",
        "tags": [
          "jobs"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}/builds/{number}/jobs/{job_id}": {
      "get": {
        "callbacks": {},
        "description": "Returns a single job within the build. A `job_id` that belongs to another build is reported as 404.",
        "operationId": "getJob",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The build number.",
            "in": "path",
            "name": "number",
            "required": true,
            "schema": {
              "type": "integer"
            }
          },
          {
            "description": "The job id.",
            "in": "path",
            "name": "job_id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            },
            "description": "The job"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such job in this build"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get a job",
        "tags": [
          "jobs"
        ]
      }
    },
    "/api/v0/organizations/{org}/pipelines/{pipeline}/builds/{number}/log-token": {
      "get": {
        "callbacks": {},
        "description": "Returns a short-lived (~1 hour) HMAC token the SSE log stream accepts, scoped to this build. Pass it as the stream's `token` query parameter.",
        "operationId": "getBuildLogToken",
        "parameters": [
          {
            "description": "The organization slug.",
            "in": "path",
            "name": "org",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The pipeline slug.",
            "in": "path",
            "name": "pipeline",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "description": "The build number.",
            "in": "path",
            "name": "number",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogTokenResponse"
                }
              }
            },
            "description": "The log token and its expiry"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such build in this pipeline"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Mint a build-scoped log token",
        "tags": [
          "builds"
        ]
      }
    },
    "/api/v0/user": {
      "delete": {
        "callbacks": {},
        "operationId": "deleteCurrentUser",
        "parameters": [],
        "responses": {
          "204": {
            "description": "Account deleted"
          },
          "409": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Account has billing history"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Delete the current user's account",
        "tags": [
          "user"
        ]
      },
      "get": {
        "callbacks": {},
        "description": "Returns the bearer-authenticated user and their personal-organization slug.",
        "operationId": "getCurrentUser",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentUserResponse"
                }
              }
            },
            "description": "The current user"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Get the current authenticated user",
        "tags": [
          "user"
        ]
      },
      "patch": {
        "callbacks": {},
        "operationId": "updateCurrentUser",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UserUpdateRequest"
              }
            }
          },
          "description": "Profile update",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CurrentUserResponse"
                }
              }
            },
            "description": "The updated user"
          },
          "422": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "Invalid name"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Update the current user's display name",
        "tags": [
          "user"
        ]
      }
    },
    "/api/v0/user/api-tokens": {
      "get": {
        "callbacks": {},
        "operationId": "listApiTokens",
        "parameters": [],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiTokenListResponse"
                }
              }
            },
            "description": "The user's API keys"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "List the current user's API keys",
        "tags": [
          "user"
        ]
      },
      "post": {
        "callbacks": {},
        "description": "Returns the raw secret in the response body. It is shown only once and cannot be retrieved later.",
        "operationId": "createApiToken",
        "parameters": [],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiTokenCreateRequest"
              }
            }
          },
          "description": "Create request",
          "required": false
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiTokenCreateResponse"
                }
              }
            },
            "description": "The created key and its one-time secret"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Create a personal API key",
        "tags": [
          "user"
        ]
      }
    },
    "/api/v0/user/api-tokens/{id}": {
      "delete": {
        "callbacks": {},
        "operationId": "revokeApiToken",
        "parameters": [
          {
            "description": "The API key id.",
            "in": "path",
            "name": "id",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Key revoked"
          },
          "404": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            },
            "description": "No such key for this user"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ],
        "summary": "Revoke one of the current user's API keys",
        "tags": [
          "user"
        ]
      }
    }
  },
  "security": [],
  "servers": [
    {
      "url": "https://api.harmont.dev",
      "variables": {}
    }
  ],
  "tags": [
    {
      "name": "auth"
    },
    {
      "name": "billing"
    },
    {
      "name": "builds"
    },
    {
      "name": "jobs"
    },
    {
      "name": "organizations"
    },
    {
      "name": "pipelines"
    },
    {
      "name": "user"
    }
  ]
}
