Skip to main content
POST
/
api
/
partner
/
v1
/
posts
import fetch from "node-fetch";

const url = "https://www.genviral.io/api/partner/v1/posts";
const payload = {
  caption: "Holiday drops start Monday!",
  media: {
    type: "video",
    url: "https://cdn.genviral.com/holidays.mp4",
  },
  music_url: "https://www.tiktok.com/@genviral/video/1234567890",
  accounts: [
    { id: "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812" },
    { id: "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089" },
  ],
  scheduled_at: "2025-02-01T15:00:00Z",
  external_id: "partner-run-884",
};

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: "Bearer <token>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

console.log(await response.json());
{
  "ok": true,
  "code": 201,
  "message": "Post scheduled",
  "data": {
    "id": "11111111-1111-1111-1111-111111111111",
    "status": "scheduled",
    "scheduled_at": "2025-02-01T15:00:00Z",
    "warnings": [
      {
        "field": "media",
        "message": "Video duration metadata is missing",
        "code": "VIDEO_DURATION_MISSING"
      }
    ]
  }
}
Schedule a post to TikTok, Instagram, YouTube, Pinterest, LinkedIn, or Facebook - or all of them at once. This is the core posting endpoint that OpenClaw agents and automation scripts use to distribute content across platforms. Posts are stored with source="partner_api" and appear in the dashboard queue immediately. Supports TikTok photo carousels (slideshows), video posts, draft uploads via MEDIA_UPLOAD mode, trending music via music_url, and multi-account targeting across all 6 platforms.

Body Parameters

caption
string
required
Text caption applied to every platform.
Max 500 characters (applies to all accounts).
media
object
required
Provide either a single video clip or a slideshow bundle.
music_url
string
Optional TikTok post URL (e.g., https://www.tiktok.com/@genviral/video/1234567890) used to resolve the track. The API validates that the link points to TikTok and rejects requests that target any non-TikTok account.
accounts
array
required
Up to 10 account objects referencing IDs returned from /accounts.All IDs must belong to the authenticated key scope. Unknown or inactive accounts cause the request to fail.
tiktok
object
Optional TikTok publishing settings.
Supported only when every targeted account is a TikTok BYO account.
pinterest
object
Optional Pinterest publishing settings.
Supported only when at least one targeted account is a Pinterest account.
tiktok and pinterest settings are mutually exclusive in one request. Include only the platform-specific object that matches your targeted accounts.
scheduled_at
string
ISO 8601 timestamp (e.g., 2025-02-01T15:00:00Z). If omitted or within 30 seconds of “now”, the post is treated as pending for immediate publish.
external_id
string
Optional correlation ID to map posts back to your system.

Media Hosting

  • BYO Accounts: Media is automatically ingested into Genviral storage.
  • Hosted-Only: Provided URLs are validated for reachability but not re-hosted unless necessary.
  • Google Drive: Share links are auto-converted to direct download links.

Limits (enforced for all accounts)

  • Caption: max 500 characters.
  • Video: MP4/MOV/M4V/AVI, under 100MB. If duration metadata is present, we enforce 15–60 seconds; when duration is missing we proceed with a warning. ~9:16 aspect recommended.
  • Slideshow: 1–35 images, JPG/JPEG/PNG, each under 5MB. ~9:16 aspect recommended.
  • Music: blocked when any Instagram account is selected.
  • TikTok settings: supported only when all selected accounts are TikTok BYO accounts.
  • tiktok.post_mode = MEDIA_UPLOAD: supported only for media.type = slideshow (photo posts).
  • Pinterest settings: supported only when at least one selected account is Pinterest.
  • pinterest.tags: up to 30 tags, each 1–100 characters (spaces allowed).

Canonical payload shape

Use this canonical structure when calling POST /api/partner/v1/posts:
{
  "caption": "3 looks for the long weekend",
  "media": {
    "type": "slideshow",
    "urls": [
      "https://cdn.example.com/slide-1.jpg",
      "https://cdn.example.com/slide-2.jpg"
    ]
  },
  "accounts": [{ "id": "3e30c8d1-0bc0-42d8-9a1a-bb63fe6c3c16" }],
  "tiktok": {
    "post_mode": "MEDIA_UPLOAD",
    "privacy_level": "SELF_ONLY"
  },
  "scheduled_at": "2026-02-20T15:00:00Z",
  "external_id": "carousel-campaign-12"
}
Shape notes:
  • caption: required, 1..500 chars.
  • media: required object.
  • video media: { "type": "video", "url": "https://..." }.
  • slideshow media: { "type": "slideshow", "urls": ["https://...", "..."] }.
  • accounts: required array of { "id": "<uuid>" } objects.
  • scheduled_at: optional ISO 8601 string; omit for immediate queueing.
  • tiktok: optional object for TikTok-only settings.
  • pinterest: optional object for Pinterest-specific board/title/link/tag settings.
  • tiktok and pinterest cannot be sent together because their account constraints are mutually exclusive.

Response

Successful requests return 201 with:
  • id - canonical Genviral post ID
  • status - either pending or scheduled, matching the scheduling logic above
  • scheduled_at - ISO timestamp stored on the post. Immediate requests are stamped with the server “publish ASAP” timestamp (not null).
  • warnings - optional array describing non-blocking validation issues

Examples

Single video post (default)

Use a single video object when targeting TikTok, Instagram Reels, or YouTube Shorts. Add music only when all selected accounts support it (TikTok hosted/BYO today).
import fetch from "node-fetch";

const url = "https://www.genviral.io/api/partner/v1/posts";
const payload = {
  caption: "Holiday drops start Monday!",
  media: {
    type: "video",
    url: "https://cdn.genviral.com/holidays.mp4",
  },
  music_url: "https://www.tiktok.com/@genviral/video/1234567890",
  accounts: [
    { id: "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812" },
    { id: "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089" },
  ],
  scheduled_at: "2025-02-01T15:00:00Z",
  external_id: "partner-run-884",
};

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: "Bearer <token>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

console.log(await response.json());
curl --request POST \
  --url https://www.genviral.io/api/partner/v1/posts \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "caption": "Holiday drops start Monday!",
  "media": {
    "type": "video",
    "url": "https://cdn.genviral.com/holidays.mp4"
  },
  "music_url": "https://www.tiktok.com/@genviral/video/1234567890",
  "accounts": [
    { "id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812" },
    { "id": "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089" }
  ],
  "scheduled_at": "2025-02-01T15:00:00Z",
  "external_id": "partner-run-884"
}'

Slideshow post (photo carousel)

Provide up to 35 slideshow items to schedule a TikTok Photo Mode or Instagram carousel. Order is preserved as given in the media array.
import fetch from "node-fetch";

const url = "https://www.genviral.io/api/partner/v1/posts";
const payload = {
  caption: "3 looks for the long weekend ☀️",
  media: {
    type: "slideshow",
    urls: [
      "https://cdn.genviral.com/slides/look-01.jpg",
      "https://cdn.genviral.com/slides/look-02.jpg",
      "https://cdn.genviral.com/slides/look-03.jpg",
    ],
  },
  music_url: "https://www.tiktok.com/@genviral/video/9876543210",
  accounts: [{ id: "3e30c8d1-0bc0-42d8-9a1a-bb63fe6c3c16" }],
  scheduled_at: "2025-02-14T18:30:00Z",
  external_id: "carousel-campaign-12",
};

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: "Bearer <token>",
    "Content-Type": "application/json",
  },
  body: JSON.stringify(payload),
});

console.log(await response.json());
curl --request POST \
  --url https://www.genviral.io/api/partner/v1/posts \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
  "caption": "3 looks for the long weekend ☀️",
  "media": {
    "type": "slideshow",
    "urls": [
      "https://cdn.genviral.com/slides/look-01.jpg",
      "https://cdn.genviral.com/slides/look-02.jpg",
      "https://cdn.genviral.com/slides/look-03.jpg"
    ]
  },
  "music_url": "https://www.tiktok.com/@genviral/video/9876543210",
  "accounts": [
    { "id": "3e30c8d1-0bc0-42d8-9a1a-bb63fe6c3c16" }
  ],
  "scheduled_at": "2025-02-14T18:30:00Z",
  "external_id": "carousel-campaign-12"
}'
{
  "ok": true,
  "code": 201,
  "message": "Post scheduled",
  "data": {
    "id": "11111111-1111-1111-1111-111111111111",
    "status": "scheduled",
    "scheduled_at": "2025-02-01T15:00:00Z",
    "warnings": [
      {
        "field": "media",
        "message": "Video duration metadata is missing",
        "code": "VIDEO_DURATION_MISSING"
      }
    ]
  }
}
Responses may include a warnings array when media metadata (size/duration/aspect) is missing. These are informational so you can decide whether to re-upload media before Hosted Accounts enforce limits.

Error Responses

  • 400 invalid_json - body is not valid JSON
  • 422 invalid_payload - schema validation failed (caption/media/accounts)
  • 400 unknown_accounts - at least one account.id is outside the authenticated key scope
  • 400 validation_failed - caption/media/music rules failed (TikTok-only music, bad aspect ratio, etc.)
  • 400 missing_accounts - resolved account array is empty after validation
  • 400 invalid_music_url or 400 media_unreachable - media or music URLs failed reachability checks
  • 401 - authentication failed (missing/invalid/revoked token)
  • 402 subscription_required - active Creator/Professional/Business plan required
  • 403 tier_not_allowed - Scheduler tier cannot use Partner API
  • 500 create_failed - upstream scheduler error (retry after inspecting logs)