Drafted content
Upload X posts, LinkedIn posts, newsletters, and X Articles to a publication's library over the REST API.
Alongside long-form posts, a Paragraph publication keeps a library of short-form content: X posts and threads, LinkedIn posts, one-off emails, and X Articles. Writers see it in the app under Content, and the writing agent fills it as it works.
The /v1/content endpoints let you fill it too. Draft something anywhere (a script, a CI job, your own editor) and it lands in the writer's library ready to review.
Uploading creates a draft. Nothing is posted, emailed, or scheduled, and no draft you upload can go out without the writer sending it from the app.
Kinds and their bodies
Every piece has a kind, and body carries the artifact in the shape that kind uses. title is what the piece is called in the library, and isn't published anywhere.
| Kind | Body |
|---|---|
tweet | text for a single post, or tweets for a thread (one entry per tweet, at most 280 characters each). Send one or the other, never both. |
linkedin | text |
newsletter | subject, body, and an optional preheader |
x_article | title (the headline X publishes), body as CommonMark markdown, and an optional canonicalUrl |
Bodies are validated the same way the app validates them, so a thread with a 400-character entry or an Article missing its headline comes back with the same explanation you'd see while editing.
Drafts created through the API are text-only. Media has to be uploaded to X or LinkedIn first, using the publication's own connection to those platforms, which the API can't do yet. Add images to a draft in the app.
Create a draft
curl -X POST "https://public.api.paragraph.com/api/v1/content" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{
"kind": "tweet",
"title": "Thread on writing in public",
"body": { "tweets": ["Writing in public changes what you write.", "Here is what changed for me."] }
}'{
"id": "7f3a2c18-5b9e-4c21-9a0d-8e6b1f4d2a55",
"kind": "tweet",
"title": "Thread on writing in public",
"excerpt": "Writing in public changes what you write.",
"status": "draft",
"scheduled": false,
"lockedReason": null,
"publishedAt": null,
"url": null,
"archivedAt": null,
"createdAt": "2026-08-19T09:12:44.108Z",
"updatedAt": "2026-08-19T09:12:44.108Z",
"body": { "tweets": ["Writing in public changes what you write.", "Here is what changed for me."] }
}List your library
curl "https://public.api.paragraph.com/api/v1/content?kind=tweet&status=draft&limit=20" \
-H "Authorization: Bearer your-api-key"Filter with kind and status, and page with cursor. status takes all (the default), draft, published, or archived:
publishedmeans the piece has been delivered somewhere. A delivered piece keeps that status even if it's archived afterwards.archivedis what the writer has put away and never sent.allis everything except the pieces archived without being sent.
A delivered piece also carries url, the address it went live at, alongside the publishedAt of the same delivery. It's null for anything still a draft, and null by design for a channel that publishes no page: a one-off email renders into the message itself, so there's nowhere to link to. A thread reports the link to its first post.
Listing leaves bodies out. Fetch a single piece to read one:
curl "https://public.api.paragraph.com/api/v1/content/7f3a2c18-5b9e-4c21-9a0d-8e6b1f4d2a55" \
-H "Authorization: Bearer your-api-key"Edit a draft
PATCH renames a piece, replaces its body, or both. body replaces the artifact entirely, in the same shape POST takes, so send the whole thing rather than the part that changed. Media attached to the draft in the app is the one exception: you can't send it back, so it's carried over rather than dropped.
curl -X PATCH "https://public.api.paragraph.com/api/v1/content/7f3a2c18-5b9e-4c21-9a0d-8e6b1f4d2a55" \
-H "Authorization: Bearer your-api-key" \
-H "Content-Type: application/json" \
-d '{"body": {"text": "Rewritten, and shorter."}}'A piece with a send already queued goes out exactly as written, so editing its body is refused with an explanation, and lockedReason says so on every read. Cancel the schedule in the app first. Renaming is always allowed.
Archive and restore
Archiving puts a piece away without deleting it, and drops it out of the writer's Content surface. Anything still proposing the piece is dismissed alongside it, so nothing is left offering to send something that's been filed away.
curl -X POST "https://public.api.paragraph.com/api/v1/content/7f3a2c18-5b9e-4c21-9a0d-8e6b1f4d2a55/archive" \
-H "Authorization: Bearer your-api-key"Restoring brings the draft back. It doesn't bring the dismissed suggestions back with it.
curl -X POST "https://public.api.paragraph.com/api/v1/content/7f3a2c18-5b9e-4c21-9a0d-8e6b1f4d2a55/restore" \
-H "Authorization: Bearer your-api-key"Errors
| Status | What it means |
|---|---|
400 | The body doesn't match its kind, or a queued send has locked the piece. msg says which. |
401 | Missing or invalid API key. |
404 | No such piece in this publication, or the publication has no agent workspace yet (open it once at app.paragraph.com). |