Latchkey Jobs API Jobs API

Create, submit, observe and cancel runner jobs.

Operations 6

POST /jobs Create a job #
GET /jobs List recent jobs #
GET /jobs/{id} Get job status #
POST /jobs/{id}/submit Submit a job for execution #
GET /jobs/{id}/logs Read job logs #
POST /jobs/{id}/cancel Cancel a job #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/latchkey-jobs-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

latchkey-jobs-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Latchkey Jobs API
  version: 1.0.0
  summary: Run a single command on a fresh Latchkey runner, with no GitHub in the path.
  description: The Jobs API gives a coding agent or CI system direct access to an ephemeral Latchkey runner.
  contact:
    name: Latchkey Support
    url: https://latchkey.dev/support
    email: support@latchkey.dev
  license:
    name: Proprietary
    url: https://latchkey.dev/terms
servers:
- url: https://api.latchkey.dev
  description: Production
security:
- latchkeyApiKey: []
tags:
- name: Jobs
  description: Create, submit, observe and cancel runner jobs.
paths:
  /jobs:
    post:
      operationId: createJob
      tags:
      - Jobs
      summary: Create a job
      description: Reserves a job in state `created` and returns its id. If `context_bytes` is supplied, the response includes a presigned URL to upload a gzipped tar archive of your working tree before submitting. Creating a job does not start it; call submitJob next.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateJobRequest'
      responses:
        '200':
          description: Job created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateJobResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
    get:
      operationId: listJobs
      tags:
      - Jobs
      summary: List recent jobs
      description: Returns recent jobs for the organization that owns the API key, newest first. Served from durable storage, so it outlives an individual job record and can answer what was run last week.
      parameters:
      - name: limit
        in: query
        required: false
        description: Maximum jobs to return.
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
      responses:
        '200':
          description: A page of jobs.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobListResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
  /jobs/{id}:
    parameters:
    - $ref: '#/components/parameters/JobId'
    get:
      operationId: getJob
      tags:
      - Jobs
      summary: Get job status
      description: Returns the current state of one job, including exit code and failure reason once terminal. A submitted job that has not started within one hour is reported as `expired`.
      responses:
        '200':
          description: Current job status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobStatusResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /jobs/{id}/submit:
    parameters:
    - $ref: '#/components/parameters/JobId'
    post:
      operationId: submitJob
      tags:
      - Jobs
      summary: Submit a job for execution
      description: Moves a job from `created` to `queued` and enqueues a runner for it. Only a job in `created` may be submitted, so a failed or cancelled job can never be resubmitted. Upload the context archive first if you created the job with `context_bytes`.
      responses:
        '200':
          description: Job queued.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubmitJobResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          description: Job is not in a submittable state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /jobs/{id}/logs:
    parameters:
    - $ref: '#/components/parameters/JobId'
    get:
      operationId: getJobLogs
      tags:
      - Jobs
      summary: Read job logs
      description: Returns ordered log chunks from the given cursor. Poll with the returned `next_cursor` until `complete` is true to stream a running job's output.
      parameters:
      - name: cursor
        in: query
        required: false
        description: Chunk index to read from. Omit to start at the beginning.
        schema:
          type: integer
          minimum: 0
          default: 0
      responses:
        '200':
          description: Log chunks.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobLogsResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /jobs/{id}/cancel:
    parameters:
    - $ref: '#/components/parameters/JobId'
    post:
      operationId: cancelJob
      tags:
      - Jobs
      summary: Cancel a job
      description: Requests cancellation. A job that has not started is cancelled immediately; a running job is signalled and terminalises shortly after. Cancellation is idempotent.
      responses:
        '200':
          description: Cancellation recorded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CancelJobResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  responses:
    BadRequest:
      description: The request was malformed or exceeded a documented limit.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: No such job for this organization.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Forbidden:
      description: The key is valid but lacks the required scope, or the organization is not entitled.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: Per-organization job creation quota exceeded.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  schemas:
    SubmitJobResponse:
      type: object
      required:
      - job_id
      - state
      properties:
        job_id:
          type: string
          description: Identifier of the submitted job.
        state:
          $ref: '#/components/schemas/JobState'
        context_present:
          type:
          - boolean
          - 'null'
          description: Whether a context archive was found for this job.
    JobListResponse:
      type: object
      required:
      - jobs
      properties:
        jobs:
          type: array
          description: Jobs, newest first.
          items:
            $ref: '#/components/schemas/JobListItem'
    CreateJobRequest:
      type: object
      required:
      - command
      - runner_size
      properties:
        command:
          type: string
          maxLength: 16384
          description: The single shell command to run on the runner.
          examples:
          - npm ci && npm test
        runner_size:
          $ref: '#/components/schemas/RunnerSize'
        env:
          type: object
          additionalProperties:
            type: string
          description: Environment variables for the command. At most 64 keys and 32768 bytes total.
          maxProperties: 64
        timeout_seconds:
          type: integer
          minimum: 30
          maximum: 7200
          default: 1800
          description: Wall-clock limit for the command.
        context_bytes:
          type: integer
          minimum: 1
          maximum: 209715200
          description: Byte length of the gzipped tar archive you intend to upload. Supplying it returns a presigned upload URL bound to this exact length.
    CancelJobResponse:
      type: object
      required:
      - job_id
      - state
      - cancel_requested
      properties:
        job_id:
          type: string
          description: Identifier of the job.
        state:
          $ref: '#/components/schemas/JobState'
        cancel_requested:
          type: boolean
          description: Always true once cancellation has been recorded.
    JobLogChunk:
      type: object
      required:
      - index
      - content
      properties:
        index:
          type: integer
          description: Zero-based chunk index.
        content:
          type: string
          description: Raw log text for this chunk.
    JobStatusResponse:
      type: object
      required:
      - job_id
      - state
      - runner_size
      - timeout_seconds
      - created_at
      properties:
        job_id:
          type: string
          description: Identifier of the job.
        state:
          $ref: '#/components/schemas/JobState'
        runner_size:
          $ref: '#/components/schemas/RunnerSize'
        timeout_seconds:
          type: integer
          description: Wall-clock limit in seconds.
        cancel_requested:
          type: boolean
          description: Whether cancellation has been requested.
        exit_code:
          type:
          - integer
          - 'null'
          description: Process exit code once the command has finished.
        failure_reason:
          type:
          - string
          - 'null'
          description: Why the job failed, when it did.
        created_at:
          type: string
          format: date-time
        submitted_at:
          type:
          - string
          - 'null'
          format: date-time
        started_at:
          type:
          - string
          - 'null'
          format: date-time
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
    CreateJobResponse:
      type: object
      required:
      - job_id
      properties:
        job_id:
          type: string
          description: Identifier for the new job.
        context_upload_url:
          type: string
          format: uri
          description: Presigned PUT URL for the context archive. Present only when `context_bytes` was supplied. Upload with Content-Type `application/gzip` before calling submitJob.
    JobLogsResponse:
      type: object
      required:
      - chunks
      - next_cursor
      - complete
      properties:
        chunks:
          type: array
          description: Ordered log chunks from the requested cursor.
          items:
            $ref: '#/components/schemas/JobLogChunk'
        next_cursor:
          type: integer
          description: Cursor to pass on the next poll.
        complete:
          type: boolean
          description: True once the job is terminal and all logs have been returned.
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
          description: Human-readable description of the problem.
    JobState:
      type: string
      description: Job lifecycle state. `succeeded`, `failed`, `cancelled` and `expired` are terminal.
      enum:
      - created
      - queued
      - provisioning
      - running
      - succeeded
      - failed
      - cancelled
      - expired
    JobListItem:
      type: object
      required:
      - job_id
      - conclusion
      - runner_size
      - command_label
      - queued_at
      properties:
        job_id:
          type: string
          description: Identifier of the job.
        conclusion:
          type: string
          description: Terminal outcome, or an empty string while the job is still in flight.
        runner_size:
          type: string
          description: Runner size the job ran on.
        command_label:
          type: string
          description: Binary plus first subcommand, with arguments scrubbed.
        exit_code:
          type:
          - integer
          - 'null'
          description: Process exit code.
        duration_ms:
          type: integer
          description: Run duration in milliseconds.
        queued_at:
          type: string
          format: date-time
        completed_at:
          type:
          - string
          - 'null'
          format: date-time
    RunnerSize:
      type: string
      description: Latchkey runner t-shirt size. Determines vCPU and memory.
      enum:
      - small
      - medium
      - large
      - xlarge
  parameters:
    JobId:
      name: id
      in: path
      required: true
      description: Job id returned by createJob.
      schema:
        type: string
        pattern: ^cli-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
        examples:
        - cli-3f2a1b4c-5d6e-7f80-91a2-b3c4d5e6f708
  securitySchemes:
    latchkeyApiKey:
      type: http
      scheme: bearer
      description: 'A Latchkey API key, sent as `Authorization: Bearer lk_live_...`. Create one in the Latchkey dashboard. Scopes: `jobs:read` for getJob, getJobLogs and listJobs; `jobs:run` for createJob, submitJob and cancelJob.'
externalDocs:
  description: Latchkey CLI and agent documentation
  url: https://latchkey.dev/documentation/latchkey-cli