Matches in other apps
uw — the drafting page and the scoreboard
mount /uw · base https://zpe.vc/uw · v2.1.0 · 6 operations · 2 write · 0 need no account · beta
the document this app serves now. Contract: https://zpe.vc/uw/_/api/v1/openapi.json · sha256 9f319e10c028
GET
/_/api/v1/state
The scoreboard, the published snapshot's job list, and one job's two panes and history.
uwState
One read for the page and for a client: the HTML at /uw/p and this answer are built from the same state, so the two cannot disagree about a number. The drafted body is included, because the client's job is to show it. A key reads exactly what the owner's page reads: the answer's `scope` names `reads: "publisher"` and the account whose shelf answered.
Parameters
| name | in | required | type | notes |
|---|---|---|---|---|
job | query | no | string | A job's short ref. Omitted, the newest live proposal answers. |
Request body
None: this operation takes its arguments in the path and the query.
Responses
| status | meaning | body |
|---|---|---|
200 | The read. | object |
401 | No door session and no key: sign in at auth.zpe.vc, or send the fleet key as a bearer token. | object |
403 | A key without the `uw` grant, or a key on a method this surface only writes. | object |
503 | The door did not answer, or nobody has published a snapshot yet. | object |
Example
The scoreboard, the published snapshot's job list, and one job's two panes and history., with a token from the door.
curl -X GET 'https://zpe.vc/uw/_/api/v1/state?job=string' \ -H 'Authorization: Bearer $ZPE_TOKEN'
{
"ok": false,
"schema": "string",
"published_at": "2026-10-31T09:00:00Z",
"snapshot_id": 0,
"window": {},
"scoreboard": {},
"corpus": {},
"jobs": [
{}
],
"job": null,
"agent": null,
"owner": null,
"history": [
{}
],
"against_agent": null,
"scope": {
"account_id": null,
"organization_id": null,
"organization_name": null,
"role": "string",
"via": "session",
"reads": "organisation"
}
}The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.
POST
/_/api/v1/save
Append his version of one job's letter, once per idempotency key.
uwSaveDraft
The only write on this surface. It appends a row and never updates one: a version that can be edited is a version that can be made to agree with whatever happened afterwards. The page's own button posts the same fields as a form and is answered with a 303 back to the pane; a client posting JSON is answered 201 with the row it wrote.
Parameters
| name | in | required | type | notes |
|---|---|---|---|---|
Idempotency-Key | header | no | string | The caller's retry handle. The same key twice replays the first answer and writes nothing. |
Request body application/json · required
| field | type | required | notes |
|---|---|---|---|
job_ref | string | yes | A short ref the snapshot lists. Any other ref is refused. |
body | string | yes | The letter, as plain text. |
note | string | no | What changed and why, in one line. |
idempotency_key | string | no | The same handle as the header, for a client that cannot set one. |
Responses
| status | meaning | body |
|---|---|---|
201 | The version this call appended, and what it changed against the suggestion. | object |
303 | The form's answer: the pane, with the saved version printed under the button. | |
400 | The body names no job this snapshot lists, or carries no letter. | object |
401 | No door session. | object |
403 | A bearer key: a save names the person who wrote it, and this route refuses a key. | object |
Example
Append his version of one job's letter, once per idempotency key., with a token from the door.
curl -X POST 'https://zpe.vc/uw/_/api/v1/save' \
-H 'Authorization: Bearer $ZPE_TOKEN' \
-H 'content-type: application/json' \
-d '{
"job_ref": "string",
"body": "string",
"note": "string",
"idempotency_key": "string"
}'{
"id": "string",
"version": null,
"words": 0,
"at": "2026-10-31T09:00:00Z",
"against_agent": null
}The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.
GET
/_/api/v1/drafts
The drafts this session may read, oldest first.
uwDrafts
The session's own shelf: the organisation the door named and the role it gave decide which rows answer, so a second organisation sees none of the first's. A client reading this sees exactly the versions the page would draw. A KEY reads the publisher's shelf, which is the owner's: the same rows his page draws. `make pull` uses the token-only pull at `/_/publish/drafts`, which is scoped to the deployment's own publisher.
Parameters
| name | in | required | type | notes |
|---|---|---|---|---|
job | query | no | string | One job's short ref. Omitted, every job's drafts answer. |
since | query | no | integer | Only rows with an id greater than this, for a pull that keeps its place. |
Request body
None: this operation takes its arguments in the path and the query.
Responses
| status | meaning | body |
|---|---|---|
200 | The rows. | object |
401 | No door session. | object |
Example
The drafts this session may read, oldest first., with a token from the door.
curl -X GET 'https://zpe.vc/uw/_/api/v1/drafts?job=string&since=0' \ -H 'Authorization: Bearer $ZPE_TOKEN'
{
"ok": false,
"drafts": [
{
"id": 0,
"job_ref": "string",
"body": "string",
"words": 0,
"note": null,
"account_id": "string",
"organization_id": null,
"created_at": "2026-10-31T09:00:00Z"
}
]
}The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.
GET
/_/api/v1/events
This account's record after one event's id, oldest first, one page at a time.
uwEvents
THE RECORD, READ FORWARD. Every state change writes one row to this account's outbox in the same transaction as the change it describes, and this read walks those rows by the event's own id: a keyset, never an offset. `since` names a row rather than a position, so an event written between two pages is in the page it belongs in instead of shifting one out of it, and a reader that follows `next_cursor` sees every event once and none twice. Ordering is by id, which is monotonic, so two events written inside the same second — or the same minute — keep the order they were written in. `next_cursor` is null when this page is the last one the record holds; pass it back as `since` to continue. A `since` that is not an id this account holds is REFUSED, because a cursor silently read as "the beginning" makes a consumer re-read the record and never learn that it had. The record is the account's own: a session reads what it wrote, and a key reads the publisher's shelf. An event carries ids, refs and the numbers that moved, never a letter's body.
Parameters
| name | in | required | type | notes |
|---|---|---|---|---|
limit | query | no | integer | How many events one page carries, 1 to 200. Omitted, 50. |
since | query | no | integer | An event's own id: the answer begins after it. `0` starts at the oldest event this account holds, and is the only value that is not looked up. |
Request body
None: this operation takes its arguments in the path and the query.
Responses
| status | meaning | body |
|---|---|---|
200 | One page of the record, oldest first. | object |
400 | `since` is not an id this account holds, or `limit` is outside 1 to 200, or either is not a whole number. | object |
401 | No door session and no key: sign in at auth.zpe.vc, or send the fleet key as a bearer token. | object |
403 | A key without the `uw` grant. | object |
Example
This account's record after one event's id, oldest first, one page at a time., with a token from the door.
curl -X GET 'https://zpe.vc/uw/_/api/v1/events?limit=50&since=0' \ -H 'Authorization: Bearer $ZPE_TOKEN'
{
"ok": false,
"events": [
{
"id": 0,
"kind": "proposal.changed",
"subject_ref": "string",
"at": "2026-10-31T09:00:00Z",
"payload": null
}
],
"next_cursor": null
}The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.
GET
/_/api/v1/send
One send request's state, for the page and for a client polling it.
uwSendRequestState
The row the page writes is the row the page polls. `state` is `pending`, `claimed`, `done` or `refused`, and it is written only by the runner: a client that reads `done` is reading what the machine reported, never a guess made here. A refused row carries the sentence that refused it (`result.reason`) and no preview. A KEY reads the publisher's rows, which is how the fleet puts the exact letter and Upwork's preview in front of the owner before anything is sent.
Parameters
| name | in | required | type | notes |
|---|---|---|---|---|
id | query | no | integer | One request's id. Omitted, the newest for `job` answers. |
job | query | no | string | A job's short ref: the newest request for it. |
Request body
None: this operation takes its arguments in the path and the query.
Responses
| status | meaning | body |
|---|---|---|
200 | The request, as the queue holds it. | object |
401 | No door session. | object |
404 | This account wrote no request at that address. | object |
Example
One send request's state, for the page and for a client polling it., with a token from the door.
curl -X GET 'https://zpe.vc/uw/_/api/v1/send?id=0&job=string' \ -H 'Authorization: Bearer $ZPE_TOKEN'
{
"ok": false,
"request": {
"id": 0,
"kind": "prepare",
"state": "pending",
"job_ref": "string",
"requested_by": "string",
"at": "2026-10-31T09:00:00Z",
"claimed_at": null,
"run_by": null,
"rate": null,
"body": null,
"terms": null,
"preview": null,
"result": null,
"prepare_id": null,
"wait_seconds": 0
}
}The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.
POST
/_/api/v1/send
Queue a send request: a prepare (Upwork's preview) or a confirm (the submit).
uwSendRequest
THE WORKER CANNOT SEND, AND THAT IS THE DESIGN. The Upwork credential lives on the machine that owns the ledger, so this route records an INTENTION. A `prepare` asks the machine for Upwork's preview — the exact text, the Connects, and any screening questions — and a `confirm` names the prepare row whose preview the person saw. A runner on the machine claims the oldest pending row, calls Upwork, and writes `done` or `refused` back; the state is asynchronous by construction and is answered within about a minute (`wait_seconds` names the wait). Nothing here submits anything. A confirm carries no letter: the text a person authorised is the preview's, and a body posted with a confirm is refused.
Parameters
No parameters.
Request body application/json · required
| field | type | required | notes |
|---|---|---|---|
kind | "prepare" | "confirm" | yes | |
job_ref | string | yes | A short ref the snapshot lists. |
rate | number | no | The bid, in whole USD. |
body | string | no | The letter. Omitted, the newest version saved here is prepared. |
answers | array of object | no | Screening answers, as Upwork takes them. |
estimated_duration | string | no | How long the work will take. One of the snapshot's `send_terms.durations`; required with `payment_mode`. |
payment_mode | string | no | How it is paid. One of the snapshot's `send_terms.payment_modes`; required with `estimated_duration`. |
milestones | array of object | no | For the milestone payment mode: `{description, amount, due_date?}` rows whose amounts add up to the bid. |
prepare_id | integer | no | The prepare row this confirm belongs to. Required for `confirm`. |
room_ref | string | no | The room that asked, when a room asked: the channel writes it to `send_origin` in the same batch as the request, so which room a send came from is a row and not a recollection. Absent on the drafting page, which names no room. |
Responses
| status | meaning | body |
|---|---|---|
201 | Queued. The row's id, its state (`pending`), and the wait the page prints. | object |
303 | The page's own form answer: the pane, with this request's state drawn under it. | |
400 | The ref is not in the published snapshot, the rate is missing, half a fixed-price pair is named, there is no letter, or a confirm carries a body. | object |
401 | No door session. | object |
403 | A bearer key: a send request names the person who asked, and this route refuses a key. Queue a prepare with the owner's own session. | object |
Example
Queue a send request: a prepare (Upwork's preview) or a confirm (the submit)., with a token from the door.
curl -X POST 'https://zpe.vc/uw/_/api/v1/send' \
-H 'Authorization: Bearer $ZPE_TOKEN' \
-H 'content-type: application/json' \
-d '{
"kind": "prepare",
"job_ref": "string",
"rate": 0,
"body": "string",
"answers": [
{}
],
"estimated_duration": "string",
"payment_mode": "string",
"milestones": [
{}
],
"prepare_id": 0,
"room_ref": "string"
}'{
"ok": false,
"id": 0,
"kind": "prepare",
"state": "pending",
"job_ref": "string",
"wait_seconds": 0
}The example is generated from the schema in the contract: the shape is the document's, the values are placeholders.