A pass is a named job over the library: “translate every line of dialogue into French”. Workers ask the server for a batch, get lines nobody else holds and nobody has finished, and mark each one done as they write it back. Run one worker or a hundred; every line is handed out once.
1. Ask for a batch
S=http://127.0.0.1:8080
curl -s -X POST $S/documents/-/scenes/-/elements/dialogue/checkout \
-H "Authorization: Bearer $T" \
-d '{"limit": 20, "pass": "translate-fr", "has_lang": "en"}'
- means any document and any scene. has_lang keeps to lines that have
English to translate from. The answer holds up to 20 lines, each checked out
to your token:
{
"checkouts": [
{
"path": "/documents/3f64…/scenes/0fea…/elements/dialogue/a1be…",
"rev": 1,
"node": { "type": "dialogue", "text": { "en": "Say that again." }, "…": "…" },
"pass": "translate-fr",
"expires": "2026-10-01T17:58:43Z"
}
],
"exhausted": false
}
2. Write each line back
Add the French and finish the line for the pass in one request:
curl -s -X PATCH "$S$path?pass=translate-fr&release=true" \
-H "Authorization: Bearer $T" -H "If-Match: \"$rev\"" \
-d '{"text": {"fr": "Redis-le."}}'
PATCH merges, so the English stays and the French is added beside it.
?pass= marks the line done for the pass; release=true lets go of the
checkout.
3. A worker
Put the two together in a loop. This one is Python; translate() is wherever
your translations come from (an LLM, a translation API, a human queue):
import os, requests
S = os.environ.get("S", "http://127.0.0.1:8080")
H = {"Authorization": f"Bearer {os.environ['T']}"}
PASS = "translate-fr"
def translate(text: str) -> str:
... # call your model or service here
while True:
batch = requests.post(
f"{S}/documents/-/scenes/-/elements/dialogue/checkout",
headers=H, json={"limit": 20, "pass": PASS, "has_lang": "en"},
).json()
if batch["exhausted"]:
break
for c in batch["checkouts"]:
fr = translate(c["node"]["text"]["en"])
requests.patch(
f"{S}{c['path']}", params={"pass": PASS, "release": "true"},
headers={**H, "If-Match": f'"{c["rev"]}"'},
json={"text": {"fr": fr}},
).raise_for_status()
Start as many copies as you like, each with its own token. When the server
has nothing left to hand out, exhausted is true and the workers stop.
4. Watch progress
curl -s $S/passes/translate-fr
# {"label":"translate-fr","done":1840,"checked_out":60,…}
The same numbers stream live on the pass:translate-fr channel. A worker
that dies just lets its checkouts expire; those lines go back into the pool.
Variations
- Other element types:
…/elements/action/checkoutfor action lines, or…/elements/checkoutfor every type. - One script: put its ID in place of the first
-. - Filter:
"where": {"/locked": false}skips locked lines. - Agents as workers: an MCP-connected agent can do the same with the
api_requesttool. See Have a team of agents edit a library over MCP.