---
title: "Upload Org Action Specs"
method: POST
path: "/api/v1/org-actions/spec-uploads"
tags: ["authed"]
---

# Upload Org Action Specs

`POST /api/v1/org-actions/spec-uploads`

## Request

- Base URL: `https://api.teaflask.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.teaflask.com/api/v1/org-actions/spec-uploads`
- Auth: none declared

Upload API spec files — OpenAPI 3 or Swagger 2.0 as JSON or YAML, or
GraphQL operations (.graphql files) with the schema they are checked
against (SDL files or one introspection result) and graphql_endpoint,
the path on your API every operation is sent to — several files that
reference each other, or a zip of them. Each upload adds to the catalog
and is scanned like a repository: proposals land in the review inbox,
never directly in the catalog. An upload never proposes removing an
action; archive the ones you no longer want.

## Request body `multipart/form-data`

- BodyUploadOrgActionSpecs
  - `files` string[], required
  - `graphql_endpoint` string, nullable

## Response `202`

Successful Response

- SpecUploadScanResponse
  - `id` string, uuid, required
  - `organization_id` string, uuid, required
  - `status` 'queued' | 'working' | 'succeeded' | 'failed' | 'unfunded', required
  - `error` string, nullable, required
  - `findings` ScanFindings, required: What a finished scan found: the repositories it cloned or the files uploaded, which specs it read, any API client generator config, and where every operation ended up.
    - `repos` RepoRef[], required
      - `full_name` string, required
      - `commit_sha` string, required
    - `specs` SpecRef[], required
      - `repo` string, required
      - `path` string, required
    - `api_client_config` ApiClientConfigFinding: An API client generator config, with the repository it was found in.
      - `config_path` string, required
      - `spec_path` string, nullable
      - `mutator_path` string, nullable
      - `mutator_name` string, nullable
      - `repo` string, required
      - `mutator_blob_sha` string, nullable
    - `counts` ScanCounts, required: Where every scanned operation ended up. skipped is the honest receipt for anything the scan could not carry — unparseable operations, tripped caps, archived targets, or a change a reviewer already dismissed. A GraphQL client project's skips are counted once, by reason, on its own receipt (ScanFindings.graphql), never again here.
      - `operations` integer
      - `created` integer
      - `updated` integer
      - `refreshed` integer
      - `unchanged` integer
      - `skipped` integer
      - `deprecated` integer
      - `withdrawn` integer
      - `rebaselined` integer
      - `enriched` integer
      - `inferred` integer
      - `generated` integer
    - `formats` FormatFinding[]
      - `repo` string, required
      - `path` string, required
      - `format` 'openapi_3' | 'swagger_2' | 'swagger_1' | 'raml' | 'api_blueprint' | 'wsdl' | 'odata_metadata' | 'asyncapi' | 'graphql_sdl' | 'graphql_operations' | 'protobuf' | 'postman_collection' | 'unrecognized', required
      - `support` 'recognized_unsupported' | 'unrecognized' | 'normalization_failed', required: Why a discovered artifact yielded no operations: a format the scan recognizes but cannot import yet, something spec-named that classified as nothing, or a supported format the normalizer rejected.
      - `detail` string, nullable
    - `preparation` PreparationReceipt: What the catalog-preparer agent leg did on this run — the card's honest answer, separate from the deterministic counts. Also the STUB the prepare activity returns (the receipt-handoff law): a summary only — the recorded work persists in agent session state, read by the filing activity. New fields default so old findings validate.
      - `status` 'succeeded' | 'failed' | 'skipped', required: How the catalog-preparer leg ended. SKIPPED is the push path, whose only agent run is a funded repair of a stored spec generator and whose receipt speaks through the generator lines instead; FAILED is a readable degradation — the deterministic filings still landed, un-enriched.
      - `enrichments` integer
      - `inferences` integer
      - `error` string, nullable
      - `note` string, nullable
      - `candidates_total` integer, nullable
      - `left_as_is` integer, nullable
      - `turns_used` integer, nullable
      - `turn_budget` integer, nullable
      - `generation` GenerationReceipt: The generation leg's summary on the scan card: how it ended, how many repositories got an accepted generator, how many operations their documents carried, and its own turn spend.
        - `status` 'succeeded' | 'failed' | 'skipped', required: How the catalog-preparer leg ended. SKIPPED is the push path, whose only agent run is a funded repair of a stored spec generator and whose receipt speaks through the generator lines instead; FAILED is a readable degradation — the deterministic filings still landed, un-enriched.
        - `accepted_repos` integer
        - `generated_operations` integer
        - `error` string, nullable
        - `note` string, nullable
        - `turns_used` integer, nullable
        - `turn_budget` integer, nullable
    - `unreadable` UnreadableFile[]
      - `repo` string, required
      - `path` string, required
      - `reason` 'missing' | 'oversize' | 'incomplete', required: Why the scan never read a file it found. The domain's own spelling of FileReadStatus's non-OK members — src/actionscan imports nothing from src/integrations, so the reader's enum is MAPPED onto this one rather than leaked through it.
      - `expected_bytes` integer, nullable
      - `received_bytes` integer, nullable
      - `gitignored` boolean
    - `generators` StoredGeneratorReceipt[]
      - `repo` string, required
      - `outcome` 'reran' | 'repaired' | 'repair_unfunded' | 'repair_failed' | 'interrupted' | 'not_run', required: What a push rescan did with one repository's stored spec generator. INTERRUPTED is the workspace's fault, never the script's; NOT_RUN is the one line for every generator past the rescan's limit.
      - `note` string, required
    - `graphql` GraphQLUnitReceipt[]
      - `repo` string, required
      - `manifest_path` string, nullable, required
      - `status` 'ok' | 'timeout' | 'escaped_root' | 'oversized_output' | 'not_a_unit' | 'tool_crash' | 'deferred' | 'units_over_the_cap' | 'files_over_the_cap', required: How discovery ended for a client project, or — on a repo-level receipt — for the repo: the tool's own statuses, plus the three the pass gives what it never read.
      - `clients` GraphQLClientLibrary[]
      - `schema_kind` 'committed-sdl' | 'sdl-in-code' | 'introspection' | 'config-url' | 'generated-sdl' | 'none'
      - `schema_accepted` boolean
      - `schema_repo` string, nullable
      - `endpoint_kind` 'absolute' | 'relative' | 'env' | 'runtime-config' | 'computed' | 'ambiguous' | 'unknown': How the app's client names its endpoint: a literal (absolute or relative), an env variable, runtime config, a computed value — or several disagreeing, or none found.
      - `endpoint_assumed` boolean
      - `filed` integer
      - `skipped` object
      - `collisions` string[]
      - `order` object
      - `read_whole` boolean
      - `capped` GraphQLCap[]
      - `detail` string, nullable
      - `unread_sources` integer
    - `malformed` MalformedSpecFinding[]
      - `repo` string, required
      - `path` string, required
      - `operations` integer, required
      - `withheld` WithheldOperation[]
        - `method` 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', required: The catalog's method whitelist: an action can only ever speak these.
        - `path_template` string, required
        - `detail` string, required
      - `withheld_count` integer
      - `dropped` DroppedKeyword[]
        - `keyword` '$dynamicRef' | '$recursiveRef' | '$recursiveAnchor' | '$schema' | '$vocabulary' | 'additionalItems' | 'dependencies' | 'id', required
        - `schemas` integer, required
    - `upload` UploadFinding: What an upload run read: the set, the spec files that held operations, and its GraphQL read.
      - `file_count` integer, required
      - `total_bytes` integer, required
      - `set_sha256` string, required
      - `spec_roots` string[]
      - `converted_from_swagger_2` boolean
      - `roots_not_read` string[]
      - `roots_not_read_count` integer
      - `roots_limit` integer
      - `graphql` UploadedGraphQLFinding: What an upload's GraphQL read found, for its receipt: the path every operation is sent to, the documents and schema files (the first few named, all counted), what filed and why the rest did not, and whether the set was read whole — every document read, nothing capped, the schema built.
        - `endpoint_path` string, required
        - `documents` integer, required
        - `schema_files` string[], required
        - `schema_file_count` integer, required
        - `schema_read` boolean, required
        - `filed` integer, required
        - `skipped` object
        - `collisions` string[]
        - `unreadable_documents` integer
        - `capped` boolean
        - `read_whole` boolean, required
  - `requested_by_user_id` string, uuid, nullable, required
  - `trigger` 'manual' | 'push' | 'upload', required: What started the scan: manual (a member scanned the connected repositories), push (a push to a default branch) or upload (a member uploaded spec files).
  - `finished_at` string, date-time, nullable, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `replaced_a_waiting_upload` boolean

## Other responses

- `401`: Unauthorized
- `403`: The caller may not manage actions
- `404`: Not found
- `405`: Method not allowed
- `409`: A scan is already running
- `413`: The files are over the size limit
- `422`: No file is an OpenAPI 3 or Swagger 2.0 spec and the files are not GraphQL operations plus their schema, the set can't be stored as uploaded, or GraphQL operations come without the path they are sent to
- `500`: Internal server error
- `503`: The orchestration service is unreachable

## Changes

- **2026-11-03** `c6ac9fd6657f`: 9 info
  - removed the `STEEP_LEARNING_DISABLED` enum value from the `error/code` response property for the response status `401`
  - removed the `STEEP_LEARNING_DISABLED` enum value from the `error/code` response property for the response status `403`
  - removed the `STEEP_LEARNING_DISABLED` enum value from the `error/code` response property for the response status `404`
  - removed the `STEEP_LEARNING_DISABLED` enum value from the `error/code` response property for the response status `405`
  - …5 more

[Operation history](https://skmtc.dev/teaflask/apis/teaflask-api/changes/api/v1/org-actions/spec-uploads/post.md)

---

[API](https://skmtc.dev/teaflask/apis/teaflask-api.md) - [All operations](https://skmtc.dev/teaflask/apis/teaflask-api/llms.txt) - [OpenAPI document](https://skmtc.dev/teaflask/apis/teaflask-api/revisions/c6ac9fd6657f?raw)
