Python API

Quick Start

from plaid_client import PlaidClient

client = PlaidClient.login("http://localhost:8080", "user@example.com", "password")

# Create a project
project = client.projects.create("My Project")

# Create a document
doc = client.documents.create(project["id"], "Document 1")

# List a project's documents
docs = client.projects.list_documents(project["id"])

# Batch multiple operations atomically
with client.batched() as b:
    b.tokens.create(token_layer_id, text_id, 0, 5)
    b.tokens.create(token_layer_id, text_id, 6, 11)
results = b.results

Client

check_constraints(layer_id, constraints)

The violations the given constraints would meet in this layer's stored data, as {'violations', 'violation_count'}. Writes nothing.

ParameterTypeDescription
layer_id Any The layer ID
constraints Any The list to check
id() @property
set_message([message])
ParameterTypeDescription
message optional Any
server_now()

The server's time now (UTC), as its last response's Date header put it (to the second), else this machine's. Judge a time the server stamped, such as an audit entry's ts, against this rather than the machine's own clock, which can be minutes off.

query(body)

Run a query over every project you can read.

body is the query AST. Its keys follow the usual client convention (snake_case, e.g. scope['project_ids']) and are converted to the wire format automatically; clause heads and variables are plain strings you write literally (e.g. 'span', '?s1', 'vocab-link').

Example:

client.query({
    'find': ['?s1', '?s2'],
    'where': [
        ['span', '?s1', {'layer': pos_layer_id, 'value': 'NOUN'}],
        ['span', '?s2', {'layer': pos_layer_id, 'value': 'VERB'}],
        ['covers', '?s1', '?t1'], ['covers', '?s2', '?t2'],
        ['precedes', '?t1', '?t2'],
    ],
    'return': 'entities',   # 'ids' (default) | 'entities' | 'count'
    'limit': 100,
})

A layer is referenced by its id (its UUID) only — not by name or path. To match a layer by name, bind it with a *-layer clause (e.g. ['span-layer', '?sl', {'name': 'pos'}]) and use the variable.

Optional keys: scope (restrict to projects by id, {'project_ids': [...]}), order_by (sort rows), and bindings (substitute ?name placeholders with literals). return may also be an aggregate spec {group, aggregates}. See the query language reference.

ParameterTypeDescription
body Any The query AST ({find, where, scope?, limit?, order_by?, return?, bindings?}).
enter_strict_mode(document_id)

Enter strict mode for a specific document.

Enables strict mode, requiring document version headers on writes.

ParameterTypeDescription
document_id Any The ID of the document to track versions for
exit_strict_mode()

Exit strict mode and stop tracking document versions for writes.

key_seed()

A seed for the Idempotency-Keys of a logical operation that may be run again from the top (see begin_operation's keys). Keep it with the work it belongs to and pass it to every run.

end_operation([message])

End the current logical operation. With no argument this is purely local (no request). Pass a refined message to relabel the group now that the outcome is known (end_operation('Merged 3 morphemes')) — that sends one PATCH, skipped if the operation never wrote anything. A refine from a nested (flattened) end_operation is ignored; the outer label wins.

ParameterTypeDescription
message optional Any Optional. A refined label for the finished operation.
batch()

Open a batch: a view of this client with the same resources, on which every write of project data queues instead of going out. submit() sends the queued operations as ONE atomic request (larger than the server's cap, as consecutive requests with the results concatenated in queue order, each atomic on its own, so a failure in a later one leaves the earlier ones committed, and the error it raises carries committed, the count saved, and committed_results, their results) and returns one result per operation; abort() drops them. A call made on the client itself is never touched by an open batch, and a read or an out-of-band signal made on the batch goes over the wire now (see the note at the top of http.py):

b = client.batch()
b.tokens.bulk_create(sentence_ops)
b.tokens.bulk_create(word_ops)
sentence_results, word_results = b.submit()

Server-side a batch runs sequentially in one transaction: a child op sees parents created earlier in the same batch, and any op's failure rolls the whole batch back. A batch is not nestable. Prefer :meth:batched, which submits or aborts for you.

claim(doc_id, [pin])
ParameterTypeDescription
doc_id Any
pin optional Any
attempt([data], [request_headers])
ParameterTypeDescription
data optional Any
request_headers optional Any
batched() @contextmanager

Run the block with a batch, then submit all queued ops as :meth:PlaidBatch.submit does (ONE atomic request up to MAX_BATCH_OPS operations, consecutive requests past it), or abort the batch if the block raises. The block makes its writes on the yielded batch; the results land on its .results (a context manager can't return a value):

with client.batched() as b:
    b.tokens.bulk_create(sentence_ops)
    b.tokens.bulk_create(word_ops)
sentence_results, word_results = b.results

An empty block submits nothing and leaves .results == []. A write made on client inside the block is not part of the batch: it goes over the wire at once, as it would anywhere else. See :meth:batch.

close()

Close the underlying HTTP session.

invite_url(app_url, code) @staticmethod

Build the link to hand someone for an invite code.

The server never sees an app URL, so the app that minted the invite is the one that names it. Both SPAs use hash routing, so the code rides in the fragment — which also keeps it out of server access logs.

ParameterTypeDescription
app_url Any Where the SPA lives, e.g. "https://plaid.example.org/igt/"
code Any The code returned by invites.create()
health() @classmethod

Liveness, version and database size, with NO authentication and no client instance: for a launcher or status page checking whether a server is up before anyone logs in. client.server.health() is the same read from a client.

info() @classmethod

The version and the limits this server enforces, with NO authentication and no client instance. client.server.info() is the same read from a client, cached.

batch()
batched()

Send the queued operations and return one result per operation, in order (also kept on .results). Up to MAX_BATCH_OPS operations go as one atomic request. A larger batch goes as consecutive requests, each atomic on its own, so a failure in a later one leaves the earlier ones committed. The error then carries committed, how many operations were saved (0 when none were), and committed_results, their results in queue order.

submit()

Send the queued operations and return one result per operation, in order (also kept on .results). Up to MAX_BATCH_OPS operations go as one atomic request. A larger batch goes as consecutive requests, each atomic on its own, so a failure in a later one leaves the earlier ones committed. The error then carries committed, how many operations were saved (0 when none were), and committed_results, their results in queue order.

abort()

Drop the queued operations without sending them.

ref()

A stand-in for the id a queued operation will create, to put in a later operation's body on this batch, so a create and the write that uses it go in one transaction: op n's id, or with index the k-th of the ids a bulk create answers. op_index counts from 0, or from the end when negative (-1, the default, is the op queued last), and is fixed when ref is called. It goes only in the body of a later write on this batch, at any depth. Anywhere else (a path, another batch, a call made on the client) the client refuses it:

with client.batched() as b:
    b.vocab_items.create(vocab_id, 'dog')
    b.vocab_links.create(b.ref(), [token_id])

Admin

admin.server()

Everything about the server in one read.

Version and JVM uptime, database size and per-table row counts, media directory usage, backup configuration and the backups on disk, and the settings an operator gets asked to confirm. Carries no secrets. Runs a count per table and walks the media directory, so call it when someone asks, not on a timer.

admin.backup()

Take a database backup right now, outside the nightly schedule.

Returns the backup block, with ok reporting whether the snapshot succeeded. Uses VACUUM INTO, which only reads, so it is safe while people are working.

out_of_band, as every admin action on the server itself is: none of them writes project data (see the note at the top of http.py).

admin.locks()

Documents currently held by an editing lock, with who holds each and when it expires on its own.

admin.release_lock(document_id)

Drop the lock on a document whoever holds it. Idempotent.

For a client that went away without releasing one, which otherwise leaves the document unwritable until the lock expires.

ParameterTypeDescription
document_id Any The document to unlock
admin.rate_limits()

Live login and invite rate-limit buckets: the address, the account where there is one, failures inside the window, the limit, and whether it is currently blocking.

admin.log_file(lines)

The tail of the configured log file, as text lines.

The only place third-party library messages (connection pool, SQLite driver, HTTP server) and anything from before the last restart can be read. Returns an error string instead of lines when no log file is configured or it does not exist yet. The server also logs to stdout, where a file is not required.

ParameterTypeDescription
lines Any How many lines (default 200, max 2000)

Api Tokens

api_tokens.list(user_id)

List a user's named API tokens.

Never includes the signed token string itself — that is only returned once, by create(). Transparently follows server-side pagination cursors and returns the full flat list.

ParameterTypeDescription
user_id Any The user ID who owns the tokens
api_tokens.iter_pages(user_id, page_size)

Iterate over pages of a user's API tokens, yielding each page's entries.

ParameterTypeDescription
user_id Any The user ID who owns the tokens
page_size Any Page size (1..1000)
api_tokens.create(user_id, name)

Mint a named API token for a user.

The returned token is the signed credential and is shown ONLY here — store it immediately. API tokens do not expire and survive password changes / logout; revoke to kill. Returns a dict with id, name and token.

ParameterTypeDescription
user_id Any The user ID who will own the token
name Any A human label, e.g. "Stanza parser"
api_tokens.revoke(user_id, token_id)

Revoke a named API token (soft-revoke; idempotent).

ParameterTypeDescription
user_id Any The user ID who owns the token
token_id Any The token ID to revoke

Auth

auth.logout_everywhere()

End every sign-in of this user on every device: each browser tab, script and service holding one of the user's sign-in tokens is refused from now on, this client included. Named API tokens are not affected (revoke one with api_tokens.revoke). To sign out of one tab only, discard its token instead. A signal rather than project data: made on a batch it still goes out at once, and it never joins an operation.

Comments

comments.get()

Read one comment.

comments.update()

Edit a comment's body.

Only the comment's author may do this — not maintainers, not admins. Sets edited on the returned comment.

comments.delete()

Delete a comment (author, or a maintainer of its project).

comments.list_in_vocab(vocab_id, entity_id)

List the comments on a vocabulary's entries, oldest first.

Requires read access to the vocabulary. Transparently follows server-side pagination cursors and returns the full flat list.

ParameterTypeDescription
vocab_id Any The vocab layer to read
entity_id Any Only this entry's thread
comments.counts_in_vocab()

Comment counts per entry of a vocabulary, as an {entry_id: n} dict.

The response is NOT key-transformed: its keys are entry ids.

Documents

documents.check_lock(document_id)

Get information about a document lock.

ParameterTypeDescription
document_id Any The document ID
documents.acquire_lock(document_id, [new_lock_id])

Acquire a document lock as a new holder.

The answer's lock_id names the holder: :meth:renew_lock and :meth:release_lock take it. While the lock is held, a second acquire is refused with HTTP 423, whoever makes it, this user included.

new_lock_id names the holder from the client's side (a fresh UUID). An acquire whose answer never arrived can then be sent again, which answers 200 while that holder has the lock, or released. Without it the server mints the id, and a lost answer leaves a lock nobody can release until it expires.

out_of_band: the lock is a signal, not project data (see the note at the top of http.py). Queued on a batch it would be taken only at submit, after every write it was meant to guard, and until then answer success to a caller that does not hold it and cannot see the 423 saying somebody else does.

ParameterTypeDescription
document_id Any The document ID
new_lock_id optional Any Optional holder id this client minted
documents.renew_lock(document_id, lock_id)

Renew the lock lock_id holds while it is live. HTTP 423 if another holder has it, and also once it has expired or been dropped, even when nobody holds the document now: a renewal never takes a free document.

out_of_band, for the same reason as :meth:acquire_lock.

ParameterTypeDescription
document_id Any The document ID
lock_id Any The lock_id :meth:acquire_lock answered with
documents.release_lock(document_id, lock_id)

Release the lock lock_id holds. Idempotent: a lock that holder no longer has is left alone.

out_of_band, for the same reason as :meth:acquire_lock: queued, the lock would be held until the batch submits, and not released at all if it aborts.

ParameterTypeDescription
document_id Any The document ID
lock_id Any The lock_id :meth:acquire_lock answered with
documents.locked(document_id, keep_alive) @contextmanager

Hold this document's server-enforced lock for a with block, releasing it on exit (including on error).

Wrap any multi-step, server-side mutation of a document that must not interleave with a human editor or another service — e.g. a parser or tokenizer that deletes and recreates a document's tokens/spans/relations (a single atomic call doesn't need this). While the lock is held, writes to the document by ANOTHER user are rejected by the server with HTTP 423; the holder's own writes pass and refresh the lock. If anyone already holds it, another block of this same user included, this raises :class:PlaidAPIError (status == 423, the same Locked code the server returns when rejecting another user's write) with a readable message and the block does NOT run:

with client.documents.locked(doc_id):
    ...delete + recreate tokens...

The block is renewed for as long as it runs, so work that computes for minutes before it writes holds the lock the whole time rather than only for its first minute. If a renewal fails the lock is gone: every later write from this client raises :class:DocumentLockLost, and a block that got to the end anyway ends with that error rather than reporting success. The block may also read lock.lost to give up sooner:

with client.documents.locked(doc_id) as lock:
    for sentence in sentences:
        lock.raise_if_lost()
        ...
ParameterTypeDescription
document_id Any The document to hold.
keep_alive Any Renew the lock on a timer while the block runs (default). Pass False for a block that writes as it goes and wants no background thread.
documents.get_media(document_id)

Get the media file for a document.

Media is not versioned, so there is no as-of form: the route refuses the parameter. Prefer the document's own media URL, which carries the file's version for caching.

ParameterTypeDescription
document_id Any The document ID
documents.delete_media(document_id)

Delete media file for a document.

ParameterTypeDescription
document_id Any The document ID
documents.delete(document_id)

Delete a document and all data contained.

ParameterTypeDescription
document_id Any The document ID
documents.update(document_id, name)

Update a document's name.

ParameterTypeDescription
document_id Any The document ID
name Any The name
documents.set_metadata(document_id, body)

Replace all metadata for a document.

ParameterTypeDescription
document_id Any The document ID
body Any The request body
documents.delete_metadata(document_id)

Remove all metadata from a document.

ParameterTypeDescription
document_id Any The document ID
documents.patch_metadata(document_id, body)

Edit metadata for a document with a list of ops applied in order.

{"op": "set", "path": [...], "value": v} writes v at the path, creating missing objects along it; {"op": "delete", "path": [...]} removes the key at the path (a no-op when absent). A path is a non-empty list of keys, the first a top-level key. A path through a non-object is refused (400). See :func:metadata_ops and :func:apply_metadata_ops.

ParameterTypeDescription
document_id Any The document ID
body Any The metadata ops

Events

events.create(project_id, [events])

Record events in a project. Requires write access.

The server stamps the user and its own time. All or nothing: one bad event refuses the request with a 400 naming it.

ParameterTypeDescription
project_id Any The project the events happened in
events optional Any At most 500 dicts, each with type and optional document_id, target_id, data (an object) and client_ts (an ISO-8601 instant)

Guidelines

guidelines.get()

Read one guideline, Markdown body included.

guidelines.delete()

Delete a guideline.

guidelines.list(project_id, include_bodies)

List a project's guidelines, by title.

Transparently follows server-side pagination cursors and returns the full flat list.

Without include_bodies each entry carries body_chars, the length of its body, so a caller can budget before fetching any. Pinned guidelines are NOT sorted first: pinned is on every entry and grouping is the caller's.

ParameterTypeDescription
project_id Any The project to read
include_bodies Any Return each body instead of its length

Invites

invites.list(project_id, all)

List invites you minted, oldest first.

With project_id, lists that project's invites instead (including ones minted by co-maintainers), which requires maintainer or admin on that project. With all, lists every invite on the server, which requires admin. Never includes invite codes — a code is returned once, by create(), and is not recoverable afterward. Transparently follows server-side pagination cursors and returns the full flat list.

ParameterTypeDescription
project_id Any List this project's invites rather than your own
all Any List every invite on the server (admin only)
invites.revoke(invite_id)

Revoke an invite, killing the link immediately.

Idempotent. Allowed for the invite's creator, an admin, or a maintainer of the project the invite grants access to.

ParameterTypeDescription
invite_id Any The invite ID

Messages

messages.listen(project_id, on_event, path)

Open a Server-Sent Events stream for a project.

ParameterTypeDescription
project_id Any The UUID of the project to listen to
on_event Any Callback function that receives (event_type, data). If it returns true, listening will stop.
path Any Stream path under the base URL. Defaults to the project /listen bus (audit-log + broadcast messages); service request channels pass their own path.
messages.send_message(project_id, data)

Send a message to project listeners.

data may be any JSON value and is sent VERBATIM (raw_body): a message payload is opaque application data, like metadata and config, so its keys must not be re-cased on the way out. Without this a key such as case-marker would reach listeners as case_marker in Python and caseMarker in JavaScript. :meth:listen restores it verbatim on the way in.

A message is not saved and writes nothing to History, so it never joins an open operation (:meth:PlaidClient.operation). Made on a batch it still queues, so a message queued after the writes goes out after them.

ParameterTypeDescription
project_id Any The UUID of the project to send to
data Any The message data to send
messages.discover_services(project_id)

Discover the services seen on a project.

Reads the server-side service registry synchronously. Returns every service ever registered on the project: currently connected ones carry online: True; previously-seen offline ones carry online: False plus a last_seen_at stamp. Goes over the wire even while a batch is open on the client.

ParameterTypeDescription
project_id Any The UUID of the project to query
messages.discard_service(project_id, service_id)

Forget a previously-seen (offline) service.

Removes the service's row from the project's persistent registry. Maintainer-only; 409 if the service is currently connected.

ParameterTypeDescription
project_id Any The UUID of the project
service_id Any The ID of the service to forget
messages.cancel_service_request(project_id, request_id)

Ask the service to stop a request made earlier. The request still ends with whatever the service then reports, on the stream of whoever is awaiting it. 404 if unknown or expired, 409 once finished.

ParameterTypeDescription
project_id Any The UUID of the project
request_id Any The request id

Operation Groups

operation_groups.get(id)

Get a logical-operation group (its label + creator).

ParameterTypeDescription
id Any The group id
operation_groups.update(id, message)

Relabel a logical-operation group after the fact. Owner or admin only.

ParameterTypeDescription
id Any The group id
message Any The new label

Projects

projects.create(name, [id])

Create a new project.

Also registers the current user as a maintainer.

ParameterTypeDescription
name Any The name
id optional Any Optional. The id to create it under, a UUIDv7 this client minted (plaid_client.uuid7()), so a create sent again after its answer was lost lands once (409 with id_taken when the id was used before).
projects.list()

List all projects accessible to the current user.

Transparently follows server-side pagination cursors and returns the full flat list.

projects.list_page(limit, cursor)

List one page of projects.

ParameterTypeDescription
limit Any Page size (1..1000)
cursor Any Opaque cursor from a previous page's next_cursor
projects.iter_pages(page_size)

Iterate over pages of projects, yielding each page's entries list.

ParameterTypeDescription
page_size Any Page size (1..1000)
projects.list_documents(id)

List all documents (IDs and names) in a project.

Transparently follows server-side pagination cursors and returns the full flat list. Replaces the removed include_documents param on get.

Note: this endpoint does not support temporal (as-of) queries; the server rejects ?as-of= on the documents-list route with a 400.

ParameterTypeDescription
id Any The project ID
projects.iter_documents(id, page_size)

Iterate over pages of a project's documents, yielding each page's entries.

Note: this endpoint does not support temporal (as-of) queries; the server rejects ?as-of= on the documents-list route with a 400.

ParameterTypeDescription
id Any The project ID
page_size Any Page size (1..1000)
projects.get(id)

Get a project by ID.

To fetch the project's document IDs and names, use list_documents (the former include_documents param has been removed server-side).

ParameterTypeDescription
id Any The resource ID
projects.delete(id, audit_message, timeout)

Delete a project and everything in it. This is irrecoverable. The project is gone when this returns, and what it holds is removed on the server afterwards.

ParameterTypeDescription
id Any The resource ID
audit_message Any Custom audit-log message.
timeout Any Per-request timeout in seconds, the client's own by default. None disables it.
projects.update(id, name)

Update a project's name.

ParameterTypeDescription
id Any The resource ID
name Any The name
projects.add_writer(id, user_id)

Set a user's access level to read and write for this project.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
projects.remove_writer(id, user_id)

Remove a user's writer privileges for this project.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
projects.add_reader(id, user_id)

Set a user's access level to read-only for this project.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
projects.remove_reader(id, user_id)

Remove a user's reader privileges for this project.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
projects.add_maintainer(id, user_id)

Assign a user as a maintainer for this project.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
projects.remove_maintainer(id, user_id)

Remove a user's maintainer privileges for this project.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
projects.my_last_edits(project_id)

When you last wrote to each document in a project, as a {document_id: timestamp} dict.

Documents you have never written to are absent. One request covers a whole document list.

The response is NOT key-transformed: its keys are document ids, and recasing would mangle the hyphens in a UUID.

ParameterTypeDescription
project_id Any The project ID

Relation Layers

relation_layers.get(relation_layer_id)

Get a relation layer by ID.

ParameterTypeDescription
relation_layer_id Any The relation layer ID
relation_layers.delete(relation_layer_id)

Delete a relation layer.

ParameterTypeDescription
relation_layer_id Any The relation layer ID
relation_layers.update(relation_layer_id, name)

Update a relation layer's name.

ParameterTypeDescription
relation_layer_id Any The relation layer ID
name Any The name
relation_layers.shift(relation_layer_id, direction)

Shift a relation layer's display order.

ParameterTypeDescription
relation_layer_id Any The relation layer ID
direction Any The direction ("up" or "down")

Relations

relations.set_metadata(relation_id, body)

Replace all metadata for a relation.

ParameterTypeDescription
relation_id Any The relation ID
body Any The request body
relations.delete_metadata(relation_id)

Remove all metadata from a relation.

ParameterTypeDescription
relation_id Any The relation ID
relations.patch_metadata(relation_id, body)

Edit metadata for a relation with a list of ops applied in order.

{"op": "set", "path": [...], "value": v} writes v at the path, creating missing objects along it; {"op": "delete", "path": [...]} removes the key at the path (a no-op when absent). A path is a non-empty list of keys, the first a top-level key. A path through a non-object is refused (400). See :func:metadata_ops and :func:apply_metadata_ops.

ParameterTypeDescription
relation_id Any The relation ID
body Any The metadata ops
relations.set_target(relation_id, span_id)

Update the target span of a relation.

ParameterTypeDescription
relation_id Any The relation ID
span_id Any The span ID
relations.set_source(relation_id, span_id)

Update the source span of a relation.

ParameterTypeDescription
relation_id Any The relation ID
span_id Any The span ID
relations.get(relation_id)

Get a relation by ID.

ParameterTypeDescription
relation_id Any The relation ID
relations.delete(relation_id)

Delete a relation.

ParameterTypeDescription
relation_id Any The relation ID
relations.update(relation_id, value)

Update a relation's value.

ParameterTypeDescription
relation_id Any The relation ID
value Any The value
relations.bulk_create(body)

Create multiple relations in a single operation.

ParameterTypeDescription
body Any The request body
relations.bulk_delete(body)

Delete multiple relations in a single operation. Provide a list of IDs.

ParameterTypeDescription
body Any The request body
relations.bulk_update(body)

Update many relations in a single operation: set values and/or patch metadata.

ParameterTypeDescription
body Any A list of {"id": ..., "value": ..., "metadata": [...]} objects. value is set only when the key is present (None sends JSON null); metadata is a list of metadata ops, as for patch_metadata. The relations may lie in several documents of one project. Every document touched has its version bumped, and every new version comes back in X-Document-Versions (past fifty documents, only their number, in X-Document-Versions-Omitted). A document-version precondition is accepted only when every entry lies in one document. An unknown id refuses the whole update.

Server

server.info()

This server's version and the limits it enforces. Sizes are in bytes. Unauthenticated.

server.limits()

Just the limits, which is what a caller almost always wants.

server.health()

Liveness, version and database size.

Unauthenticated, and served outside the REST router at /health, so it answers even when the API is refusing requests. Never cached — the point is that it is current.

Span Layers

span_layers.get(span_layer_id)

Get a span layer by ID.

ParameterTypeDescription
span_layer_id Any The span layer ID
span_layers.delete(span_layer_id)

Delete a span layer.

ParameterTypeDescription
span_layer_id Any The span layer ID
span_layers.update(span_layer_id, name)

Update a span layer's name.

ParameterTypeDescription
span_layer_id Any The span layer ID
name Any The name
span_layers.shift(span_layer_id, direction)

Shift a span layer's display order.

ParameterTypeDescription
span_layer_id Any The span layer ID
direction Any The direction ("up" or "down")

Spans

spans.set_metadata(span_id, body)

Replace all metadata for a span.

ParameterTypeDescription
span_id Any The span ID
body Any The request body
spans.delete_metadata(span_id)

Remove all metadata from a span.

ParameterTypeDescription
span_id Any The span ID
spans.patch_metadata(span_id, body)

Edit metadata for a span with a list of ops applied in order.

{"op": "set", "path": [...], "value": v} writes v at the path, creating missing objects along it; {"op": "delete", "path": [...]} removes the key at the path (a no-op when absent). A path is a non-empty list of keys, the first a top-level key. A path through a non-object is refused (400). See :func:metadata_ops and :func:apply_metadata_ops.

ParameterTypeDescription
span_id Any The span ID
body Any The metadata ops
spans.set_tokens(span_id, tokens)

Replace the tokens associated with a span.

ParameterTypeDescription
span_id Any The span ID
tokens Any The tokens
spans.get(span_id)

Get a span by ID.

ParameterTypeDescription
span_id Any The span ID
spans.delete(span_id)

Delete a span.

ParameterTypeDescription
span_id Any The span ID
spans.update(span_id, value)

Update a span's value.

ParameterTypeDescription
span_id Any The span ID
value Any The value
spans.bulk_create(body)

Create multiple spans in a single operation.

ParameterTypeDescription
body Any The request body
spans.bulk_delete(body)

Delete multiple spans in a single operation. Provide a list of IDs.

ParameterTypeDescription
body Any The request body
spans.bulk_update(body)

Update many spans in a single operation: set values and/or patch metadata.

ParameterTypeDescription
body Any A list of {"id": ..., "value": ..., "metadata": [...]} objects. value is set only when the key is present (None sends JSON null); metadata is a list of metadata ops, as for patch_metadata. The spans may lie in several documents of one project. Every document touched has its version bumped, and every new version comes back in X-Document-Versions (past fifty documents, only their number, in X-Document-Versions-Omitted). A document-version precondition is accepted only when every entry lies in one document. An unknown id refuses the whole update.

Text Layers

text_layers.get(text_layer_id)

Get a text layer by ID.

ParameterTypeDescription
text_layer_id Any The text layer ID
text_layers.delete(text_layer_id)

Delete a text layer.

ParameterTypeDescription
text_layer_id Any The text layer ID
text_layers.update(text_layer_id, name)

Update a text layer's name.

ParameterTypeDescription
text_layer_id Any The text layer ID
name Any The name
text_layers.shift(text_layer_id, direction)

Shift a text layer's order within the project.

ParameterTypeDescription
text_layer_id Any The text layer ID
direction Any The direction ("up" or "down")

Texts

texts.get(text_id)

Get a text.

ParameterTypeDescription
text_id Any The text ID
texts.delete(text_id)

Delete a text and all dependent data.

ParameterTypeDescription
text_id Any The text ID
texts.set_metadata(text_id, body)

Replace all metadata for a text.

ParameterTypeDescription
text_id Any The text ID
body Any The request body
texts.delete_metadata(text_id)

Remove all metadata from a text.

ParameterTypeDescription
text_id Any The text ID
texts.patch_metadata(text_id, body)

Edit metadata for a text with a list of ops applied in order.

{"op": "set", "path": [...], "value": v} writes v at the path, creating missing objects along it; {"op": "delete", "path": [...]} removes the key at the path (a no-op when absent). A path is a non-empty list of keys, the first a top-level key. A path through a non-object is refused (400). See :func:metadata_ops and :func:apply_metadata_ops.

ParameterTypeDescription
text_id Any The text ID
body Any The metadata ops

Token Layers

token_layers.get(token_layer_id)

Get a token layer by ID.

ParameterTypeDescription
token_layer_id Any The token layer ID
token_layers.delete(token_layer_id)

Delete a token layer.

ParameterTypeDescription
token_layer_id Any The token layer ID
token_layers.update(token_layer_id, name)

Update a token layer's name.

ParameterTypeDescription
token_layer_id Any The token layer ID
name Any The name
token_layers.shift(token_layer_id, direction)

Shift a token layer's display order.

ParameterTypeDescription
token_layer_id Any The token layer ID
direction Any The direction ("up" or "down")

Tokens

tokens.set_metadata(token_id, body)

Replace all metadata for a token.

ParameterTypeDescription
token_id Any The token ID
body Any The request body
tokens.delete_metadata(token_id)

Remove all metadata from a token.

ParameterTypeDescription
token_id Any The token ID
tokens.patch_metadata(token_id, body)

Edit metadata for a token with a list of ops applied in order.

{"op": "set", "path": [...], "value": v} writes v at the path, creating missing objects along it; {"op": "delete", "path": [...]} removes the key at the path (a no-op when absent). A path is a non-empty list of keys, the first a top-level key. A path through a non-object is refused (400). See :func:metadata_ops and :func:apply_metadata_ops.

ParameterTypeDescription
token_id Any The token ID
body Any The metadata ops
tokens.get(token_id)

Get a token.

ParameterTypeDescription
token_id Any The token ID
tokens.delete(token_id)

Delete a token and remove it from any spans.

If this causes a span to have no remaining tokens, the span will also be deleted.

ParameterTypeDescription
token_id Any The token ID
tokens.bulk_create(body)

Create multiple tokens in a single operation.

ParameterTypeDescription
body Any The request body
tokens.bulk_delete(body)

Delete multiple tokens in a single operation. Provide a list of IDs.

ParameterTypeDescription
body Any The request body
tokens.bulk_update(body)

Patch the metadata of many tokens in a single operation.

ParameterTypeDescription
body Any A list of {"id": ..., "metadata": [...]} objects; each metadata is a list of metadata ops, as for patch_metadata. The tokens may lie in several documents of one project. Every document touched has its version bumped, and every new version comes back in X-Document-Versions (past fifty documents, only their number, in X-Document-Versions-Omitted). A document-version precondition is accepted only when every entry lies in one document. An unknown id refuses the whole update.
tokens.merge(token_id, other_token_id)

Merge two tokens.

The left token (smaller begin) survives with the combined extent; the right is deleted and its spans and vocab-links are reparented to the left. On partitioning layers the two tokens must be adjacent; on non-overlapping layers the merged extent must not engulf a third token.

ParameterTypeDescription
token_id Any The anchor token ID
other_token_id Any The other token to merge in
tokens.shift(token_id, begin, end)

Shift a token's boundary.

On partitioning layers the adjacent token is auto-adjusted to preserve the partition; on non-overlapping layers the shift is rejected if it would create an overlap.

ParameterTypeDescription
token_id Any The token ID
begin Any New start offset, inclusive (Unicode code points). Omit to leave unchanged.
end Any New end offset, exclusive (Unicode code points). Omit to leave unchanged.

User Data

user_data.get()

Read one entry ({key, updated_at, value}); 404 if absent.

user_data.put()

Create or replace one entry. value is any JSON (up to 1 MB).

The server stores it verbatim, but this client recases object keys on the way out and back like any other body (my_key <-> my-key), so a value whose keys are snake_case round-trips unchanged while one keyed by arbitrary strings does not. Put such a map under a metadata key, which both clients pass through untouched.

user_data.delete()

Delete one entry; 404 if absent.

Users

users.list(q)

List (or search) users. Admin-or-maintainer only.

Transparently follows server-side pagination cursors and returns the full flat list.

ParameterTypeDescription
q Any Filter to users whose display name or email contains this text (case-insensitive)
users.iter_pages(q, page_size)

Iterate over pages of users, yielding each page's entries list.

ParameterTypeDescription
q Any Filter to users whose display name or email contains this text (case-insensitive)
page_size Any Page size (1..1000)
users.get(id)

Get a user by ID.

ParameterTypeDescription
id Any The resource ID
users.delete(id)

Deactivate a user.

Users are never hard-deleted: deactivation rejects their logins and tokens, strips their project memberships and vocab maintainerships, and revokes their API tokens. The user stays visible in listings with a deactivated-at timestamp. Reversible via :meth:activate, which restores login only.

ParameterTypeDescription
id Any The resource ID
users.activate(id)

Reactivate a deactivated user, restoring their ability to log in.

Project memberships, vocab maintainerships, and API tokens removed at deactivation are NOT restored — re-grant them deliberately.

ParameterTypeDescription
id Any The resource ID
users.get_avatar(id)

Get a user's profile picture as raw bytes.

Raises for 404 when the user has no picture, so check the user record's avatar_hash first if that is not an error for you.

ParameterTypeDescription
id Any The user ID
users.set_avatar(id, file)

Upload a profile picture. Your own, or anyone's if you are an admin.

The server center-crops to a square, scales to the configured edge length, and re-encodes, so no client-side resizing is needed. Accepts PNG, JPEG, WebP, and GIF. Returns the updated user record, whose avatar_hash is the new picture's cache key.

ParameterTypeDescription
id Any The user ID
file Any The image to upload (an open binary file object or bytes)
users.delete_avatar(id)

Remove a profile picture. Your own, or anyone's if you are an admin.

ParameterTypeDescription
id Any The user ID
users.avatar_url(id, avatar_hash)

URL for a user's profile picture, with the session token in the query string so it works in contexts that cannot set an Authorization header (an HTML image element, say).

Pass the user record's avatar_hash whenever you have it: the URL then addresses that exact picture, so it can be cached indefinitely and still picks up a replacement the moment the user changes it.

ParameterTypeDescription
id Any The user ID
avatar_hash Any The user record's avatar_hash

Vocab Items

vocab_items.bulk_create(body)

Create multiple vocab items in a single operation.

Entries may target different vocab layers; the user must have write access to each. Each entry is a dict with keys vocab_layer_id, form, and optional metadata.

ParameterTypeDescription
body Any The vocab items to create
vocab_items.bulk_update(body)

Update many vocab items in a single operation: set forms and/or patch metadata.

Each entry is a dict with id and either or both of form (set only when the key is present) and metadata (a list of metadata ops, as for patch_metadata). The entries may lie in several vocab layers; the user must have write access to each. An unknown id refuses the whole update, and an id may appear only once. Only an entry whose form really changes restates the documents linking it, and a strict-mode client picks up their new versions from the response.

ParameterTypeDescription
body Any The vocab item updates
vocab_items.bulk_delete(body)

Delete multiple vocab items in a single operation. Provide a list of IDs.

Each item's descendant vocab links are deleted too. Every document holding one of those links has its version bumped, and a strict-mode client picks up their new versions from the response.

ParameterTypeDescription
body Any The vocab item IDs to delete
vocab_items.get(id)

Get a vocab item by ID.

ParameterTypeDescription
id Any The resource ID
vocab_items.delete(id, expected_link_count)

Delete a vocab item, and every link to it.

Every document holding one of those links has its version bumped, and a strict-mode client picks up their new versions from the response.

ParameterTypeDescription
id Any The resource ID
expected_link_count Any The number of links the caller showed. The delete is refused with a 409 when the entry has any other number of links by then.
vocab_items.merge(survivor_id, loser_ids)

Merge entries into this one, in one operation.

Every link to a loser moves to the survivor (keeping its id and metadata), except a link on words the survivor is already linked to, which is deleted, and then the losers are deleted. Links are read when the merge runs, so one made after the caller looked moves too. Every loser must be in the survivor's vocabulary, and a loser that is already gone is skipped, so a repeated merge changes nothing. References to a loser in other entries' metadata are the caller's to rewrite, in the same batch. Needs maintainer rights on the vocabulary.

ParameterTypeDescription
survivor_id Any The entry that stays
loser_ids Any The entries merged into it
vocab_items.update(id, form)

Update a vocab item's form.

A document read carries the entry's form on every link to it, so a rename restates those documents: each has its version bumped, and a strict-mode client picks up their new versions from the response.

ParameterTypeDescription
id Any The resource ID
form Any The vocab item form
vocab_items.set_metadata(id, body)

Replace all metadata for a vocab item.

ParameterTypeDescription
id Any The resource ID
body Any The request body
vocab_items.delete_metadata(id)

Remove all metadata from a vocab item.

ParameterTypeDescription
id Any The resource ID
vocab_items.patch_metadata(id, body)

Edit metadata for a vocab item with a list of ops applied in order.

{"op": "set", "path": [...], "value": v} writes v at the path, creating missing objects along it; {"op": "delete", "path": [...]} removes the key at the path (a no-op when absent). A path is a non-empty list of keys, the first a top-level key. A path through a non-object is refused (400). See :func:metadata_ops and :func:apply_metadata_ops.

ParameterTypeDescription
id Any The resource ID
body Any The metadata ops

Vocab Layers

vocab_layers.get_item_at(id, item_id, as_of)

One entry of the vocabulary as it was at as_of, also when it has been deleted since.

The same shape as vocab_items.get. A 404 when the entry was not in this vocabulary at that time.

ParameterTypeDescription
id Any The vocabulary ID
item_id Any The entry ID
as_of Any The moment to read at (ISO-8601 instant)
vocab_layers.delete(id)

Delete a vocab layer.

ParameterTypeDescription
id Any The resource ID
vocab_layers.update(id, name)

Update a vocab layer's name.

ParameterTypeDescription
id Any The resource ID
name Any The name
vocab_layers.list()

List all vocab layers accessible to the current user.

Transparently follows server-side pagination cursors and returns the full flat list.

vocab_layers.list_page(limit, cursor)

List one page of vocab layers.

ParameterTypeDescription
limit Any Page size (1..1000)
cursor Any Opaque cursor from a previous page's next_cursor
vocab_layers.iter_pages(page_size)

Iterate over pages of vocab layers, yielding each page's entries list.

ParameterTypeDescription
page_size Any Page size (1..1000)
vocab_layers.create(name, [id])

Create a new vocab layer.

Also registers the current user as a maintainer.

ParameterTypeDescription
name Any The name
id optional Any Optional. The id to create it under, a UUIDv7 this client minted (plaid_client.uuid7()), so a create sent again after its answer was lost lands once (409 with id_taken when the id was used before).
vocab_layers.add_maintainer(id, user_id)

Assign a user as a maintainer for this vocab layer.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID
vocab_layers.remove_maintainer(id, user_id)

Remove a user's maintainer privileges for this vocab layer.

ParameterTypeDescription
id Any The resource ID
user_id Any The user ID