{
  "openapi": "3.1.0",
  "info": {
    "title": "Outmarket API",
    "version": "2.0.0",
    "description": "The Outmarket REST API, version 2."
  },
  "tags": [
    {
      "name": "Health"
    },
    {
      "name": "Jobs"
    }
  ],
  "paths": {
    "/api/v2/health": {
      "get": {
        "operationId": "Health_check",
        "summary": "Check health",
        "description": "Answers while the service behind the API is running, and names it.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The request has succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceHealth"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "tags": [
          "Health"
        ],
        "security": [
          {}
        ]
      }
    },
    "/api/v2/jobs": {
      "get": {
        "operationId": "Jobs_list",
        "summary": "List jobs",
        "description": "Lists the jobs the caller owns.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageParams.page_token"
          },
          {
            "$ref": "#/components/parameters/PageParams.page_size"
          }
        ],
        "responses": {
          "200": {
            "description": "The request has succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JobPage"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/v2/jobs/{job_id}": {
      "get": {
        "operationId": "Jobs_get",
        "summary": "Get a job",
        "description": "Returns a job. While the job is `running` the response carries `Retry-After`, the number of\nseconds to wait before polling again. A job that does not exist or that the caller does not\nown returns 404.",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The job's current state.",
            "headers": {
              "Retry-After": {
                "required": false,
                "description": "Delay in seconds.",
                "schema": {
                  "type": "integer",
                  "format": "int32",
                  "minimum": 0
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist or is not visible to the caller.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/v2/jobs/{job_id}:cancel": {
      "post": {
        "operationId": "Jobs_cancel",
        "summary": "Cancel a job",
        "description": "Asks a running job to stop and returns its current state. The job stops at its next\ncheckpoint, so it may still be `running` in the response; poll it until `status` is\nterminal. Cancelling a job that has already reached a terminal status returns 409\n`CONFLICT`, including on a retry with the same `Idempotency-Key`. A job that does not exist\nor that the caller does not own returns 404.",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "$ref": "#/components/schemas/uuid"
            }
          },
          {
            "$ref": "#/components/parameters/IdempotencyKeyHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "The request has succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Job"
                }
              }
            }
          },
          "404": {
            "description": "The resource does not exist or is not visible to the caller.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "409": {
            "description": "The request conflicts with the resource's current state.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "tags": [
          "Jobs"
        ]
      }
    },
    "/api/v2/ping": {
      "get": {
        "operationId": "Health_ping",
        "summary": "Ping",
        "description": "Answers while the API is reachable.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "The request has succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ping"
                }
              }
            }
          },
          "default": {
            "description": "An error.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        },
        "tags": [
          "Health"
        ],
        "security": [
          {}
        ]
      }
    }
  },
  "security": [
    {
      "ApiToken": []
    }
  ],
  "components": {
    "parameters": {
      "IdempotencyKeyHeader": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Client-generated UUID (v4 or v7), unique per logical request. A retry with the same key and\nbody repeats no work. An operation that starts a job returns the job as it now is. Cancelling\na job does too until the job has finished, then returns 409 `CONFLICT`. Any other write replays\nthe first response. A different body returns `IDEMPOTENCY_KEY_REUSED`.",
        "schema": {
          "$ref": "#/components/schemas/uuid"
        }
      },
      "PageParams.page_size": {
        "name": "page_size",
        "in": "query",
        "required": false,
        "description": "Maximum number of items to return.",
        "schema": {
          "type": "integer",
          "format": "int32",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        },
        "explode": false
      },
      "PageParams.page_token": {
        "name": "page_token",
        "in": "query",
        "required": false,
        "description": "`next_page_token` from the previous page; omit for the first page.",
        "schema": {
          "type": "string"
        },
        "explode": false
      }
    },
    "schemas": {
      "ErrorCode": {
        "type": "string",
        "enum": [
          "AMBIGUOUS_CONNECTION",
          "APPROVAL_REQUIRED",
          "BAD_REQUEST",
          "CAPABILITY_UNAVAILABLE",
          "CONFLICT",
          "CONNECTOR_UNAVAILABLE",
          "CREDENTIALS_INVALID",
          "IDEMPOTENCY_KEY_REUSED",
          "INTERNAL",
          "NOT_CONNECTED",
          "NOT_FOUND",
          "OUTCOME_UNKNOWN",
          "PAYLOAD_TOO_LARGE",
          "PERMISSION_DENIED",
          "PROVISIONING_RETRIES_EXHAUSTED",
          "RATE_LIMITED",
          "REAUTH_REQUIRED",
          "UNAUTHENTICATED",
          "UNAVAILABLE",
          "UPSTREAM_ERROR",
          "UPSTREAM_TIMEOUT",
          "VALIDATION_ERROR",
          "VENDOR_FORBIDDEN",
          "VENDOR_REJECTED",
          "VENDOR_RESPONSE_INVALID",
          "VENDOR_UNAVAILABLE",
          "VENDOR_VALIDATION_FAILED"
        ],
        "description": "Every value `ProblemDetails.code` can take."
      },
      "FieldViolation": {
        "type": "object",
        "required": [
          "pointer",
          "code",
          "detail"
        ],
        "properties": {
          "pointer": {
            "type": "string",
            "description": "JSON Pointer to the field, e.g. `#/inputs/effective_date`."
          },
          "code": {
            "type": "string",
            "description": "Machine-readable reason, e.g. `REQUIRED`."
          },
          "detail": {
            "type": "string",
            "description": "Human-readable explanation."
          }
        },
        "description": "One invalid field in a request."
      },
      "Job": {
        "type": "object",
        "required": [
          "id",
          "job_type",
          "status",
          "create_time"
        ],
        "properties": {
          "id": {
            "$ref": "#/components/schemas/uuid",
            "readOnly": true
          },
          "job_type": {
            "type": "string",
            "description": "What the job does, e.g. `document_extraction`."
          },
          "status": {
            "$ref": "#/components/schemas/JobStatus"
          },
          "create_time": {
            "type": "string",
            "format": "date-time"
          },
          "start_time": {
            "type": "string",
            "format": "date-time"
          },
          "end_time": {
            "type": "string",
            "format": "date-time",
            "description": "When the job reached a terminal status."
          },
          "expire_time": {
            "type": "string",
            "format": "date-time",
            "description": "When the job and its result are deleted."
          },
          "progress": {
            "$ref": "#/components/schemas/JobProgress"
          },
          "result_url": {
            "type": "string",
            "format": "uri",
            "description": "Where to download the result when it is too large to inline."
          },
          "error": {
            "$ref": "#/components/schemas/ProblemDetails",
            "description": "Set when `status` is `failed`."
          },
          "result": {
            "description": "Set when `status` is `succeeded` and the result is small enough to inline."
          }
        },
        "description": "A long-running operation. Poll it until `status` is terminal."
      },
      "JobPage": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Job"
            },
            "description": "The items on this page."
          },
          "next_page_token": {
            "type": "string",
            "description": "Pass as `page_token` to get the next page; omitted on the last page."
          }
        },
        "description": "One page of a list."
      },
      "JobProgress": {
        "type": "object",
        "required": [
          "update_time"
        ],
        "properties": {
          "percent": {
            "type": "integer",
            "format": "int32",
            "minimum": 0,
            "maximum": 100,
            "description": "Estimated completion, 0–100."
          },
          "message": {
            "type": "string",
            "description": "Human-readable description of the current step."
          },
          "update_time": {
            "type": "string",
            "format": "date-time",
            "description": "When this progress was reported."
          }
        },
        "description": "Latest progress reported by a running job."
      },
      "JobStatus": {
        "type": "string",
        "enum": [
          "running",
          "succeeded",
          "failed",
          "cancelled"
        ],
        "description": "Lifecycle state of a job. `running` is the only non-terminal state."
      },
      "Ping": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          }
        },
        "description": "The API is answering."
      },
      "ProblemDetails": {
        "type": "object",
        "required": [
          "type",
          "title",
          "status",
          "code",
          "retryable"
        ],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri",
            "description": "Documentation URL for the code: `https://docs.outmarket.ai/errors/<kebab-case code>`."
          },
          "title": {
            "type": "string",
            "description": "Short summary of the code; the same for every occurrence."
          },
          "status": {
            "type": "integer",
            "format": "int32",
            "description": "The HTTP status code of the response."
          },
          "detail": {
            "type": "string",
            "description": "Explanation specific to this occurrence."
          },
          "instance": {
            "type": "string",
            "description": "URI reference identifying this occurrence."
          },
          "code": {
            "$ref": "#/components/schemas/ErrorCode",
            "description": "Stable machine-readable code; `type` and `title` follow from it."
          },
          "trace_id": {
            "type": "string",
            "description": "Trace ID to quote when reporting the error."
          },
          "retryable": {
            "type": "boolean",
            "description": "Whether the same request may succeed if retried unchanged."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldViolation"
            },
            "description": "Per-field validation failures."
          }
        },
        "description": "An RFC 9457 problem: the body of every error response."
      },
      "ServiceHealth": {
        "type": "object",
        "required": [
          "status",
          "service"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "ok"
            ]
          },
          "service": {
            "type": "string",
            "description": "The service that answered."
          }
        },
        "description": "The service behind the API is answering."
      },
      "uuid": {
        "type": "string",
        "format": "uuid",
        "description": "An RFC 9562 UUID. Responses always use lowercase hex; requests are accepted in any case,\nso there is no pattern."
      }
    },
    "securitySchemes": {
      "ApiToken": {
        "type": "http",
        "scheme": "Bearer",
        "description": "An Outmarket API token, sent as `Authorization: Bearer <token>`. Every operation needs one\nunless it declares another credential (`x-om-auth`); what a token may call is decided by its\ntype and scopes (`x-om-visibility`, `x-om-scope`)."
      }
    }
  },
  "servers": [
    {
      "url": "https://gateway.stage.outmarket.ai",
      "description": "Staging",
      "variables": {}
    }
  ]
}
