> ## Documentation Index
> Fetch the complete documentation index at: https://docs.outerlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Report a heartbeat from a runner host

> Upserts one row for `host`, stamping the platform's own clock for the heartbeat time and, when `workRequest` is present, for the last success or the last error. `runnerProtocol` is the gateway contract the runner was built against; a heartbeat without it is stored as 0, below every minimum. `workRequest` is omitted when this poll made no request for work; its error, when present, is only what the platform already returned to this host. `lastUpdate` is the runner's last self-update; omitted, it leaves the stored one as it was. `lowDisk` is the free and needed bytes of a runner that holds off new work for want of disk; omitted, it clears the stored figures. `blocked` is the reason a runner claims nothing because a check of its own failed (no `/dev/kvm`, no Claude credential, a placeholder command, builds that do not fit in memory); omitted, it clears the stored reason. Answers with the host in the same shape `GET /v1/hosts` lists it in, plus `runnerVersion`, the `@outerlayer/cli` version the factory's runner update setting picks: the version this gateway was built from (`follow`, the default), the runner's own `cliVersion` (`paused`), or the held version (`pinned`).



## OpenAPI

````yaml /openapi.yaml post /v1/hosts/heartbeat
openapi: 3.0.3
info:
  contact:
    email: hello@outerlayer.ai
    url: https://www.outerlayer.ai
  description: >-
    The OuterLayer Gateway API lets you ingest coding-agent sessions and record
    evidence of work programmatically.


    Most teams reach it through the `outerlayer` CLI, which calls it for them.

    Call it directly to build your own integration or to automate what the
    dashboard does.


    Versioning: every endpoint is prefixed with `/v1/`. Breaking changes ship
    under a new version prefix (`/v2/`, etc.) with a 90-day-minimum deprecation
    window; anything additive lands on the current prefix.


    Vocabulary: the dashboard calls this API's `app` a **factory** — the unit
    that owns a set of repositories, the agent sessions worked on them, and the
    evidence they produce. They are the same entity. The API keeps `app` in its
    paths, headers, and field names because those are a published contract; only
    the product surface was renamed.
  title: OuterLayer Gateway API
  version: '1.0'
servers:
  - description: Production (OuterLayer Cloud)
    url: https://api.outerlayer.ai
  - description: Local dev server (self-hosted gateway)
    url: http://localhost:9418
security:
  - AppId: []
    BearerAuth: []
tags:
  - description: >-
      What a CLI login token can read about its owner without naming a factory,
      such as the factories they can use.
    name: Account
  - description: >-
      Coding-agent session ingest (outerlayer sync) and content-addressed
      session images.
    name: Agents
  - description: >-
      Evidence artifacts (screenshots, recordings, reports, logs) emitted as
      proof of a change and anchored to pull requests.
    name: Artifacts
  - description: >-
      Pass/fail outcomes of checks run in your own CI or compute, anchored to
      pull requests with the run URL as proof.
    name: Emitted Results
  - description: >-
      The acceptance criteria of a work item, recorded as one list. The Criteria
      tab, its count and the item status read that list.
    name: Criteria
  - description: >-
      What any agent found wrong about a change under review or about a rule it
      ran on, recorded on a work item.
    name: Findings
  - description: >-
      Add or remove a source's statement that it is working on an issue or pull
      request, and read what is currently live.
    name: Work Items
  - description: >-
      What the factory's GitHub App installation lets builds do on each
      connected repository, and whether each default branch requires a pull
      request.
    name: Repositories
  - description: >-
      A runner host's heartbeat — its CLI version, slots and last request for
      work — and the factory's list of hosts with a derived status.
    name: Hosts
  - description: Query server feature availability.
    name: Capabilities
  - description: Per-model LLM pricing data. Public, unauthenticated.
    name: Pricing
  - description: >-
      Create, list, and revoke factory API keys. Plaintext returned exactly once
      at creation.
    name: API Keys
  - description: >-
      Factory CRUD — a factory is owned by an organization and is the unit every
      other resource hangs off. Lets a headless agent provision one without the
      dashboard.
    name: Apps
  - description: >-
      Agent-coding session list and full transcript reads, with actor-privacy
      controls for machine keys.
    name: Sessions
  - description: >-
      Per-model token spend and fleet-wide agent behavior tiles, including
      two-window comparisons.
    name: Metrics
  - description: Synced-commit history of the app's `.outerlayer/` context tree.
    name: Context
  - description: >-
      Session→PR attribution: which agent sessions produced which pull requests,
      and what each attributed PR cost.
    name: PRs
  - description: Service health checks.
    name: Health
  - description: >-
      Org member and role administration, authenticated with an org-scoped
      management API key (`olk_…`) minted in the dashboard's settings.
    name: Org Management
  - description: OAuth 2.1 discovery metadata for MCP connector clients.
    name: OAuth
paths:
  /v1/hosts/heartbeat:
    post:
      tags:
        - Hosts
      summary: Report a heartbeat from a runner host
      description: >-
        Upserts one row for `host`, stamping the platform's own clock for the
        heartbeat time and, when `workRequest` is present, for the last success
        or the last error. `runnerProtocol` is the gateway contract the runner
        was built against; a heartbeat without it is stored as 0, below every
        minimum. `workRequest` is omitted when this poll made no request for
        work; its error, when present, is only what the platform already
        returned to this host. `lastUpdate` is the runner's last self-update;
        omitted, it leaves the stored one as it was. `lowDisk` is the free and
        needed bytes of a runner that holds off new work for want of disk;
        omitted, it clears the stored figures. `blocked` is the reason a runner
        claims nothing because a check of its own failed (no `/dev/kvm`, no
        Claude credential, a placeholder command, builds that do not fit in
        memory); omitted, it clears the stored reason. Answers with the host in
        the same shape `GET /v1/hosts` lists it in, plus `runnerVersion`, the
        `@outerlayer/cli` version the factory's runner update setting picks: the
        version this gateway was built from (`follow`, the default), the
        runner's own `cliVersion` (`paused`), or the held version (`pinned`).
      operationId: record-host-heartbeat
      parameters:
        - description: >-
            HTTP Message Signature (RFC 9421) made with the runner key's host
            key: `runner=:<base64>:`.
          in: header
          name: Signature
          required: false
          schema:
            description: >-
              HTTP Message Signature (RFC 9421) made with the runner key's host
              key: `runner=:<base64>:`.
            type: string
        - description: >-
            The signature's covered components and parameters:
            `runner=("@method" "@path" "@query" "content-digest");created=<unix
            seconds>;nonce="<random>";keyid="<host key
            fingerprint>";alg="ed25519"`.
          in: header
          name: Signature-Input
          required: false
          schema:
            description: >-
              The signature's covered components and parameters:
              `runner=("@method" "@path" "@query"
              "content-digest");created=<unix
              seconds>;nonce="<random>";keyid="<host key
              fingerprint>";alg="ed25519"`.
            type: string
        - description: >-
            RFC 9530 digest of the request body, always sent, including for a
            request with none: `sha-256=:<base64>:`.
          in: header
          name: Content-Digest
          required: false
          schema:
            description: >-
              RFC 9530 digest of the request body, always sent, including for a
              request with none: `sha-256=:<base64>:`.
            type: string
      requestBody:
        content:
          application/json:
            schema:
              properties:
                blocked:
                  properties:
                    reason:
                      maxLength: 500
                      minLength: 1
                      type: string
                  required:
                    - reason
                  type: object
                cliVersion:
                  maxLength: 64
                  minLength: 1
                  type: string
                host:
                  maxLength: 255
                  minLength: 1
                  type: string
                lastUpdate:
                  properties:
                    at:
                      format: date-time
                      type: string
                    from:
                      maxLength: 64
                      minLength: 1
                      type: string
                    outcome:
                      enum:
                        - updated
                        - rolled_back
                        - verification_failed
                        - not_published
                        - retrying
                      type: string
                    reason:
                      maxLength: 500
                      minLength: 1
                      nullable: true
                      type: string
                    to:
                      maxLength: 64
                      minLength: 1
                      type: string
                  required:
                    - from
                    - to
                    - outcome
                    - reason
                  type: object
                lowDisk:
                  properties:
                    free:
                      maximum: 9007199254740991
                      minimum: 0
                      type: integer
                    needed:
                      maximum: 9007199254740991
                      minimum: 0
                      type: integer
                  required:
                    - free
                    - needed
                  type: object
                pollSeconds:
                  maximum: 86400
                  minimum: 1
                  type: integer
                runnerProtocol:
                  maximum: 1000000
                  minimum: 0
                  type: integer
                slotsTotal:
                  minimum: 1
                  type: integer
                slotsUsed:
                  minimum: 0
                  type: integer
                workRequest:
                  anyOf:
                    - properties:
                        ok:
                          enum:
                            - true
                          type: boolean
                      required:
                        - ok
                      type: object
                    - properties:
                        code:
                          maxLength: 100
                          minLength: 1
                          type: string
                        message:
                          maxLength: 500
                          minLength: 1
                          type: string
                        ok:
                          enum:
                            - false
                          type: boolean
                      required:
                        - ok
                        - code
                        - message
                      type: object
              required:
                - host
                - cliVersion
                - pollSeconds
                - slotsUsed
                - slotsTotal
              type: object
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    properties:
                      blocked:
                        nullable: true
                        properties:
                          reason:
                            type: string
                        required:
                          - reason
                        type: object
                      cliVersion:
                        type: string
                      host:
                        type: string
                      lastError:
                        nullable: true
                        properties:
                          at:
                            type: string
                          code:
                            type: string
                          message:
                            type: string
                        required:
                          - code
                          - message
                          - at
                        type: object
                      lastHeartbeatAt:
                        type: string
                      lastSuccessAt:
                        nullable: true
                        type: string
                      lastUpdate:
                        nullable: true
                        properties:
                          at:
                            nullable: true
                            type: string
                          from:
                            type: string
                          outcome:
                            enum:
                              - updated
                              - rolled_back
                              - verification_failed
                              - not_published
                              - retrying
                            type: string
                          reason:
                            nullable: true
                            type: string
                          to:
                            type: string
                        required:
                          - from
                          - to
                          - outcome
                          - reason
                          - at
                        type: object
                      lowDisk:
                        nullable: true
                        properties:
                          free:
                            type: integer
                          needed:
                            type: integer
                        required:
                          - free
                          - needed
                        type: object
                      pollSeconds:
                        type: integer
                      runnerProtocol:
                        type: integer
                      runnerVersion:
                        type: string
                      slotsTotal:
                        type: integer
                      slotsUsed:
                        type: integer
                      status:
                        enum:
                          - ok
                          - failing
                          - outdated
                          - stale
                        type: string
                    required:
                      - host
                      - cliVersion
                      - runnerProtocol
                      - pollSeconds
                      - slotsUsed
                      - slotsTotal
                      - lastHeartbeatAt
                      - lastSuccessAt
                      - lastError
                      - lastUpdate
                      - lowDisk
                      - blocked
                      - status
                      - runnerVersion
                    type: object
                required:
                  - data
                type: object
          description: The heartbeat is recorded.
        '400':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                    type: object
                required:
                  - error
                type: object
          description: A malformed heartbeat body.
        '401':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                    type: object
                required:
                  - error
                type: object
          description: >-
            Missing or invalid API key, or a runner key's signature was refused:
            `runner_key_signature_required` (the request carries no signature),
            `runner_key_signature_invalid` (it does not verify against the host
            key the key is bound to, or the body does not match its digest),
            `runner_key_signature_stale` (`created` is more than five minutes
            from the gateway's clock) or `runner_key_signature_replayed` (the
            nonce was accepted in the last five minutes).
        '403':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                    type: object
                required:
                  - error
                type: object
          description: The caller lacks hosts.ingest.
components:
  securitySchemes:
    AppId:
      description: Factory id the request is scoped to.
      in: header
      name: X-Outerlayer-App-Id
      type: apiKey
    BearerAuth:
      description: API key (sk_outerlayer_*)
      scheme: bearer
      type: http

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.