What it does not do
The gaps, the sharp edges and the settled refusals — stated here so the first curl doesn't have to teach them.
A page that only lists features costs more than no page at all: it wastes the
time of whoever believes it, and the first curl corrects it anyway.
So here is the other list. It has three parts, and they are different in kind.
Settled refusals
These are decisions, not gaps. They are not coming.
No GraphQL. REST only, now and later.
No shareable file URLs. No public bucket, no signed links. Why.
No sorting or grouping by an array property. multi_select and
multi_person refuse both, with a 400 that says so, rather than ordering by the
document order of a JSON array.
Why.
No offset pagination. Keyset cursors only, everywhere. Why.
No 403 for a workspace you are not in. You get a 404, and the API will not tell you which of the two it means. Why.
No HTML in page bodies. The block format cannot express it. Why.
Not built yet
These are absences, and they may change. They are listed because building on their absence, or on their imminent arrival, is a choice you should make knowingly.
No draft/published distinction. There is no publication state, no scheduled
publishing and no preview régime. A card is either there or archived. If you
need drafts today, a select property is the honest answer, and your consumer
filters on it.
No full-text search over card bodies. Search covers card titles and list names only. There is no body index, and views cannot filter or sort on a body either.
No calendar view in the interface. The API accepts kind: "calendar" and
the view compiler serves it like any other grouped view; the web app renders
table, list and board. The data is there, the screen is not.
No automatic index creation. POST …/index-hints tells you which indexes a
view wants, and stops there — the job that would apply them does not exist.
Details.
No bulk edit past 5 000 cards. The API refuses rather than pretending to be
atomic or silently queueing work. Between 200 and 5 000 you must pass
expectedCount. Details.
No duplicating a subtree past 200 cards. Duplication is a synchronous, atomic gesture that hands you back the copy; past that it would be an import, and it refuses rather than pretend. Details.
No rate limits. None today. That is a description of the current implementation, not a guarantee — do not build something that requires their absence.
No outbound email. This deployment sends none. Invitations are tokens someone hands over; nothing verifies that an address belongs to whoever typed it. That last part matters if you are reasoning about account security: it is precisely why an external identity is never linked to an account on the strength of a matching email address.
Sharp edges
These are real behaviours that surprise people. They are documented rather than patched, because in each case the alternative was worse.
A select sorts alphabetically, not in the order of its options. A view "by
priority" returns critical, high, low, normal. Encode the order into the
values if you need it. Details.
Manual order is global to a list, not per board column. Dropping a card at the top of a column also makes it the first row of the table view. Details.
display is replaced wholesale. Saving a table's columns without sending
back groupOrder erases the board's column order.
config is replaced wholesale too. Saving a property's options without
sending back optionColors erases the colours. To add or recolour a single
option, POST …/fields/{fieldId}/options merges instead.
Properties are keyed by key, views reference fieldId. Two names for the
same field, used in different places, and mixing them up gets you an "unknown
property" 400. Details.
The type a file is served as is not the type it was uploaded as. An SVG
comes back as an application/octet-stream download with image: false.
Why.
icon: null removes the icon; omitting icon leaves it alone. The same
distinction the rest of PATCH uses, and worth knowing before you build a client
that sends a whole card back on every keystroke.
coverFileId is a file id, not a URL, and only an image passes. Upload
first (POST …/files), then write the id. A file from another workspace, or one
not served as an image (an SVG, a PDF), is a 400. null — or "" — removes the
cover, omitting it leaves it alone, and removing it does not delete the file.
Files.
createdBy is set from the caller, never from the request body. Sending one
is not an error — unknown keys are dropped, so you get a 201 and an author that
is you. It is null for imports, seeds and anything created before the column
existed.
When this page is wrong
It is written by hand, and the API reference is generated from the code. If they disagree, believe the reference — and the gap is a bug in this page.