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

# Emit an artifact (evidence for a pull request)

> Accepts one artifact — a screenshot, recording, report, or log emitted as proof that a change works — plus its content-addressed blob. The artifact anchors to a pull request directly (prNumber), through the recorded session that produced it, or through git context resolved to a PR later; a request with no anchor at all is refused and nothing is stored. `kind` is inferred from the media type and provenance (session / ci / local) is derived from the submission path — neither can be supplied by the caller. A session binding must name a session that already synced for the app, with `turnIndex` within its recorded turns; note the binding proves the session exists, not that the bytes came from it. `ci` is advisory: it is honored only for a shared (non-actor) API key and downgrades to `local` otherwise. An XML file whose root is `testsuites` or `testsuite` is stored as kind `test` with its parsed tests; `tests` selects which of them bind to the criterion. A JUnit file that declares a DOCTYPE, malformed XML bound to a criterion, or a selected test the file lacks is refused with `unreadable_test_results`. `bytes` must equal the decoded blob length. `emittedAt` is display metadata; because it also starts the unmatched grace clock, the stored value is clamped to a bounded window ending at server receipt time. Retrying with the same clientArtifactId returns the already-stored artifact when the content matches and fails with 409 when it does not. `replaces` names the clientArtifactId of an earlier artifact this one corrects: on success that artifact is marked superseded in the same request, and every display surface hides it from then on (its bytes and deep link still work). `replacesTargets` names several at once (2-20 ids, no repeats) — one correction retires every listed target. REQUIRED together: when `replacesTargets` is present, `replaces` MUST also be present and equal `replacesTargets[0]` — a request with `replacesTargets` and no `replaces`, or a `replaces` that disagrees with the array's first element, is refused as malformed rather than guessed at, so a caller unaware of `replacesTargets` always sees exactly one target retired, never zero. Every named target is resolved by its own id and stamped in a single all-or-nothing operation: an unknown, cross-app, or already-superseded target refuses the WHOLE request, naming the offending id — nothing is created and nothing is stamped. The response carries `url`, the artifact's dashboard page, when the gateway has a dashboard origin configured.



## OpenAPI

````yaml /openapi.yaml post /v1/artifacts
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: >-
      Find, file and edit the issue a spec lives in, through OuterLayer, with no
      tracker credential on the caller's side.
    name: Specs
  - 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/artifacts:
    post:
      tags:
        - Artifacts
      summary: Emit an artifact (evidence for a pull request)
      description: >-
        Accepts one artifact — a screenshot, recording, report, or log emitted
        as proof that a change works — plus its content-addressed blob. The
        artifact anchors to a pull request directly (prNumber), through the
        recorded session that produced it, or through git context resolved to a
        PR later; a request with no anchor at all is refused and nothing is
        stored. `kind` is inferred from the media type and provenance (session /
        ci / local) is derived from the submission path — neither can be
        supplied by the caller. A session binding must name a session that
        already synced for the app, with `turnIndex` within its recorded turns;
        note the binding proves the session exists, not that the bytes came from
        it. `ci` is advisory: it is honored only for a shared (non-actor) API
        key and downgrades to `local` otherwise. An XML file whose root is
        `testsuites` or `testsuite` is stored as kind `test` with its parsed
        tests; `tests` selects which of them bind to the criterion. A JUnit file
        that declares a DOCTYPE, malformed XML bound to a criterion, or a
        selected test the file lacks is refused with `unreadable_test_results`.
        `bytes` must equal the decoded blob length. `emittedAt` is display
        metadata; because it also starts the unmatched grace clock, the stored
        value is clamped to a bounded window ending at server receipt time.
        Retrying with the same clientArtifactId returns the already-stored
        artifact when the content matches and fails with 409 when it does not.
        `replaces` names the clientArtifactId of an earlier artifact this one
        corrects: on success that artifact is marked superseded in the same
        request, and every display surface hides it from then on (its bytes and
        deep link still work). `replacesTargets` names several at once (2-20
        ids, no repeats) — one correction retires every listed target. REQUIRED
        together: when `replacesTargets` is present, `replaces` MUST also be
        present and equal `replacesTargets[0]` — a request with
        `replacesTargets` and no `replaces`, or a `replaces` that disagrees with
        the array's first element, is refused as malformed rather than guessed
        at, so a caller unaware of `replacesTargets` always sees exactly one
        target retired, never zero. Every named target is resolved by its own id
        and stamped in a single all-or-nothing operation: an unknown, cross-app,
        or already-superseded target refuses the WHOLE request, naming the
        offending id — nothing is created and nothing is stamped. The response
        carries `url`, the artifact's dashboard page, when the gateway has a
        dashboard origin configured.
      operationId: emit-artifact
      requestBody:
        content:
          application/json:
            schema:
              properties:
                artifact:
                  properties:
                    bytes:
                      minimum: 0
                      type: integer
                    caption:
                      maxLength: 500
                      type: string
                    ci:
                      type: boolean
                    clientArtifactId:
                      pattern: ^[A-Za-z0-9_.:-]{1,64}$
                      type: string
                    commitSha:
                      maxLength: 64
                      type: string
                    criterionId:
                      pattern: ^[A-Za-z0-9_.:-]{1,64}$
                      type: string
                    emittedAt:
                      type: string
                    filename:
                      maxLength: 120
                      minLength: 1
                      type: string
                    gitBranch:
                      maxLength: 255
                      type: string
                    gitRepo:
                      maxLength: 300
                      type: string
                    mediaType:
                      maxLength: 100
                      minLength: 1
                      type: string
                    prNumber:
                      exclusiveMinimum: true
                      minimum: 0
                      type: integer
                    replaces:
                      pattern: ^[A-Za-z0-9_.:-]{1,64}$
                      type: string
                    replacesTargets:
                      items:
                        pattern: ^[A-Za-z0-9_.:-]{1,64}$
                        type: string
                      maxItems: 20
                      minItems: 2
                      type: array
                      uniqueItems: true
                    repository:
                      maxLength: 200
                      type: string
                    session:
                      properties:
                        sessionId:
                          maxLength: 128
                          minLength: 1
                          type: string
                        turnIndex:
                          minimum: 0
                          type: integer
                      required:
                        - sessionId
                      type: object
                    sha256:
                      pattern: ^[0-9a-f]{64}$
                      type: string
                    tests:
                      items:
                        properties:
                          file:
                            type: string
                          line:
                            exclusiveMinimum: true
                            maximum: 10000000
                            minimum: 0
                            type: integer
                          name:
                            maxLength: 500
                            minLength: 1
                            type: string
                        required:
                          - name
                        type: object
                      maxItems: 100
                      minItems: 1
                      type: array
                  required:
                    - clientArtifactId
                    - filename
                    - mediaType
                    - bytes
                    - sha256
                    - caption
                    - emittedAt
                  type: object
                blob:
                  properties:
                    data:
                      minLength: 1
                      type: string
                  required:
                    - data
                  type: object
                schemaVersion:
                  enum:
                    - 1
                  type: number
              required:
                - schemaVersion
                - artifact
                - blob
              type: object
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    properties:
                      id:
                        type: string
                      kind:
                        enum:
                          - video
                          - screenshot
                          - report
                          - log
                          - test
                          - file
                        type: string
                      prNumber:
                        nullable: true
                        type: integer
                      provenance:
                        enum:
                          - session
                          - ci
                          - local
                        type: string
                      repository:
                        type: string
                      url:
                        description: >-
                          The artifact's page on the dashboard, the same page
                          the pull request's evidence comment links. Absent when
                          the gateway has no dashboard origin configured or
                          cannot read the factory's names.
                        type: string
                      verification:
                        enum:
                          - pending
                          - confirmed
                          - unmatched
                        type: string
                    required:
                      - id
                      - kind
                      - provenance
                      - verification
                      - prNumber
                      - repository
                    type: object
                required:
                  - data
                type: object
          description: The stored artifact (the existing row on an idempotent retry).
        '400':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                    type: object
                required:
                  - error
                type: object
          description: >-
            Malformed body, sha256 or bytes mismatch, unknown session binding,
            turn index out of range, unknown/cross-app/already-superseded
            replaces target, or nothing to attach to.
        '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.
        '409':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                    type: object
                required:
                  - error
                type: object
          description: The clientArtifactId is already stored with different content.
        '413':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      limit:
                        type: integer
                      message:
                        type: string
                    required:
                      - code
                      - message
                      - limit
                    type: object
                required:
                  - error
                type: object
          description: Blob or request exceeds the byte ceiling (`error.limit` names it).
        '429':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      currentBytes:
                        type: number
                      limitBytes:
                        type: number
                      message:
                        type: string
                    required:
                      - code
                      - message
                      - currentBytes
                      - limitBytes
                    type: object
                required:
                  - error
                type: object
          description: >-
            Monthly storage cap exceeded (`error.currentBytes` /
            `error.limitBytes` carry the usage).
        '503':
          content:
            application/json:
              schema:
                properties:
                  error:
                    properties:
                      code:
                        type: string
                      message:
                        type: string
                    required:
                      - code
                      - message
                    type: object
                required:
                  - error
                type: object
          description: Ingest temporarily unavailable; retry after the interval.
          headers:
            Retry-After:
              required: false
              schema:
                minimum: 0
                type: integer
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.