Recoup

Get started with Recoup.

API REFERENCE

Create Post

On this page
POST/api/posts

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.

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:

Platformtitledescriptioncover_urlprivacy, tags
youtuberequired, video title, max 100 charactersvideo description, max 5,000 charactersnot used (YouTube picks the frame)used
instagramnot usedrequired, Reel caption, max 2,200 charactersReel cover imagenot used
xnot usedrequired, post text, max 280 weighted characters (URLs count as 23)not usednot used
linkedinvideo title shown on the postrequired, post commentary, max 3,000 charactersvideo thumbnailnot 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
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 for this request
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_idstring

Optional. The account whose connection posts (e.g. an artist). The authenticated account must have access. Defaults to the calling account.

format: uuid

platformstring · enumrequired

Target platform. The account must have this platform connected.

Values: "instagram", "x", "linkedin", "youtube"

sub_account_idstring

Optional, 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_urlstringrequired

Publicly reachable URL of the video file (MP4). It is fetched at publish time, so it must still resolve then.

format: uri

cover_urlstring

Optional. Publicly reachable image URL used as the cover or thumbnail where the platform supports one (see the table in the description).

format: uri

titlestring

Title. Required for youtube; see the table in the description.

descriptionstring

Body text: caption, post text or description, depending on the platform. Required for instagram, x and linkedin.

privacystring · enum

Optional, YouTube only. Visibility after upload.

Values: "public", "unlisted", "private"

Default: "public"

tagsarray<string>

Optional, YouTube only. Video tags.

Item properties for tags

string

publish_atstring

Optional. 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 header

URL of the created post.

application/json

idstringrequired

The post's id. Poll Get Post with this.

format: uuid

statusstring · enumrequired

Lifecycle 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 · enumrequired

Always 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

errorstringrequired

Human-readable error message

401Unauthorized. Provide exactly one of x-api-key or Authorization: Bearer.

application/json

statusstring · enumrequired

Always 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

errorstringrequired

Human-readable error message

403Access denied to the specified account_id.

application/json

statusstring · enumrequired

Always 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

errorstringrequired

Human-readable error message

409The platform is not connected for this account. Authorize it via Authorize Connector before retrying.

application/json

statusstring · enumrequired

Always 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

errorstringrequired

Human-readable error message

500Internal server error.

application/json

statusstring · enumrequired

Always 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

errorstringrequired

Human-readable error message

Full specification

Download the OpenAPI file for complete schemas, constraints, and examples.

Download social.json
View operation source
json
{
  "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"
}