Kamai Projects / Jobs API

A job is one upload being read. Poll it until it is final.

GitHub OpenAPI

Operations 4

GET /v1/projects/{project_id}/jobs List a project's upload jobs #
GET /v1/projects/{project_id}/jobs/{job_id} Get an upload job's status #
DELETE /v1/projects/{project_id}/jobs/{job_id} Delete an upload job #
POST /v1/projects/{project_id}/jobs/{job_id}/cancel Cancel an upload 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/kamai-io:kamai-io-projects-jobs-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

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

kamai-io-projects-jobs-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Kamai Projects / Jobs API
  description: 'Read construction drawings from your own software. Upload a PDF plan; Kamai reads it and gives you back every room, wall,

    door and window with measurements in metres, every printed word, and every printed table (schedules, legends, revision

    blocks), as JSON. ## Authentication


    Create an API key at Kamai → Settings → API keys and send it on every request:


    ```

    Authorization: Bearer

    ```


    A key acts as the user who created it and sees only that user''s projects. Keep it on your server. A call without a key,

    or with a revoked one, gets `401`. Revoking a key, or a change Kamai makes to its settings, takes effect within 3 minutes. ## Acting for your users


    A partner whose users sign in through it can use any API key to act for those users. The request carries, next to the

    key, `X-Kamai-Partner` (your partner ID, as Kamai gave it to you) and `X-Kamai-Partner-User-Token` (that user''s own token

    from you). Kamai checks the token with you and acts as that user: that user''s projects, that user''s page quota, as if the

    user did it in Kamai. Without the two headers the key acts as its own owner. One header without the other is `400`

    (`partner_id_required`, `partner_user_token_required`). A user who does not exist in Kamai or is disabled gets `403`

    (`act_as_not_allowed`). A key Kamai has tied to one partner always needs both headers and never acts as its owner. What the key may do for a user is set by Kamai on the key:


    | | routes | needs |

    |---|---|---|

    | read | every `GET`, and `POST /v1/blueprints/{blueprint_id}/markups/clip` | the key enabled for partner users |

    | change | create and update projects, upload, update sheets, create and update markups, rename conversations | edit enabled |

    | delete | delete projects, jobs, markups and conversations, cancel jobs | delete enabled, separately from edit |


    A route the key may not use gets `403` (`edit_not_allowed`, `delete_not_allowed`). On every key, not only for partner users, renaming a conversation needs edit enabled and deleting one needs delete enabled; a new key has both off. Replacing a sheet''s features with

    `PUT /v1/blueprints/{blueprint_id}` for a user needs the `revision` you read as `base_revision` (`422`

    `base_revision_required`), and replacing them with none counts as a delete. Conversations work the same way;

    with edit enabled for the key, the assistant may change the user''s drawings and undo that user''s own changes; keys

    without it never write. Uploads count against the user''s own page quota. Kamai keeps the answer of each token check for up to 60 seconds, so a user you disable on your side can still be served

    for that long. ## The usual flow


    1. `POST /v1/blueprints/upload/init` returns a signed URL. 2. `PUT` the PDF to that URL. 3. `POST /v1/blueprints/upload/complete` returns a `job_id`. It starts one job per upload: calling it again with the

    same `file_uuid` returns the same `job_id` (`409` with `Retry-After` while the first call is still running). To send

    another file, start a new upload with `init`. 4. Poll `GET /v1/projects/{project_id}/jobs/{job_id}` every 5–10 s until `status` is final: `SUCCEEDED`, or

    `FAILED` / `CANCELLED` / `ABANDONED`, which end the job; for those, show `error_code` and `error_message`. The PDF check fails a job with `pdf_incomplete` (the file looks cut off or damaged: download or export it again),

    `pdf_filter_unsupported` (Brotli compression, or a PDF that cannot be read) or `pdf_scan_over_budget` (too large or

    complex to check). New `error_code` values can be added over time: treat one you do not know as a failure and show

    its `error_message`. The job record is written when processing starts, not when the upload returns, and when many uploads are queued that

    can take minutes. Until then, a `job_id` you got from an upload answers `404` with `detail` `Job not found`: keep

    polling it. `Project not found` is final. 5. On `SUCCEEDED`, read the sheet with `GET /v1/blueprints/{blueprint_id}`.'
  version: 0.1.0
tags:
- name: Projects / Jobs
  description: A job is one upload being read. Poll it until it is final.
paths:
  /v1/projects/{project_id}/jobs:
    get:
      tags:
      - Projects / Jobs
      summary: List a project's upload jobs
      description: Every upload job of the project, as an object keyed by job id (`{}` when there are none).
      operationId: list_project_jobs_v1_projects__project_id__jobs_get
      security:
      - HTTPBearer: []
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          title: Project Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobListResponse'
              examples:
                real:
                  summary: A project with no upload jobs
                  description: Real response shape from a Kamai test account, 2026-10-13. Names, text and ids are illustrative; numbers and structure are as returned. Jobs are keyed by job id; see Get Project Job for one.
                  value:
                    jobs: {}
                    message: Jobs fetched successfully
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/projects/{project_id}/jobs/{job_id}:
    get:
      tags:
      - Projects / Jobs
      summary: Get an upload job's status
      description: 'The state of one upload job, the one `/upload/complete` returned.


        The job record is written when processing starts, not when the upload returns, and when many uploads are queued that

        can take minutes. Until then, a `job_id` you got from an upload answers `404` with `detail` `Job not found`: keep

        polling it. `Project not found` is final.


        Poll it every 5–10 seconds until `status` is final:


        | status | meaning |

        |---|---|

        | `PENDING` | queued, not started |

        | `RUNNING` | being read; `progress` is 0–100 |

        | `SUCCEEDED` | done; read the sheet with `GET /v1/blueprints/{blueprint_id}` |

        | `FAILED` | could not be read; see `error_code` and `error_message` |

        | `CANCELLED` | stopped by `POST …/cancel` |

        | `ABANDONED` | stopped by the system, for example after a timeout |'
      operationId: get_project_job_v1_projects__project_id__jobs__job_id__get
      security:
      - HTTPBearer: []
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          title: Project Id
      - name: job_id
        in: path
        required: true
        schema:
          type: string
          title: Job Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobResponse'
              examples:
                running:
                  summary: Still reading
                  description: Real response shape from a Kamai test account, 2026-10-13 (one 1-page PDF on the dev lane). Ids are illustrative.
                  value:
                    job:
                      id: cccccccc-0000-4000-8000-000000000001
                      status: RUNNING
                      state: PREPARING_TILES
                      blueprint_id: bbbbbbbb-0000-4000-8000-000000000003
                      filename: A-101 Ground floor.pdf
                      progress: 10
                      error_code: null
                      error_message: null
                    message: Job fetched successfully
                done:
                  summary: Done
                  description: Real response shape from a Kamai test account, 2026-10-13 (one 1-page PDF on the dev lane). Ids are illustrative.
                  value:
                    job:
                      id: cccccccc-0000-4000-8000-000000000001
                      status: SUCCEEDED
                      state: COMPLETED
                      blueprint_id: bbbbbbbb-0000-4000-8000-000000000003
                      filename: A-101 Ground floor.pdf
                      progress: 100
                      error_code: null
                      error_message: null
                    message: Job fetched successfully
        '404':
          description: No such job in this project.
          content:
            application/json:
              examples:
                real:
                  summary: Unknown job id
                  description: Real response shape from a Kamai test account, 2026-10-13. Ids are illustrative.
                  value:
                    detail: Job not found
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
    delete:
      tags:
      - Projects / Jobs
      summary: Delete an upload job
      description: Remove the job record from the project. The sheet it produced stays.
      operationId: delete_project_job_v1_projects__project_id__jobs__job_id__delete
      security:
      - HTTPBearer: []
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          title: Project Id
      - name: job_id
        in: path
        required: true
        schema:
          type: string
          title: Job Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobDeleteResponse'
        '404':
          description: No such job in this project.
          content:
            application/json:
              examples:
                real:
                  summary: Unknown job id
                  description: Real response shape from a Kamai test account, 2026-10-13. Ids are illustrative.
                  value:
                    detail: Job not found
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
  /v1/projects/{project_id}/jobs/{job_id}/cancel:
    post:
      tags:
      - Projects / Jobs
      summary: Cancel an upload job
      description: Stop a job that is still `PENDING` or `RUNNING`. A job that is already final answers `409`.
      operationId: cancel_project_job_v1_projects__project_id__jobs__job_id__cancel_post
      security:
      - HTTPBearer: []
      parameters:
      - name: project_id
        in: path
        required: true
        schema:
          type: string
          title: Project Id
      - name: job_id
        in: path
        required: true
        schema:
          type: string
          title: Job Id
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobCancelResponse'
        '409':
          description: The job is already final (SUCCEEDED, FAILED, CANCELLED or ABANDONED).
          content:
            application/json:
              examples:
                real:
                  summary: Cancelling a finished job
                  description: Real response shape from a Kamai test account, 2026-10-13. Ids are illustrative.
                  value:
                    detail: Job is already SUCCEEDED
        '404':
          description: No such job in this project.
          content:
            application/json:
              examples:
                real:
                  summary: Unknown job id
                  description: Real response shape from a Kamai test account, 2026-10-13. Ids are illustrative.
                  value:
                    detail: Job not found
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
components:
  schemas:
    JobResponse:
      properties:
        job:
          $ref: '#/components/schemas/JobPublic'
        message:
          type: string
          title: Message
          default: Job fetched successfully
      type: object
      required:
      - job
      title: JobResponse
    JobDeleteResponse:
      properties:
        job_id:
          type: string
          title: Job Id
        message:
          type: string
          title: Message
          default: Job deleted successfully
      type: object
      required:
      - job_id
      title: JobDeleteResponse
    JobListResponse:
      properties:
        jobs:
          additionalProperties:
            $ref: '#/components/schemas/JobPublic'
          type: object
          title: Jobs
        message:
          type: string
          title: Message
          default: Jobs fetched successfully
      type: object
      required:
      - jobs
      title: JobListResponse
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    JobPublic:
      properties:
        id:
          type: string
          title: Id
        status:
          type: string
          title: Status
        state:
          anyOf:
          - type: string
          - type: 'null'
          title: State
        blueprint_id:
          type: string
          title: Blueprint Id
        filename:
          type: string
          title: Filename
        progress:
          type: integer
          title: Progress
          default: 0
        error_code:
          anyOf:
          - type: string
          - type: 'null'
          title: Error Code
        error_message:
          anyOf:
          - type: string
          - type: 'null'
          title: Error Message
        page_index:
          anyOf:
          - type: integer
          - type: 'null'
          title: Page Index
          description: The page of the uploaded file this job reads, 0-based. `null` until the page is cut from the upload.
        auto_rotation:
          anyOf:
          - type: integer
          - type: 'null'
          title: Auto Rotation
          description: Degrees clockwise (0, 90, 180, 270) Kamai turned this page at upload to bring it upright; 0 = left as the PDF had it. `null` until the page is cut, or when the check did not run. The sheet carries the same value (`auto_rotation` on GET /v1/blueprints/{id}).
      type: object
      required:
      - id
      - status
      - blueprint_id
      - filename
      title: JobPublic
    JobCancelResponse:
      properties:
        job:
          $ref: '#/components/schemas/JobPublic'
        message:
          type: string
          title: Message
          default: Job cancelled
      type: object
      required:
      - job
      title: JobCancelResponse
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer