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-apiCreate an API key
Every request needs an External API key for the project you want to work in.
- In your project, open Settings → API Keys → External API.
- Select Create External API Key and enter a Name.
- Under Expiration, choose 90 days (the default), 1 year, or Custom, or turn on Never expires.
- 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:
| Resource | What you can do |
|---|---|
| Requirements | List, 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. |
| Blueprints | The 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 orders | List, 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 Base | List, 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 items | List, 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 themes | List, 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. |
| Skills | List, read, create, save, rename, move, and delete skills. Read versions and comment threads, add and resolve comments, and propose edits as tracked suggestions. |
| Users | List project members, optionally filtered by name or email. |
| Automations | List, read, create, update, and delete automations, and list the integrations you can grant to one. The API does not run automations. |
| Agent conversations | List and read the agent conversations that belong to the key's creator. |
| Health | Check 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 returns400. - 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 returns404and nothing is saved. If the API can't check repository access because GitHub or GitLab is unavailable, the request returns502and 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
labelfilter matches titles exactly, and an unknown title returns no work orders. The feedback item and theme lists uselabels, which matches titles in any case and rejects an unknown title with400. - Person filters: each list filters by people in its own way.
- The work order list's
assigneetakes one name or email. - The theme list takes
assignee_name, a partial match on name or email, andassignee_id, which you can repeat to match any of several user IDs.
- The work order list's
- Feature filters: the theme list's
feature_requirements_document_idtakes one document ID. The work order list usesfeature, 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, orlabels, at most 25 times per request. - Work orders: the work order list's
searchis at most 500 characters. A pull requesturlis at most 2,048 characters, andrepositoryat most 200. - Feedback: a new feedback item's
content_markdownis at most 20,000 characters, andend_user_emailat most 320. - Skills: a skill title is at most 200 characters.
- Blueprint code links:
file_pathis at most 1,024 characters,file_noteat 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
pageandpage_sizein the request body, with the same default and maximum. Its response contains onlyitems, so keep requesting the next page until one comes back with fewer thanpage_sizeitems. - Agent conversations are cursor-based.
page_sizedefaults to 20 and can be at most 100. Each response includesnext_cursor, which isnullat the end of the collection. To fetch the next page, pass it back unchanged as thecursorquery 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.