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": "post_789",
    "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 for multiple accounts. Posts are stored with source="partner_api" and appear in the dashboard queue immediately.

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 automatically ignores it when any Instagram account is targeted.
accounts
array
required
Up to 10 account objects referencing IDs returned from /accounts.All IDs must belong to the authenticated workspace. Unknown or inactive accounts cause the request to fail.
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.

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 (or null when queued immediately)
  • 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": "post_789",
    "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 not part of the workspace
  • 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
  • 500 create_failed — upstream scheduler error (retry after inspecting logs)