Never gets rejected
Every write validates every channel against its platform's real rules and returns errors with real numbers and machine fixes — one ready flag is the only gate, and autofix repairs what it safely can.
Inklate validates every channel against the real rules of its platform — length limits, media counts, aspect ratios, required titles — on every write and read. There is no separate check step: create, update, get, and the dry-run validate all return the same per-channel verdict, so a post that says it’s ready cannot be rejected at publish time. The fit check runs the exact ladder the publish worker runs.

What the fit check covers
For each channel, an ordered ladder where the first refusal wins:
- Reachability — is the channel connected and its grant active? A disconnected channel or expired token refuses here (code
auth) and needs a reconnect, not a retry. - Scopes — does the connection carry the permissions the format needs?
- Content — does the adapted text and media fit the platform’s rules: length, item counts, media count, aspect ratio, mixing?
- Org policy — banned terms and blocked links, if your organization configured them; quiet hours once a schedule time exists.
- Publish windows — a provider’s own daily quota, checked last: it’s about how much already went out, not whether this draft fits.
Errors carry real numbers and machine fixes
Each channel entry in the response reports ready plus errors[]. Every error names a stable code, the field to change (content, channel, media, options, or schedule), a human message with the actual numbers (“truncated at 280”, not “too long”), and machine fixes[] where a repair exists. Soft issues arrive as warnings[] — “dropped 2 images” when a story carried more than a format allows — and don’t block publishing.
The full catalog of codes, fields, and fix parameters is on Errors and fixes.
The five fix kinds
A fix is a machine-readable repair — the composer renders it as a one-click button, and the API can apply it for you:
| Fix | When it appears | What it does |
|---|---|---|
trim_to_fold | Text is over the format’s limit | Trims the text to fit the limit. |
split_to_thread | An over-length post on a provider with threads | Reshapes the post into a thread, losslessly. |
rebalance_thread | Too many or too-long thread items | Caps the item count and trims each item. |
crop_to | An image doesn’t match a required aspect ratio | Crops a derivative to the required ratio. |
set_title | A video format requires a title and none is set | Adds a title within the provider’s length limit. |
Only a content refusal carries fixes; a policy refusal (a banned term, a blocked link) names the rule but has no automatic repair — you edit the copy yourself.
Autofix — repairs applied for you
autofix (default on for API writes) applies suggested fixes server-side during the write and reports them in fixes_applied, so an agent’s post fits on the first call. Two kinds are deliberately never auto-applied: split_to_thread changes the channel’s shape (that stays your decision, via per_channel format), and crop_to needs an explicit media decision. Pass autofix: false (the composer does) to keep manual control and apply fixes yourself.
An applied fix marks that channel copy Edited. No channel is ever re-adapted when the story changes later — Synced channels merely go Stale — so a fix is never silently discarded; see Write once, native everywhere.
ready is the only gate
Top-level ready is the AND of every channel’s verdict plus media readiness — the server’s single publishability signal. No client re-derives it. The consequences:
- A draft with errors saves. Saving work-in-progress is always legitimate; the response tells you what’s left to fix.
- Only arming refuses — atomically. Setting a
schedule(or publishing) a post that isn’treadyis refused and the whole write rolls back, so a partially-armed post cannot exist. The refusal carries the post it refused, so the per-channelerrors[]andfixes[]are on the failed call itself — nothing to re-send to find out why. Send the write again withoutschedulewhen you want the draft kept. - The dry run is the same verdict.
POST /posts/validate(CLI:inklate posts validate) returns the identical response shape for an inline draft or a stored post id, persisting nothing.
Beside the verdict: approval and media readiness
Two gates sit next to the fit check rather than inside it, because neither is a content problem with a channel:
- Media readiness — an attached asset still processing holds the publish until it finishes; a failed asset (upload broke, rejected, or flagged by a scan) must be removed. Both are reported on the response’s
media[]entries. See Images and video. - Approval — when your organization requires approval, a post arms only once approved; the response’s
approvalblock carries the state. See Approvals.
A failing fit check also blocks approval requests — a reviewer is never asked to approve a version that can’t ship.
Images and video
Attach images and video that upload directly over a presigned PUT and are validated against each channel's limits, moving through processing to ready or failed.
Scheduling
Schedule each channel at its own time, watch one aggregate status roll up across them, and work in your local timezone while Inklate stores everything in UTC.