API REFERENCE
Create Post
On this page
/api/postsAvailability: pending. This endpoint is specified ahead of implementation and is not yet live on the hosted API; it ships through recoupable/app#2241. Do not rely on it until this notice is removed.
Publish a video with its copy to one connected social account, now or at a set time. Send one request per target account; to post the same video to several platforms, call this once per platform.
Without publish_at the post starts publishing immediately and is returned as publishing. With publish_at it is returned as scheduled and publishes at that time. Either way this returns 202 right away: poll Get Post until status is posted, failed or cancelled.
The platform must already be connected for the account (see Authorize Connector). Copy uses one shape for every platform. Which fields each platform uses, and their limits:
| Platform | title | description | cover_url | privacy, tags |
|---|---|---|---|---|
youtube | required, video title, max 100 characters | video description, max 5,000 characters | not used (YouTube picks the frame) | used |
instagram | not used | required, Reel caption, max 2,200 characters | Reel cover image | not used |
x | not used | required, post text, max 280 weighted characters (URLs count as 23) | not used | not used |
linkedin | video title shown on the post | required, post commentary, max 3,000 characters | video thumbnail | not used |
Limits are checked here, so an over-length body fails with 400 instead of at publish time.
Authentication
x-api-key in header
bearerAuth bearer
Request
curl --request POST \
--url 'https://api.recoupable.dev/api/posts' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"platform": "instagram",
"video_url": "string"
}'Try it
Fill in the fields, send the request from your browser, and read the live response. The curl below updates as you type.
Kept in this browser tab only and cleared when it closes.
curl --request POST \
--url 'https://recoup-api.vercel.app/api/posts' \
--header 'Content-Type: application/json' \
--data '{
"platform": "instagram",
"video_url": "string"
}'Request body required
application/json
account_idstringOptional. The account whose connection posts (e.g. an artist). The authenticated account must have access. Defaults to the calling account.
format: uuid
platformstring · enumrequiredTarget platform. The account must have this platform connected.
Values: "instagram", "x", "linkedin", "youtube"
sub_account_idstringOptional, LinkedIn only. Organization URN to post as a Page (e.g. urn:li:organization:101127186) instead of as the connected member. The connected member must be an admin of that Page.
video_urlstringrequiredPublicly reachable URL of the video file (MP4). It is fetched at publish time, so it must still resolve then.
format: uri
cover_urlstringOptional. Publicly reachable image URL used as the cover or thumbnail where the platform supports one (see the table in the description).
format: uri
titlestringTitle. Required for youtube; see the table in the description.
descriptionstringBody text: caption, post text or description, depending on the platform. Required for instagram, x and linkedin.
privacystring · enumOptional, YouTube only. Visibility after upload.
Values: "public", "unlisted", "private"
Default: "public"
tagsarray<string>Optional, YouTube only. Video tags.
Item properties for tags
string
publish_atstringOptional. ISO 8601 time to publish, in the future. Omit to publish now.
format: date-time
Responses
202Post accepted: publishing now, or scheduled for publish_at.+
Locationresponse headerURL of the created post.
application/json
statusstring · enumrequiredLifecycle phase. scheduled until publish_at, publishing while the platform upload runs, then posted or failed. cancelled only from scheduled.
Values: "scheduled", "publishing", "posted", "failed", "cancelled"
400Validation failed: a missing or unknown field, a field the platform requires (YouTube title; Instagram, X and LinkedIn description), a body over the platform's limit, or a publish_at in the past. missing_fields names the first offending field.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
401Unauthorized. Provide exactly one of x-api-key or Authorization: Bearer.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
403Access denied to the specified account_id.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
409The platform is not connected for this account. Authorize it via Authorize Connector before retrying.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
500Internal server error.+
application/json
statusstring · enumrequiredAlways error
Values: "error"
missing_fieldsarray<one of>JSON path segments to the first field that failed validation, e.g. ["description"]
Item properties for missing_fields
oneOf · string 1
string
oneOf · integer 2
integer
errorstringrequiredHuman-readable error message
Full specification
Download the OpenAPI file for complete schemas, constraints, and examples.
Download social.jsonView operation source
{
"summary": "Create post",
"description": "**Availability: pending.** This endpoint is specified ahead of implementation and is not yet live on the hosted API; it ships through recoupable/app#2241. Do not rely on it until this notice is removed.\n\nPublish a video with its copy to **one** connected social account, now or at a set time. Send one request per target account; to post the same video to several platforms, call this once per platform.\n\nWithout `publish_at` the post starts publishing immediately and is returned as `publishing`. With `publish_at` it is returned as `scheduled` and publishes at that time. Either way this returns **202** right away: poll [Get Post](/api-reference/publishing/get) until `status` is `posted`, `failed` or `cancelled`.\n\nThe platform must already be connected for the account (see [Authorize Connector](/api-reference/connectors/authorize)). Copy uses one shape for every platform. Which fields each platform uses, and their limits:\n\n| Platform | `title` | `description` | `cover_url` | `privacy`, `tags` |\n|---|---|---|---|---|\n| `youtube` | **required**, video title, max 100 characters | video description, max 5,000 characters | not used (YouTube picks the frame) | used |\n| `instagram` | not used | **required**, Reel caption, max 2,200 characters | Reel cover image | not used |\n| `x` | not used | **required**, post text, max 280 weighted characters (URLs count as 23) | not used | not used |\n| `linkedin` | video title shown on the post | **required**, post commentary, max 3,000 characters | video thumbnail | not used |\n\nLimits are checked here, so an over-length body fails with 400 instead of at publish time.",
"security": [
{
"apiKeyAuth": []
},
{
"bearerAuth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreatePostRequest"
}
}
}
},
"responses": {
"202": {
"description": "Post accepted: publishing now, or scheduled for `publish_at`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreatePostResponse"
}
}
},
"headers": {
"Location": {
"schema": {
"type": "string"
},
"description": "URL of the created post."
}
}
},
"400": {
"description": "Validation failed: a missing or unknown field, a field the platform requires (YouTube `title`; Instagram, X and LinkedIn `description`), a body over the platform's limit, or a `publish_at` in the past. `missing_fields` names the first offending field.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"401": {
"description": "Unauthorized. Provide exactly one of `x-api-key` or `Authorization: Bearer`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"403": {
"description": "Access denied to the specified `account_id`.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"409": {
"description": "The `platform` is not connected for this account. Authorize it via [Authorize Connector](/api-reference/connectors/authorize) before retrying.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
},
"500": {
"description": "Internal server error.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PostErrorResponse"
}
}
}
}
},
"x-recoup-availability": "pending"
}