Software Factory

API reference

Authenticate and call the 8090 Software Factory REST API from your own tools, scripts, and automations.

The 8090 Software Factory REST API lets your own tools and scripts read and write requirements, blueprints, work orders, feedback, Knowledge Base resources, skills, and automations. It can also read your agent conversations.

Base URL

All endpoints are served under a single base URL:

https://api.factory.8090.dev/v2/external-api

Create an API key

Every request needs an External API key for the project you want to work in.

  1. In your project, open Settings → API Keys → External API.
  2. Select Create External API Key and enter a Name.
  3. Under Expiration, choose 90 days (the default), 1 year, or Custom, or turn on Never expires.
  4. Select Create Key, then copy the key from the External API Key Created dialog. The full key is shown only once.

An expired key stops working and requests return 401 with the message API key has expired. Create a new key to replace it.

Authentication

Send the key on every request as a bearer token in the Authorization header:

Authorization: Bearer sofa-ext-...

You can send it in an X-API-Key: sofa-ext-... header instead.

Only External API keys, which start with sofa-ext-, work with this API. Reporting API keys (sf-rpt-) and older keys that start with sf- are rejected with 401.

A key acts as the member who created it and inherits that member's project access. If that member loses access to the project, the key stops working until their access is restored.

Keep your key secret

Treat an API key like a password. Send it only over HTTPS, keep it out of source control, and rotate it if it is ever exposed.

Resources

The reference in the sidebar groups endpoints by resource:

ResourceWhat you can do
RequirementsList, search, read, create, save, rename, move, and delete requirements documents. Read versions and comment threads, add and resolve comments, and propose edits as tracked suggestions.
BlueprintsThe same operations for blueprints, plus listing blueprint categories. Read the code linked to a blueprint, and set the linked line ranges one file at a time.
Work ordersList, filter, sort, search, read by ID or number, create and update in batches, replace a description, and delete work orders. Manage comments, list and create labels, list and create custom statuses, list and create phases, and link or unlink pull requests.
Knowledge BaseList, read, download, create, rename, update, move, and delete Knowledge Base resources, and delete folders. For documents, read versions and comment threads, and add and resolve comments.
Feedback itemsList, filter, sort, read, create, update, and delete feedback. Read the text of an attachment, read and add comments, manage labels, and link feedback to work orders.
Feedback themesList, filter, sort, read, create, update, and delete themes. Read and add comments, manage labels, link feedback items, work orders, and a feature requirements document, and set the assignee.
SkillsList, read, create, save, rename, move, and delete skills. Read versions and comment threads, add and resolve comments, and propose edits as tracked suggestions.
UsersList project members, optionally filtered by name or email.
AutomationsList, read, create, update, and delete automations, and list the integrations you can grant to one. The API does not run automations.
Agent conversationsList and read the agent conversations that belong to the key's creator.
HealthCheck that the API is reachable with your key.

Some resources have limits:

  • Skills: address a skill by its document ID or its stable identifier. Built-in skills are read-only. Writing-rules skills can't be moved or deleted. A skill that belongs to a module that is turned off for the project, such as Knowledge Base, returns 404. Creating a skill in that module's category returns 400.
  • Blueprint code links: setting code links replaces every link for that one file, and the file must already be indexed. Send at most 100 line ranges. A bare file path uses the most recently connected repository. To pick a repository, start the path with owner/repo/. If the key's creator can't access the repository, the request returns 404 and nothing is saved. If the API can't check repository access because GitHub or GitLab is unavailable, the request returns 502 and you can retry it.
  • Comment threads: you can't resolve a thread that is attached to a suggested change.
  • Automations: only the member who created an automation can delete it. Built-in automations can't be deleted.
  • Feedback attachments: the API returns the content of UTF-8 text attachments only. Other attachments return 422.
  • Label filters: the work order list's label filter matches titles exactly, and an unknown title returns no work orders. The feedback item and theme lists use labels, which matches titles in any case and rejects an unknown title with 400.
  • Person filters: each list filters by people in its own way.
    • The work order list's assignee takes one name or email.
    • The theme list takes assignee_name, a partial match on name or email, and assignee_id, which you can repeat to match any of several user IDs.
  • Feature filters: the theme list's feature_requirements_document_id takes one document ID. The work order list uses feature, which takes one ID.

Your application can also submit feedback through the Feedback Integration API, which has its own endpoint and key. See Feedback.

A work order's status is always one of the seven built-in statuses. If a work order has a custom status, the API reports the built-in status that represents the custom status's category, and the status filter matches it the same way. For example, a custom status in the Started category reads as in_progress.

Request limits

The newer endpoints limit request size. A request over a limit returns a 422 validation error that names the field.

  • Repeated list filters: you can repeat a filter, such as priority, type, or labels, at most 25 times per request.
  • Work orders: the work order list's search is at most 500 characters. A pull request url is at most 2,048 characters, and repository at most 200.
  • Feedback: a new feedback item's content_markdown is at most 20,000 characters, and end_user_email at most 320.
  • Skills: a skill title is at most 200 characters.
  • Blueprint code links: file_path is at most 1,024 characters, file_note at most 10,000, and you can send at most 100 line ranges.

Pagination

These list endpoints are page-based: requirements, blueprints, work orders, Knowledge Base resources, feedback items, skills, and users. Pass the page (starting at 1) and page_size query parameters. page_size defaults to 20 and can be at most 100. Each response returns the page as items, along with total, page, page_size, and total_pages.

Some collections work differently, and each endpoint page shows which applies:

  • Work order search takes page and page_size in the request body, with the same default and maximum. Its response contains only items, so keep requesting the next page until one comes back with fewer than page_size items.
  • Agent conversations are cursor-based. page_size defaults to 20 and can be at most 100. Each response includes next_cursor, which is null at the end of the collection. To fetch the next page, pass it back unchanged as the cursor query parameter, with the same filters.
  • Everything else returns the whole list in one response, without page metadata. This covers requirements and blueprint search, version history, comments, labels, blueprint categories, blueprint code links, work order statuses, phases, feedback themes, automations, and integrations. Version lists also include a total_count.

Errors

A 4xx status means the request needs to change before you retry it. A 5xx status means something went wrong on our side, and you can retry the request.

Most errors return a JSON body like this:

{
  "detail": {
    "error_code": "not_found",
    "message": "A human-readable description of what went wrong.",
    "details": null
  },
  "exception_class_name": "SofaExternalApiNotFoundException"
}

error_code is a stable, machine-readable string: bad_request, unauthorized, forbidden, not_found, conflict, unprocessable_entity, internal_server_error, or bad_gateway (a service the request depends on is unavailable). Handle errors in code by error_code, not by message or exception_class_name. details is an optional object with extra context, or null when there is none.

If the key's creator doesn't have permission for an action, the API can instead return a 403 authorization denial:

{
  "detail": "Authorization denied",
  "reason_code": "denied_missing_relation",
  "exception_class_name": "AuthorizationDeniedError",
  "authz_denial": {
    "attempt_id": "0b9f2d4e-3c1a-4f7e-9a51-6d2c8e7b1f30",
    "occurred_at": "2026-09-29T15:04:05Z",
    "reason_code": "denied_missing_relation",
    "action": "work_order.update",
    "resource_type": "work_order"
  }
}

An unexpected server error returns 500 with an error_id. Include it when you contact support so we can trace the request:

{
  "detail": "An internal error occurred",
  "error_id": "4bf92f3577b34da6a3ce929d0e0e4736"
}

Request validation errors use a different shape: a detail array where each entry names the field that failed and why.

{
  "detail": [
    {
      "loc": ["body", "title"],
      "msg": "Field required",
      "type": "missing"
    }
  ]
}

Explore the endpoints

Browse the reference by resource in the sidebar. Each page documents a single endpoint with its parameters, an example request in the language of your choice, and an example response.

On this page