playbooks:run. Reading one needs playbooks:read.
Starting one
Exactly one of
documentId and contractText. Both or neither is a 422 naming the two fields.
Text is accepted because you usually have the contract in hand and requiring an upload first would make the cheapest integration a four-call dance. Name a documentId when the file’s structure matters, when you want the source kept and addressable, or when you need tracked changes.
A documentId may be a .docx or a .pdf. That is wider than what a template import accepts, deliberately: the extractor reads text and never claims to preserve shape, where an imported template would.
The source does not have to be in any particular matter. An extraction produces positions for an organization-level playbook, so narrowing it to one matter would refuse your own signed contract for being filed somewhere else.
It answers 202 with an operation of type playbook.extract. Poll it; resource.id is a pex_ identifier.
Extracting from a redline
useTrackedChanges: true reads a .docx’s Word revision marks and extracts what a reviewer pushed from and to, rather than the final-state positions. That is a different and often better playbook: the final text says what you agreed, the redline says what you asked for first.
It needs a documentId for a file whose name ends in .docx, because revision marks exist nowhere else. A PDF is 415, and combining it with contractText is 422.
Reading the result
GET /v1/playbook-extractions/{extractionId}
200 with an empty positions map and a truthful status, never a 404. A 404 would be indistinguishable from an extraction that never existed.
status uses the same five public values an operation does. Why one failed lives on the operation rather than here.
There is no list. An extraction is short-lived work you started, so the operation you were handed is the handle. Adopt what you want into a playbook, which is permanent.
Nothing is saved
The result comes back for you to review. No playbook is created, and nothing is written to your playbook shelf. That is the same gate the web app has, and it is there because an extraction is a model’s reading of a contract. Persisting a misclassified one as the positions your automated reviews measure against is a mistake nobody sees until a review cites it. Adopting is a second call, and it is a copy rather than a translation: the position shape here is exactly the shapePOST /v1/playbooks accepts.
contractType through to the playbook. A playbook’s type decides which reviews resolve it, and it is fixed at creation.
You do not have to keep everything. Drop a clause type you disagree with, edit a standardPosition that reads oddly, add a dealBreaker the contract never stated. What you send is what the playbook holds.
What comes back, and what does not
The extractor also records how confident it was, how many sources agreed, and the minority positions it saw. None of those is published: they describe our process rather than your position, and a number a customer cannot act on is one they would build a threshold against. It can also name a clause type outside the standard set when your contract has one. Those are kept, because a model-named clause is a real finding about your own paper and dropping it would silently shorten the result.Errors worth handling
The concurrency cap counts extractions only. Contract reviews are counted separately and against their own limit, so a busy review queue never blocks an import.
A failed extraction reports why on its operation, not on the extraction. A contract with no recognisable clauses, an unreadable file and a scanned PDF with no text layer all end there with a message you can act on.

