Organization works
With POST /v1/works you send a description of a work (a spec). UpServe creates a new team in your organization (a team lead and one Assigned AI), then specializes that team for the work: it assembles a setup, tests it, and publishes it to the team. You follow this through a preparation until it is ready.
- The result is a team in your organization’s UpServe screens, not a Deployment. This API does not yet hand work to that team.
- Only organization Projects can use these paths. A Project in a personal space gets
404 NOT_FOUNDon every work path. - As with every
/v1path, only approved Projects can call these during the Private Beta. When we approve a Project for works we also enable specialized setups for its organization. If a preparation ends withRECIPE_ACCESS_DENIED, ask us.
Before you start
| scope | Allowed operations |
|---|---|
works:read | Read and list preparations |
works:write | Submit a work or a revised spec, and answer questions |
works:approve | Answer a scope confirmation on behalf of a person in your service (explicit delegation, chosen per key) |
- A key with at least one works scope and nothing else but
usage:readcan be created without any Deployment. A key that mixes in any other scope still needs at least one Deployment. - The team’s manager is the OWNER who created the key. If that person stops being an OWNER, the key stops working.
- Question texts in a preparation follow the manager’s language: Korean for a Korean user, otherwise English.
- Preparation costs are paid like executions and count toward the Project’s daily and monthly spend limits. The team is created with autonomous wake-ups off, so it does not spend on its own.
- With
usage:read,GET /v1/usagereports preparation costs inpreparations, apart from execution costs. See usage.
Flow
- Submit the work with
POST /v1/works. (works:write,Idempotency-Keyrequired) - Read
GET /v1/preparations/{preparation_id}until the status is final. (works:read) - If the status is
NEEDS_INPUT, answer the questions withPOST /v1/preparations/{preparation_id}/answers. - The work is ready when the status is
SUCCEEDEDandteam_idis set.
curl -s -X POST "$BASE/v1/works" \
-H "Authorization: Bearer $UPSERVE_API_KEY" \
-H "Idempotency-Key: work-0001" \
-H "Content-Type: application/json" \
-d @work.json{
"spec": {
"purpose": "Turn each incoming quote request into a quote from the approved catalog.",
"success_criteria": ["Every request line has a source or a question for a person."],
"sources": [{"ref": "synthetic:catalog", "revision": "1"}],
"allowed_actions": ["READ_APPROVED_CATALOG", "CREATE_QUOTE"],
"delivery": {"channel": "EMAIL", "target_ref": "synthetic:approved-operator"}
}
}The response is 202 with a Location: /v1/preparations/{preparation_id} header and the preparation itself.
{
"kind": "PREPARATION_VIEW",
"contract_version": 1,
"preparation_kind": "SPECIALIZE",
"work_id": "22222222-2222-4222-8222-222222222222",
"revision": 1,
"preparation_id": "33333333-3333-4333-8333-333333333333",
"preparation_revision": 2,
"owner": {"kind": "ORGANIZATION", "id": "11111111-1111-4111-8111-111111111111"},
"status": "QUEUED",
"stage": "CREATE",
"support": "SUPPORTED",
"reason_codes": [],
"missing_fields": [],
"questions": [],
"next_actions": [],
"retryable": false,
"spec": {"purpose": "Turn each incoming quote request into a quote from the approved catalog."},
"usage": {"status": "PENDING", "cost_su": null}
}202andQUEUEDmean accepted, not ready. Keep reading the preparation.- There are no webhooks or SSE events for preparations. Poll at a modest interval, for example every 10 to 30 seconds.
- The full response schema is
WorkPreparationPreparationViewin the OpenAPI file.
The request body
| Field | Description |
|---|---|
spec | The work description. Only spec.purpose is required. Other fields (success criteria, inputs and outputs, examples, sources, connections, allowed actions, triggers, delivery, human decisions, facts, assumptions) make the result better. |
work_id, expected_revision | Only to revise a work this Project created. Send both or neither. |
- Unknown fields are rejected with
422 INVALID_INPUT. Do not send an owner, a support profile, or a recipe. The owner is always the key’s organization, and UpServe chooses the setup. connectionsare reference names only. Never put credentials in a spec.- A spec that breaks a rule is
422 INVALID_SPECwith the field or rule indetails.reason.
Preparation status
status | Meaning |
|---|---|
NEEDS_INPUT | Something is missing. Answer questions. |
READY | The spec is complete. It is queued right away. |
QUEUED, RUNNING | Accepted and in progress. stage shows the step: CREATE, ASSEMBLE, VALIDATE, BIND, PUBLISH. |
REQUIRES_ACTION | A person must confirm a wider scope (see below). |
CANCEL_REQUESTED | Closing, for example after a scope confirmation was declined. |
SUCCEEDED | Ready. team_id is the team. |
FAILED | Ended. The reason is in reason_codes. |
CANCELED | Ended without publishing. |
next_actionslists what can happen next at the time you read it. It is not a permission. Every request is checked again.- Organization preparations have no retry or cancel path, so
next_actionsnever containsRETRYorCANCEL. unsupported_actionslists theallowed_actionsUpServe cannot do when the work is not supported.
Answering questions
{
"expected_preparation_revision": 1,
"answers": [{"question_id": "delivery-target", "value": "synthetic:approved-recipient"}]
}expected_preparation_revisionis thepreparation_revisionyou read. If the preparation changed since, the answer is409 REVISION_CONFLICT. Read again and answer the current questions.- Up to 20 answers go in one request. They are applied together or not at all.
- An answer to a question that is not open is
409 QUESTION_NOT_FOUND. An answer to a preparation that no longer takes answers is409 INVALID_TRANSITION. - The response is
200with the updated preparation. When the spec becomes complete it is queued in the same request.
Scope confirmation
A scope confirmation appears only when you revise a work that is already published and the new setup can do more than the current one (new tools, new external actions, or higher cost). The first publication of a new work does not ask. The preparation is then REQUIRES_ACTION with one question Q_SCOPE_EXPANSION that carries changes and package_hash, and next_actions is CONFIRM_SCOPE.
Show the changes to a person in your service and send their decision.
{
"expected_preparation_revision": 7,
"answers": [{"question_id": "Q_SCOPE_EXPANSION", "value": {"decision": "APPLY", "package_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}],
"actor": {"subject": "customer-user-42"}
}- The key needs
works:writeandworks:approve. Withoutworks:approvethe answer is403 ACTION_NOT_RESOLVABLE_BY_CALLER. - Send it as the only answer, with
actor.subject(an opaque id of the person your service confirmed) and thepackage_hashfrom the question. Otherwise it is422 INVALID_INPUT. APPLYresumes the same preparation (QUEUED, stepPUBLISH).KEEPkeeps the current setup and closes the preparation.- If the candidate changed after the person saw it, the answer is
409 PACKAGE_CHANGED. Show the new question again. After the 7 day deadline the answer is409 INVALID_TRANSITION. - UpServe records the decision subject apart from the key. It is not returned in responses.
Listing preparations
GET /v1/preparations returns this Project’s preparations, newest first, as items and next_cursor. (works:read)
limitis 1 to 100 (default 20). Pass the previousnext_cursorascursorfor the next page.work_idnarrows the list to one work.- Preparations of another Project are never listed, even within the same organization.
Revising a work
Send POST /v1/works again with work_id and the expected_revision you read, together with the new spec.
- Only works this Project created can be revised. Any other
work_idis404 NOT_FOUND. - If the work changed since you read it, the answer is
409 REVISION_CONFLICT. - An open preparation that is still
NEEDS_INPUTorREADYis replaced. If one is alreadyQUEUEDor later, the answer is409 PREPARATION_IN_PROGRESSwithdetails.preparation_id. Wait for it to finish.
Idempotency and limits
- Every write needs an
Idempotency-Key. Resending the same key with the same body returns the same response. The same key with a different body is409 IDEMPOTENCY_CONFLICT. - Keys are scoped to the Project. Another Project of the same organization can use the same key value without collision.
- Writes count against the acceptance rate limit, like Turn and Run submissions. Reads count against the read limit. See limits and data.
When a preparation fails
A preparation that ends as FAILED is not retried. Fix the cause, then send the work again with a new Idempotency-Key, or revise it with work_id.
reason_codes | Meaning | What to do |
|---|---|---|
PROJECT_BUDGET_EXCEEDED | The Project’s daily or monthly spend limit was reached. It stops before any team is created. | Send again after the limit window, or ask for a higher limit. |
AUTHORIZATION_REVOKED | The key was revoked, or the person who created it is no longer an organization OWNER. | Send again with a valid key. |
TEAM_CREATE_REFUSED | The team or its Assigned AI could not be created, for example because the organization reached its Assigned AI limit. | Fix it in the organization’s UpServe screens, then send again. |
RECIPE_ACCESS_DENIED | Specialized setups are not enabled for the organization. | Ask us to enable it, then send again. |
A spec UpServe cannot support ends with support UNSUPPORTED, and unsupported_actions shows which actions. HTTP errors on these paths are in errors and retries.