OpenAPI reference · Posts
Create a draft (or scheduled) post
/postsCreates a tPost row in draft status (or scheduled if
scheduledFor is set). Always pairs the post with an empty
tTranscription placeholder for parity with the dashboard.
With scheduledFor set, the post is scheduled at that
timestamp and the project's LinkedIn connection is verified
first: no access token on file is an immediate refusal, and
otherwise a live /me ping + token refresh for personal brands
(non-empty admin set for company pages). If LinkedIn isn't
usable the request fails 409 conflict and nothing is created.
The 409 body carries details.reason
(not_connected | token_invalid | no_company_page_admin),
details.retryable: false — only a human can repair a
connection — and details.reconnectUrl, the page they open to
do it (null for no_company_page_admin, whose repair is on a
different project). The post-scheduler cron handles the publish
itself; if that later fails, the post moves to
failed_to_publish (same path as the dashboard).
Required scope: posts:write.
Authorization
AuthorizationstringrequiredBearer token in the Authorization header.
Pass
Authorization: Bearer scripe_sk_live_<...>(orscripe_sk_test_<...>for test keys) on every request. Keys are scoped to a single workspace and can be revoked from the Scripe dashboard.The same header also accepts an OAuth 2.1 access token (
scripe_oat_*); both credentials share one scope vocabulary and every operation below documents the scope it requires. An API key can hold every scope named on this surface exceptwebhooks:manage, which is grantable to OAuth tokens only today — the webhook-endpoint operations answer403 scope_missingto every API key. Operations that name no scope accept any valid token of the workspace.
Header parameters
Scripe-Api-VersionstringPin the API version. Format
YYYY-MM-DD. Omit to receive the currently rolling default. Unknown versions return400 version_unsupported.Idempotency-KeystringOpaque string (1–64 chars,
[A-Za-z0-9_-]) used to dedup retried writes. Within 24h of the first request, the same key- same body returns the original response (
Idempotent-Replayed: true). Same key + different body returns409 idempotency_key_conflict.
Strongly recommended for every write — see
/docs/api/v1/idempotency.- same body returns the original response (
Request bodyapplication/json
projectIdstringrequiredcontentstringPost body. Defaults to a single space (TipTap rejects truly-empty content).
titlestringThe post's INTERNAL name — shown in the dashboard and in teammates' notifications. It is not published as post copy, with one exception: on a post whose attached media asset (a document or an image) carries no filename of its own, a title YOU set is used as that asset's display title on LinkedIn. Only a title you set is eligible — a title Scripe derived is never published, and such a post's asset goes out titled
Document.Omitting it and sending
""do the same thing here: Scripe names the post after its own opening line, so a post created withcontentis never nameless. Send a real title and the name is yours: nothing overwrites it afterwards, not a laterPATCHofcontentand not the AI passes that rewrite the draft.contentTypestringAllowed values:
PERSONAL,BUSINESS_INTERNAL,BUSINESS_EXTERNAL,EDUCATIONAL,UNKNOWNDefault: "PERSONAL"
scheduledForstring <date-time> | nullFuture ISO timestamp at which the post should publish. If omitted/null, the post stays in
draft.A timestamp WITHOUT an offset (
2026-08-18T09:00:00) is resolved in the project's calendar timezone — not UTC and not the server's zone — because that is what "9am" means to the person asking. Send an offset orZwhen you mean an absolute instant. A date alone (2026-08-18) is rejected with422: it would schedule the post at midnight.
Responses
- 200
Post created (or replayed via Idempotency-Key).
- 400
Malformed request (bad cursor, bad limit, etc.).
- 401
Missing, malformed, expired, or revoked API key.
- 403
Plan not eligible, scope missing, or workspace mismatch.
- 404
Resource not found in this workspace.
- 409
Either an
Idempotency-Keyconflict, or LinkedIn is not connected for the project — a post cannot be created already scheduled against a dead connection. - 413
Request body exceeds the size cap.
- 422
Body shape was JSON but failed validation (
unprocessable). - 429
Sliding-window rate limit exceeded.
Example request
curl --request POST \
--url 'https://api.scripe.io/v1/posts' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{
"projectId": "proj_a1b2c3d4e5f6g7h8",
"content": "string",
"title": "string",
"contentType": "PERSONAL",
"scheduledFor": "2026-08-18T09:00:00+02:00"
}'Example response (200)
{
"data": {
"id": "post_a1b2c3d4e5f6g7h8",
"projectId": "string",
"status": "waitingProcessing",
"statusId": "string",
"statusTitle": "string",
"statusCategory": "suggested",
"platform": "string",
"contentType": "string",
"funnelStage": "REACH",
"title": "string",
"content": "string",
"contentTruncated": true,
"scheduledAt": "2026-08-10T09:00:00Z",
"publishedAt": "2026-08-10T09:00:00Z",
"lastPublishError": "string",
"lastPublishAttemptAt": "2026-08-10T09:00:00Z",
"delivery": {
"state": "not_scheduled",
"willRetry": true,
"retryUntil": "2026-08-10T09:00:00Z",
"detail": "string"
},
"review": {
"state": "not_requested",
"awaitingDecision": true,
"reviewerUserId": "string",
"deadline": "2026-08-10T09:00:00Z",
"overdue": true,
"decidedAt": "2026-08-10T09:00:00Z",
"notes": "string",
"requiredByWorkspace": true,
"detail": "string"
},
"media": {},
"createdAt": "2026-08-10T09:00:00Z",
"updatedAt": "2026-08-10T09:00:00Z"
}
}