# Add Folder Items Source: https://docs.genviral.io/api-reference/add-folder-items POST /api/partner/v1/folders/{folderId}/items Add files or slideshows to a folder Adds one or more in-scope file or slideshow IDs to a folder. Duplicate IDs in the request are deduped server-side. ## Path Parameters Folder UUID. ## Body Parameters UUIDs of files or slideshows to add (`1-200` items). ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/items \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "item_ids": [ "11111111-1111-1111-1111-111111111111", "22222222-2222-2222-2222-222222222222" ] }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder items added", "data": { "folder_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "item_type": "file", "added_count": 2 } } ``` ## Error Responses * `400 invalid_json` - malformed JSON body * `422 invalid_path` - `folderId` is not a valid UUID * `422 invalid_payload` - missing or invalid `item_ids` * `404 not_found` - folder not visible in key scope * `404 item_not_found` - one or more item IDs are not visible in key scope * `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 folder_items_add_failed` - unexpected failure # Add Pack Image Source: https://docs.genviral.io/api-reference/add-pack-image POST /api/partner/v1/packs/{packId}/images Add one image URL reference to a pack Adds one image URL to a pack you can mutate. ## Behavior * Stores the provided `image_url` and inferred file metadata on the pack. * Queues AI image metadata enrichment (`description`, `keywords`) for async processing. * Does not download, fetch, or re-host the remote file during this call. * If you only have local file bytes, use [Upload File](/api-reference/upload-file) first and then pass the returned `url` as `image_url`. `image_url` should still be reachable when slideshow generation/render runs. Temporary or private URLs can fail later. ## Path Parameters Pack UUID. ## Body Parameters Absolute image URL. Optional display filename (`1-255` chars). Default: `"image"`. The API infers `content_type` from URL extension (`.png`, `.gif`, `.webp`, `.heic`, `.heif`, `.avif`) and defaults to `image/jpeg` when unknown. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/packs/11111111-1111-1111-1111-111111111111/images \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "image_url": "https://cdn.example.com/packs/motivation/03.jpg", "file_name": "03.jpg" }' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Pack image added", "data": { "id": "44444444-4444-4444-4444-444444444444", "url": "https://cdn.example.com/packs/motivation/03.jpg", "file_name": "03.jpg", "content_type": "image/jpeg", "metadata": { "status": "pending", "description": null, "keywords": [], "model": "gpt-4.1-nano", "generated_at": null, "error": null } } } ``` ## Error Responses * `400 missing_pack_id` - path param missing * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed * `404 not_found` - pack is missing/outside mutable key scope * `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 pack_image_add_failed` - unexpected insertion failure # Import TikTok Slideshow Copy Source: https://docs.genviral.io/api-reference/copy-tiktok-import POST /api/partner/v1/slideshows/copy-tiktok/import Import a TikTok slideshow into an editable Genviral draft with OCR-mapped text overlays and your selected pack images. Imports TikTok slideshow content into a new draft slideshow: * pulls TikTok slideshow slides (max `8`) * extracts text overlays with vision OCR * maps extracted text into editable text elements * creates a Genviral draft slideshow using your pack images as backgrounds * deducts copy credits and auto-refunds on failure ## Body Parameters TikTok slideshow URL. Optional preview token returned by [Preview TikTok Slideshow Copy](/api-reference/copy-tiktok-preview). When present and not expired, import reuses cached preview data. Optional slideshow title override (max 200 chars). Optional image pack ID to source background images. Optional direct list of image URLs for backgrounds. Optional product reference to attach to the created slideshow. Provide exactly one of `pack_id` or `pack_images`. ## Example ```json theme={null} { "url": "https://www.tiktok.com/@creator/photo/7499123456789012345", "preview_id": "a8d34c8b-6f31-4f04-8b62-6f01708af952", "pack_id": "11111111-1111-1111-1111-111111111111", "title": "Copied Hook Variant" } ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "TikTok slideshow imported", "data": { "slideshow": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "title": "Copied Hook Variant", "status": "draft", "slide_count": 4 }, "source_url": "https://www.tiktok.com/@creator/photo/7499123456789012345", "slide_count": 4, "total_slides": 6, "capped": true, "credits_used": 2, "used_cached_preview": true } } ``` ## Error Responses * `400 invalid_json` - request body is not valid JSON * `422 invalid_payload` - payload validation failed * `422 invalid_tiktok_url` - URL is not valid TikTok * `422 not_slideshow` - TikTok post is not a slideshow/photo post * `404 pack_not_found` * `422 pack_empty` * `422 pack_source_conflict` - both `pack_id` and `pack_images` were sent * `404 product_not_found` * `403 forbidden_product_access` * `402 insufficient_credits` * `429 rate_limited` * `502 tiktok_fetch_failed` * `500 upstream_config_error` - TikTok upstream credentials/config missing * `500 import_failed` # Preview TikTok Slideshow Copy Source: https://docs.genviral.io/api-reference/copy-tiktok-preview POST /api/partner/v1/slideshows/copy-tiktok/preview Fetch TikTok slideshow metadata + image URLs before importing into an editable Genviral slideshow. Use this endpoint to validate a TikTok slideshow URL and preview import inputs before spending credits. The preview is cached server-side (10 minutes) and returns `preview_id` so the follow-up import call can skip re-fetching upstream TikTok data. ## Body Parameters TikTok slideshow URL. Supports standard and short TikTok links. ## Example ```json theme={null} { "url": "https://www.tiktok.com/@creator/photo/7499123456789012345" } ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "TikTok slideshow preview ready", "data": { "preview_id": "a8d34c8b-6f31-4f04-8b62-6f01708af952", "source_url": "https://www.tiktok.com/@creator/photo/7499123456789012345", "images": [ "https://p16-sign.tiktokcdn.com/.../slide-1.jpeg", "https://p16-sign.tiktokcdn.com/.../slide-2.jpeg" ], "caption": "5 hooks that always convert", "author": "creator", "slide_count": 2, "total_slides": 2, "capped": false, "estimated_credits": 1 } } ``` `slide_count` is the number of slides that will be imported (max `8`). If TikTok has more slides, `capped=true` and `total_slides` shows the original count. ## Error Responses * `400 invalid_json` - request body is not valid JSON * `422 invalid_payload` - payload is missing required fields * `422 invalid_tiktok_url` - URL is not a valid TikTok link * `422 not_slideshow` - TikTok URL is valid but not a slideshow/photo post * `429 rate_limited` * `502 tiktok_fetch_failed` - upstream TikTok fetch failed * `500 upstream_config_error` - TikTok upstream credentials/config missing * `500 preview_failed` # Create Analytics Target Source: https://docs.genviral.io/api-reference/create-analytics-target POST /api/partner/v1/analytics/targets Add a tracked analytics account Creates a tracked analytics target in the authenticated key scope. ## Body Parameters Supported values: `tiktok`, `instagram`, `youtube`. Account identifier/handle to track (for example `@brand`). Optional display name override. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/analytics/targets \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "platform": "tiktok", "identifier": "@brand", "alias": "Brand HQ" }' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Analytics target created", "data": { "target": { "id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "user_id": "7f026314-7657-4b0f-bb45-8fd4c0b95fd7", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "platform": "tiktok", "identifier": "@brand", "normalized_identifier": "brand", "display_name": "Brand HQ", "last_refreshed_at": null }, "created": true } } ``` ## Error Responses * `400 invalid_json` - request body is not valid JSON * `400 invalid_payload` - body must be a JSON object * `400` - unsupported platform or missing identifier * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected database/analytics service error All error responses from analytics endpoints include an `error_code` field. # Create Folder Source: https://docs.genviral.io/api-reference/create-folder POST /api/partner/v1/folders Create a folder in the authenticated key scope Creates a folder for one media type. Use `parent_folder_id` to create nested folders. ## Body Parameters Folder name (`1-255` chars). Folder media type: `ai_image`, `ai_video`, `upload`, or `slideshow`. Optional parent folder ID for nesting. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/folders \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "March Campaign", "media_type": "upload", "parent_folder_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" }' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Folder created", "data": { "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "name": "March Campaign", "media_type": "upload", "parent_folder_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "file_count": 0, "subfolder_count": 0, "preview_files": [] } } ``` ## Error Responses * `400 invalid_json` - malformed JSON body * `404 parent_folder_not_found` - `parent_folder_id` not visible in key scope * `409 folder_name_conflict` - duplicate folder name at destination * `422 invalid_payload` - body validation failed * `500 folder_create_failed` - unexpected create failure # Create Pack Source: https://docs.genviral.io/api-reference/create-pack POST /api/partner/v1/packs Create a reusable image pack Creates a new pack in the authenticated key scope. This endpoint creates an empty pack record (metadata only). Add images with [Add Pack Image](/api-reference/add-pack-image). ## Body Parameters Pack name (`1-120` chars after trimming). Optional visibility flag. Default: `false`. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/packs \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Motivation Pack", "is_public": false }' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Pack created", "data": { "id": "11111111-1111-1111-1111-111111111111", "name": "Motivation Pack", "image_count": 0, "is_public": false, "created_at": "2026-02-14T09:00:00.000Z", "images": [] } } ``` ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed * `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 pack_create_failed` - unexpected creation failure # Create Post Source: https://docs.genviral.io/api-reference/create-post POST /api/partner/v1/posts Schedule or publish text-only, video, or slideshow posts to connected accounts. Schedule or publish immediately to one or more connected accounts. ## Before you post 1. Use an API key for a workspace with an active **Creator, Professional, or Business** subscription. 2. Call [`GET /accounts`](/api-reference/get-accounts) and pick account UUIDs. 3. Check each account's `capabilities.supported_content_kinds` and `caption_limit`. 4. Build the payload below. Only include provider settings for platforms you are actually targeting. Omit `media` or set it to `null` only when **every** selected account supports `text_only`. ## Request body Shared caption text. Final length is checked per targeted account after lookup (hosted accounts: 500 chars max). One to ten account objects: `[{ "id": "" }]`. Optional. `video`, `slideshow`, or `null`/omit for text-only posts. `video` Direct MP4/MOV/M4V/AVI URL. Google Drive links work. Duration in seconds. Also accepts `duration_seconds`, `duration`, `durationSec`, or `video_duration_sec`. **Required** for non-premium X video posts. Optional size in bytes. Also accepts `size`. Optional width in pixels. Optional height in pixels. Optional MIME type. Alias: `mime`. Optional nested object that can repeat `width`, `height`, `mime_type`, and `bytes`. `slideshow` 1–35 JPG/JPEG/PNG image URLs, in order. ISO 8601 datetime **with timezone offset** (for example `2026-05-21T15:00:00Z`). Omit or send a time within 30 seconds of now for immediate publish (`status: pending`). For true scheduled posts, the time must be **at least 2 minutes** in the future. Recommended idempotency key (max 128 chars). Same key + same payload replays the original post (`200`, `duplicate: true`). Same key + different payload returns `409`. If omitted, the API dedupes from a SHA-256 fingerprint of the request body instead. TikTok-only. A TikTok post URL used to resolve the track. Rejected when any non-TikTok account is selected, including Instagram. ## Provider settings Use **`settings.`** for X, Instagram, Facebook, LinkedIn, Reddit, Threads, Bluesky, and YouTube. For TikTok and Pinterest on **create**, use the top-level **`tiktok`** and **`pinterest`** objects — not `settings.tiktok` or `settings.pinterest`. Top-level fields run the full create-time validation (BYO-only TikTok, hosted TikTok rejection, `MEDIA_UPLOAD` rules, and field limits). Only send settings for providers present in your `accounts` list. Unknown `settings` keys return `422 invalid_payload`. `capabilities.settings_schema` from [`GET /accounts`](/api-reference/get-accounts) reflects the dashboard composer, not always the Partner API field names. For YouTube, TikTok, and Pinterest, use the tables below — not `settings_schema` — when building create payloads. ### YouTube — `settings.youtube` Requires a **video** post to a YouTube account. | Field | Type | Required | Notes | | ------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------- | | `title` | string | No | Max 100 chars. If omitted, derived from `settings.youtube.description`, then `caption` (first non-empty line). | | `description` | string | No | Overrides `caption` as the YouTube description when set. | | `type` | string | No | Visibility: `public`, `private`, or `unlisted`. Default `public`. Alias: `privacy`. | | `privacy` | string | No | Same as `type`. | | `tags` | array | No | `{ "value": "...", "label": "..." }` items. Total tag length capped at publish time. | ### TikTok — top-level `tiktok` Requires **BYO** TikTok accounts and media (video or slideshow). Hosted TikTok accounts cannot use TikTok-specific settings. For **video** posts, the merged publish text built from `caption`, `tiktok.title`, and `tiktok.description` must stay **≤ 2,200 characters combined**. | Field | Type | Notes | | -------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- | | `title` | string | Video: max 2,200 chars. Slideshow: max 90 chars. | | `description` | string | Slideshow: max 4,000 chars. Video: used as the publish body when `caption` is empty. | | `post_mode` | string | `DIRECT_POST` or `MEDIA_UPLOAD` (slideshow drafts only). Defaults to `DIRECT_POST` at publish time when omitted. | | `privacy_level` | string | `PUBLIC_TO_EVERYONE`, `MUTUAL_FOLLOW_FRIENDS`, `FOLLOWER_OF_CREATOR`, `SELF_ONLY`. | | `disable_comment` | boolean | Optional comment toggle. | | `disable_duet` | boolean | Video only. | | `disable_stitch` | boolean | Video only. | | `video_cover_timestamp_ms` | integer | Video + `DIRECT_POST` only. Thumbnail frame offset in ms. | | `auto_add_music` | boolean | Slideshow only. | | `user_consent` | boolean | TikTok policy consent. | | `is_commercial` | boolean | Commercial content flag. | | `is_your_brand` | boolean | Own-brand flag. | | `is_branded_content` | boolean | Third-party promotion flag. | ### Pinterest — top-level `pinterest` Requires at least one Pinterest account and image media. | Field | Type | Notes | | ---------- | --------- | -------------------------------------------------------------------------------------------------------------------------------- | | `board_id` | string | Pinterest board ID. Required for a successful pin publish — the API does not apply account defaults when omitted. Max 128 chars. | | `title` | string | Optional pin title. Max 100 chars. | | `link` | string | Optional destination URL. Max 2,048 chars. | | `tags` | string\[] | Up to 30 tags, 1–100 chars each. Appended to the pin description; total description must stay ≤ 800 chars. | ### Other platforms — `settings.` Supported keys: `x`, `twitter`, `instagram`, `instagram-standalone`, `facebook`, `linkedin`, `linkedin-page`, `reddit`, `threads`, `bluesky`. Example for X: ```json theme={null} "settings": { "x": { "who_can_reply_post": "everyone", "made_with_ai": false, "paid_partnership": false } } ``` ## Limits (quick reference) * **Video:** MP4/MOV/M4V/AVI, under 100MB. Hosted accounts: 15–60s when duration is provided. * **Non-premium X video:** duration metadata required; max 140 seconds. * **Slideshow:** 1–35 images, each under 5MB. * **Caption caps (BYO):** X 280 (4,000 for premium accounts); Instagram 2,200; TikTok video 2,200 / photo description 4,000; LinkedIn 3,000; Pinterest 800; YouTube 5,000 bytes; Facebook 63,206. ## Response Fresh creates return `201`. Idempotent replays return `200`. ```json theme={null} { "ok": true, "code": 201, "message": "Post scheduled", "data": { "id": "11111111-1111-1111-1111-111111111111", "status": "scheduled", "scheduled_at": "2026-05-21T15:00:00Z", "warnings": [] } } ``` `duplicate: true` appears on idempotent replays. `warnings` are non-blocking (for example missing video duration metadata). ## Examples ### Video to YouTube ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/posts \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "caption": "Weekly product recap and what shipped this sprint.", "media": { "type": "video", "url": "https://cdn.example.com/recap.mp4", "duration_sec": 42 }, "accounts": [{ "id": "YOUR_YOUTUBE_ACCOUNT_ID" }], "settings": { "youtube": { "title": "Weekly product recap", "type": "public" } }, "external_id": "yt-recap-2026-05-21" }' ``` ### TikTok slideshow draft ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/posts \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "caption": "3 outfits for the long weekend", "media": { "type": "slideshow", "urls": [ "https://cdn.example.com/look-1.jpg", "https://cdn.example.com/look-2.jpg", "https://cdn.example.com/look-3.jpg" ] }, "accounts": [{ "id": "YOUR_TIKTOK_BYO_ACCOUNT_ID" }], "tiktok": { "post_mode": "MEDIA_UPLOAD", "privacy_level": "SELF_ONLY" }, "external_id": "tiktok-draft-001" }' ``` ### Pinterest pin ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/posts \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "caption": "Minimal kitchen organization ideas that actually stick.", "media": { "type": "slideshow", "urls": ["https://cdn.example.com/pin-cover.jpg"] }, "accounts": [{ "id": "YOUR_PINTEREST_ACCOUNT_ID" }], "pinterest": { "board_id": "YOUR_BOARD_ID", "link": "https://example.com/kitchen-guide", "tags": ["kitchen organization", "home tips"] }, "external_id": "pinterest-pin-001" }' ``` ### Text-only X post ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/posts \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "caption": "Ship the repeatable thing, not the busywork.", "accounts": [{ "id": "YOUR_X_ACCOUNT_ID" }], "media": null, "settings": { "x": { "who_can_reply_post": "everyone" } } }' ``` ## Errors | Code | When | | --------------------------- | ------------------------------------------------------------------------------------------------------------------ | | `422 invalid_payload` | Schema validation failed (includes `issues` array) | | `400 validation_failed` | Caption, media, music, TikTok/Pinterest, or schedule rules failed | | `400 unknown_accounts` | Account ID outside key scope | | `400 media_unreachable` | Media URL not reachable | | `400 invalid_music_url` | Invalid or non-TikTok music URL | | `401` | Missing or invalid API key | | `402 subscription_required` | No active Creator, Professional, or Business subscription | | `403 subscription_required` | Hosted TikTok posting without an active TikTok virtual subscription | | `403 tier_not_allowed` | Scheduler tier blocked | | `409 create_failed` | Same idempotency key reused with a different payload (`error_code` is `create_failed`, not `external_id_conflict`) | | `500 create_failed` | Unexpected upstream failure | # Create Template Source: https://docs.genviral.io/api-reference/create-template POST /api/partner/v1/templates Create a reusable slideshow template Creates a template with a validated `config` object. ## Body Parameters Template name (`1-100` chars after trimming). Must be unique per owner. Optional description (max `500` chars). Optional: `private` or `workspace`. Default: `private`. Versioned slideshow template config. Must be `1`. `1-10` slides. Slide type is `pack` or `custom`. Optional map: slide index (string key) -> pack UUID. Includes `mode` (`ai` or `manual`), optional `prompt`, `productId`, `language`, and `slideshowType` (`educational` or `personal`). Includes `packId`, `aspectRatio` (`9:16` / `1:1` / `4:5`), and `textSettings` (`fontSize`, `preset`, `width`, optional `fontFamily`). `visibility: "workspace"` requires a workspace-scoped key. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/templates \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Hooks Template", "description": "2-slide educational format", "visibility": "private", "config": { "version": 1, "structure": { "slides": [ { "type": "pack", "id": "11111111-1111-1111-1111-111111111111", "pinnedImageUrl": null, "textElements": [ { "id": "22222222-2222-2222-2222-222222222222", "content": "Hook", "x": 50, "y": 25, "fontSize": 48, "width": 70 } ] } ] }, "content": { "mode": "manual", "productId": null, "language": "en", "slideshowType": "educational" }, "visuals": { "packId": null, "aspectRatio": "4:5", "textSettings": { "fontSize": "default", "preset": "tiktok", "width": "default" } } } }' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Template created", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Hooks Template", "description": "2-slide educational format", "visibility": "private", "preview_url": null, "use_count": 0, "last_used_at": null, "created_at": "2026-02-14T09:45:00.000Z", "updated_at": "2026-02-14T09:45:00.000Z", "is_owner": true, "config": { "version": 1, "structure": { "slides": [ { "type": "pack", "id": "11111111-1111-1111-1111-111111111111", "pinnedImageUrl": null, "textElements": [ { "id": "22222222-2222-2222-2222-222222222222", "content": "Hook", "x": 50, "y": 25, "fontSize": 48, "width": 70 } ] } ] }, "content": { "mode": "manual", "productId": null, "language": "en", "slideshowType": "educational" }, "visuals": { "packId": null, "aspectRatio": "4:5", "textSettings": { "fontSize": "default", "preset": "tiktok", "width": "default" } } } } } ``` ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed * `409 template_name_conflict` - name already exists for owner * `422 invalid_template_config` - config failed server-side config validation * `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 template_create_failed` - unexpected creation failure # Create Template From Slideshow Source: https://docs.genviral.io/api-reference/create-template-from-slideshow POST /api/partner/v1/templates/from-slideshow/{slideshowId} Convert an existing slideshow into a reusable template Builds a template from an existing slideshow in key scope. ## Path Parameters Source slideshow UUID. ## Body Parameters Optional template name (`1-100` chars). If omitted, API auto-generates one. Optional description (`<=500` chars). Optional: `private` or `workspace`. Default: `private`. Optional. Default: `true`. Controls whether text is kept in converted config. Empty body is valid (`{}` behavior). The API will generate a name and apply defaults. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/templates/from-slideshow/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0 \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Converted Winner", "description": "Built from a high-performing slideshow", "visibility": "workspace", "preserve_text": true }' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Template created", "data": { "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "name": "Converted Winner", "description": "Built from a high-performing slideshow", "visibility": "workspace", "preview_url": null, "use_count": 0, "last_used_at": null, "created_at": "2026-02-14T10:05:00.000Z", "updated_at": "2026-02-14T10:05:00.000Z", "is_owner": true, "config": { "version": 1, "structure": { "slides": [ { "type": "pack", "id": "11111111-1111-1111-1111-111111111111", "pinnedImageUrl": null, "textElements": [ { "id": "22222222-2222-2222-2222-222222222222", "content": "Hook text", "x": 50, "y": 25, "fontSize": 48, "width": 70 } ] } ] }, "content": { "mode": "manual", "productId": null, "language": "en", "slideshowType": "educational" }, "visuals": { "packId": null, "aspectRatio": "4:5", "textSettings": { "fontSize": "default", "preset": "tiktok", "width": "default" } } } } } ``` ## Error Responses * `400 missing_slideshow_id` - path param missing * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed * `404 slideshow_not_found` - source slideshow not found in key scope * `409 template_name_conflict` - name already exists for owner * `422 invalid_template_config` - generated/normalized config failed validation * `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 template_create_failed` - unexpected conversion failure # Data Structures Source: https://docs.genviral.io/api-reference/data-structures Canonical data models for accounts, capabilities, posts, slideshows, analytics, and more returned by the Genviral social media API. > Canonical objects returned by the Genviral Partner API. These structures are designed for easy consumption by OpenClaw agents, AI automation pipelines, and custom integrations. ## Partner API Key Represents the credential you create in **API Keys**. Every key is single-scope: either `workspace` or `personal`. | Field | Type | Description | | --------------- | ------------------- | -------------------------------------------------------------- | | `public_id` | `string` | Short identifier (e.g., `gva_live_2ff93c`). | | `secret_suffix` | `string` | Last four characters of the secret portion, useful for audits. | | `scope` | `string` | `workspace` or `personal`. | | `workspace_id` | `string \| null` | Present for workspace keys. | | `owner_user_id` | `string \| null` | Present for personal keys. | | `is_active` | `boolean` | Key active state. | | `created_at` | `string (ISO 8601)` | Timestamp when the key was issued. | | `last_used_at` | `string \| null` | Updated whenever the key is used. | | `revoked_at` | `string \| null` | Timestamp when key was revoked. | ```json theme={null} { "public_id": "gva_live_2ff93c", "secret_suffix": "b4d5", "scope": "personal", "workspace_id": null, "owner_user_id": "5ec8f2fd-8d0a-4c68-b05d-9a8a85aa5d5d", "is_active": true, "created_at": "2026-02-13T15:32:11.402Z", "last_used_at": "2026-02-13T18:14:03.009Z", "revoked_at": null } ``` ## Social Account (BYO + Hosted) Returned by `GET /accounts` and referenced when scheduling posts. Account IDs are UUIDs. | Field | Type | Description | | -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | | `id` | `string (UUID)` | Scope-constrained account identifier. | | `platform` | `string` | `tiktok`, `instagram`, etc. | | `type` | `string` | `byo` (user-connected) or `hosted` (Genviral-managed). | | `username` | `string` | Handle shown on the platform. | | `display_name` | `string` | Friendly label surfaced in the dashboard. | | `status` | `string` | `active`, `paused`, `revoked`, etc. | | `workspace_id` | `string \| null` | Workspace owner when account is workspace-scoped; `null` in personal scope. | | `capabilities` | `object` | Per-account supported content kinds, caption/media limits, unsupported reasons, and provider settings JSON Schema. | ```json theme={null} { "id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "platform": "tiktok", "type": "hosted", "username": "ugc_trends_42", "display_name": "Hosted Trends 42", "status": "active", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "capabilities": { "supported_content_kinds": ["single_image", "multi_image", "single_video"], "caption_limit": 500, "media_limits": { "max_images": 35, "max_videos": 1 }, "unsupported_content_kind_reasons": { "text_only": "Text-only posts are only available for connected BYO accounts; hosted accounts require media." }, "settings_schema": { "type": "object", "additionalProperties": false, "properties": {} } } } ``` ### Account Capabilities Returned under `account.capabilities` by `GET /accounts`. | Field | Type | Description | | ---------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------- | | `supported_content_kinds` | `string[]` | Content kinds this account supports through Genviral: `text_only`, `single_image`, `multi_image`, `single_video`. | | `caption_limit` | `number` | Account-aware caption limit. | | `media_limits.max_images` | `number` | Maximum image count accepted for the account/platform. | | `media_limits.max_videos` | `number` | Maximum video count accepted for the account/platform. | | `media_limits.max_video_seconds` | `number` | Optional max video duration when the platform/account policy needs it. | | `unsupported_content_kind_reasons` | `object` | Reason strings keyed by unsupported content kind. | | `settings_schema` | `object` | JSON Schema for provider settings under `settings.`. | ## Post Created via `POST /posts`, retrieved via `GET /posts` and `GET /posts/{id}`. | Field | Type | Description | | -------------- | ---------------- | ---------------------------------------------------------------------------------------------------------- | | `id` | `string (UUID)` | Unique post ID. | | `status` | `string` | `draft`, `pending`, `scheduled`, `retry`, `posted`, `failed`, `partial`, `canceled`, `deleted`, `expired`. | | `caption` | `string` | Cross-platform caption. | | `scheduled_at` | `string \| null` | Target publish timestamp. | | `created_at` | `string` | ISO timestamp when the post was created. | | `accounts` | `object` | Summary + per-account states (see below). | | `media` | `object \| null` | Mirrors the create/update payload (`type` + `url`/`urls`), or `null` for text-only posts. | | `music_url` | `string \| null` | TikTok post URL applied to the TikTok audio track. Not supported for Instagram targets. | | `tiktok` | `object \| null` | Optional TikTok publish settings (only for TikTok BYO-targeted posts). | | `pinterest` | `object \| null` | Optional Pinterest settings (`board_id`, `link`, `title`, `tags`). | ```json theme={null} { "id": "11111111-1111-1111-1111-111111111111", "caption": "Holiday drops start Monday!", "status": "scheduled", "scheduled_at": "2025-02-01T15:00:00Z", "created_at": "2025-01-20T12:05:11.205Z", "tiktok": { "post_mode": "MEDIA_UPLOAD", "privacy_level": "PUBLIC_TO_EVERYONE" } } ``` Top-level `tiktok` and `pinterest` settings are v1 convenience aliases. New multi-platform clients should prefer canonical provider settings under `settings.` in create/update requests. ### Media Returned as submitted to `POST /posts` and `PATCH /posts/{id}`, or `null` for text-only posts: | Field | Type | Description | | ------ | ---------- | --------------------------------------------------------------------------- | | `type` | `string` | `video` or `slideshow`. Determines which property appears below. | | `url` | `string` | Present when `type="video"`. Points to the ingested/validated clip. | | `urls` | `string[]` | Present when `type="slideshow"`. Ordered list of image URLs (1–35 entries). | When BYO accounts are present, Genviral re-hosts the assets behind the scenes so these URLs point to first-party Cloudflare R2 buckets. Hosted-only posts reuse the URLs you provided once reachability is confirmed. ### TikTok Settings Optional settings persisted from `POST /posts` / `PATCH /posts/{id}` under the `tiktok` field. These apply only when all targeted accounts are TikTok BYO accounts. | Field | Type | Description | | -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `title` | `string` | Optional TikTok title override. Video posts: max 2,200 UTF-16 runes. Photo/slideshow posts: max 90 UTF-16 runes. | | `description` | `string` | Optional TikTok description override. Photo/slideshow posts: max 4,000 UTF-16 runes. For video posts, Genviral keeps this field for compatibility and can use it as a fallback source when deriving the TikTok title if `title` is blank. | | `post_mode` | `string` | `DIRECT_POST` or `MEDIA_UPLOAD` (TikTok inbox/drafts). | | `privacy_level` | `string` | TikTok privacy level (`PUBLIC_TO_EVERYONE`, `MUTUAL_FOLLOW_FRIENDS`, `FOLLOWER_OF_CREATOR`, `SELF_ONLY`). | | `disable_comment` | `boolean` | Optional comment toggle. | | `disable_duet` | `boolean` | Optional duet toggle (video posts). | | `disable_stitch` | `boolean` | Optional stitch toggle (video posts). | | `video_cover_timestamp_ms` | `integer` | Optional video cover frame offset in milliseconds (`DIRECT_POST` video only). | | `user_consent` | `boolean` | Optional policy consent flag. | | `is_commercial` | `boolean` | Optional commercial content flag. | | `is_your_brand` | `boolean` | Optional own-brand flag. | | `is_branded_content` | `boolean` | Optional third-party branded-content flag. | | `auto_add_music` | `boolean` | Optional auto-music toggle (photo posts). | ### Pinterest Settings Optional settings persisted from `POST /posts` / `PATCH /posts/{id}` under the `pinterest` field. These apply only when at least one targeted account is Pinterest. | Field | Type | Description | | ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `board_id` | `string` | Optional Pinterest board ID (max 128 chars). | | `title` | `string` | Optional pin title override (max 100 chars). | | `link` | `string` | Optional destination URL attached to the pin (max 2,048 chars). | | `tags` | `string[]` | Optional topic tags (up to 30, spaces allowed). Genviral appends these to the final Pinterest description, which still must fit Pinterest's 800-character description ceiling. | ### Account States Each post stores a normalized state for every targeted account (from `social_post_accounts`): | Field | Type | Description | | ------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------- | | `account_id` | `string` | ID from `GET /accounts`. | | `status` | `string` | One of `draft`, `pending`, `scheduled`, `retry`, `posted`, `failed`, `partial`, `canceled`, `deleted`, `expired`. | | `published_at` | `string \| null` | Timestamp of successful publish. | | `published_url` | `string \| null` | Platform URL when the provider shares one (hosted creators). | | `error_message` | `string \| null` | Last error returned by the platform. | | `last_attempted_at` | `string \| null` | When the scheduler last touched this account. | ```json theme={null} { "accounts": { "total": 2, "states": [ { "account_id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "status": "scheduled", "published_at": null, "published_url": null, "error_message": null, "last_attempted_at": null }, { "account_id": "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089", "status": "scheduled", "published_at": null, "published_url": null, "error_message": null, "last_attempted_at": null } ] } } ``` ## Folder Returned by folder endpoints under `/api/partner/v1/folders*`. | Field | Type | Description | | ------------------ | ---------------- | ---------------------------------------------------- | | `id` | `string (UUID)` | Folder ID. | | `name` | `string` | Folder display name. | | `media_type` | `string` | `ai_image`, `ai_video`, `upload`, or `slideshow`. | | `parent_folder_id` | `string \| null` | Parent folder ID (`null` for root). | | `path` | `string` | Materialized folder path. | | `file_count` | `number` | Count of direct items in the folder. | | `subfolder_count` | `number` | Count of direct child folders. | | `preview_files` | `array` | Up to 4 preview items (`id`, `url`, `content_type`). | | `created_at` | `string` | Creation timestamp. | | `updated_at` | `string` | Last update timestamp. | ```json theme={null} { "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "name": "March Campaign", "media_type": "upload", "parent_folder_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "path": "/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "file_count": 8, "subfolder_count": 1, "preview_files": [ { "id": "11111111-1111-1111-1111-111111111111", "url": "https://cdn.vireel.io/partner-api/workspaces/workspace_123/asset-1.jpg", "content_type": "image/jpeg" } ], "created_at": "2026-03-01T09:00:00Z", "updated_at": "2026-03-01T09:10:00Z" } ``` ## Stats Snapshot Analytics endpoints (and UI exports) reuse the same structures outlined in `other/docs/product/integrations/ugcinc/data-structures/account-stat.md` and `post-stat.md`. | Field | Type | Description | | -------------------------- | ---------------- | ----------------------------------------------- | | `account_stats.id` | `string` | Unique stat row. | | `account_stats.account_id` | `string` | Links back to the account in current key scope. | | `account_stats.followers` | `number \| null` | Followers at the time of capture. | | `account_stats.views` | `number \| null` | Aggregated views across all posts. | | `post_stats.id` | `string` | Unique per-post stat row. | | `post_stats.post_id` | `string` | Links to the Genviral post. | | `post_stats.views` | `number \| null` | Views for the specific post. | | `post_stats.likes` | `number \| null` | Likes for the specific post. | | `created_at` | `string` | Snapshot timestamp. | ```json theme={null} { "account_stats": { "id": "acc_stat_1", "account_id": "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089", "followers": 15420, "views": 1250000, "likes": 89500, "created_at": "2025-01-19T10:00:00Z" }, "post_stats": { "id": "post_stat_1", "post_id": "11111111-1111-1111-1111-111111111111", "views": 48200, "likes": 3200, "created_at": "2025-01-21T04:00:00Z" } } ``` ## Analytics Summary Returned by `GET /api/partner/v1/analytics/summary`. | Field | Type | Description | | --------------------- | ---------- | ------------------------------------------------------------------ | | `range.start` | `string` | Start date (`YYYY-MM-DD`) of selected range. | | `range.end` | `string` | End date (`YYYY-MM-DD`) of selected range. | | `filters.platforms` | `string[]` | Applied platform filters. | | `filters.accountIds` | `string[]` | Applied analytics target ID filters. | | `kpis..value` | `number` | Current-period value for metric. | | `kpis..delta` | `number` | Difference from previous equivalent period. | | `interactionSeries[]` | `object[]` | Daily interactions (`views`, `likes`, `comments`, `shares`). | | `engagementSeries[]` | `object[]` | Daily engagement-rate values. | | `postingHeatmap[]` | `object[]` | Daily post counts for heatmap rendering. | | `postingStreak` | `number` | Consecutive posting-day streak ending at selected `range.end`. | | `contentMix[]` | `object[]` | Post distribution by platform (`platform`, `posts`, `percentage`). | ## Analytics Target Returned by `GET /api/partner/v1/analytics/targets` and target-management endpoints. | Field | Type | Description | | ------------------------ | ---------------- | ------------------------------------------------------------------------ | | `id` | `string (UUID)` | Canonical analytics target identifier used across analytics endpoints. | | `target_id` | `string (UUID)` | Legacy alias currently included in list rows for backward compatibility. | | `platform` | `string` | `tiktok`, `instagram`, or `youtube`. | | `identifier` | `string` | Original account handle/input. | | `normalized_identifier` | `string` | Normalized identifier used for dedupe. | | `display_name` | `string \| null` | Optional display label. | | `last_refreshed_at` | `string \| null` | Last successful refresh timestamp. | | `last_snapshot_totals` | `object \| null` | Latest aggregate totals from snapshot. | | `last_refresh_status` | `string \| null` | Status from latest refresh row. | | `last_refresh_error` | `string \| null` | Latest refresh error text when failed. | | `free_refresh_available` | `boolean` | Whether free daily refresh is currently available. | ## Analytics Refresh Returned by `POST /api/partner/v1/analytics/targets/{id}/refresh` and `GET /api/partner/v1/analytics/refreshes/{id}`. | Field | Type | Description | | ------------------- | ---------------- | -------------------------------------------------- | | `id` | `string (UUID)` | Refresh ID. | | `target_id` | `string (UUID)` | Target this refresh belongs to. | | `workspace_id` | `string \| null` | Workspace scope for billing/context. | | `status` | `string` | `pending`, `processing`, `completed`, or `failed`. | | `credits_used` | `number` | Credits consumed for this refresh (0 if free). | | `free_refresh_used` | `boolean` | Whether free refresh window was used. | | `started_at` | `string` | Start timestamp. | | `completed_at` | `string \| null` | Completion timestamp when done. | | `error` | `string \| null` | Failure reason when status is `failed`. | When available, `GET /api/partner/v1/analytics/summary/posts` returns both `analyticsId`, `genviralPostId`, and `externalId`. Use `genviralPostId` or `externalId` as the preferred correlation fields for mapping analytics rows back to the originating Partner API post. `analyticsId` and legacy `id` are the analytics-dataset row ID, not the post-creation ID. # Delete Analytics Target Source: https://docs.genviral.io/api-reference/delete-analytics-target DELETE /api/partner/v1/analytics/targets/{id} Delete one tracked analytics target Deletes a tracked analytics target from the authenticated key scope. ## Path Parameters Analytics target ID. ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/analytics/targets/e7a7f417-56db-4320-9e9a-fd6fdabda09d \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics target deleted", "data": { "deleted": true, "target_id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d" } } ``` ## Error Responses * `400 missing_target_id` - missing path parameter * `404` - target not found in current key scope * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected database/analytics service error All error responses from analytics endpoints include an `error_code` field. # Delete Folder Source: https://docs.genviral.io/api-reference/delete-folder DELETE /api/partner/v1/folders/{folderId} Delete a folder in the authenticated key scope Permanently deletes a folder. Nested children and folder items are removed by database cascade. ## Path Parameters Folder UUID. ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder deleted", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "deleted": true } } ``` ## Error Responses * `400 missing_folder_id` - missing route parameter * `422 invalid_path` - `folderId` is not a valid UUID * `404 not_found` - folder not visible in key scope * `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 folder_delete_failed` - unexpected failure # Delete Pack Source: https://docs.genviral.io/api-reference/delete-pack DELETE /api/partner/v1/packs/{packId} Delete a mutable pack in key scope Deletes a pack you can mutate in the authenticated scope. ## Path Parameters Pack UUID. ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/packs/11111111-1111-1111-1111-111111111111 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Pack deleted", "data": { "id": "11111111-1111-1111-1111-111111111111", "deleted": true } } ``` ## Error Responses * `400 missing_pack_id` - path param missing * `404 not_found` - pack is missing/outside mutable key scope * `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 pack_delete_failed` - unexpected delete failure # Delete Pack Image Source: https://docs.genviral.io/api-reference/delete-pack-image DELETE /api/partner/v1/packs/{packId}/images/{imageId} Delete one image from a pack Deletes one image from a mutable pack. ## Path Parameters Pack UUID. Image UUID from the pack `images` list. ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/packs/11111111-1111-1111-1111-111111111111/images/44444444-4444-4444-4444-444444444444 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Pack image deleted", "data": { "id": "44444444-4444-4444-4444-444444444444", "deleted": true } } ``` ## Error Responses * `400 missing_pack_id` - path param missing * `400 missing_image_id` - path param missing * `404 pack_not_found` - pack is missing/outside mutable key scope * `404 image_not_found` - image ID not found in this pack * `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 pack_image_delete_failed` - unexpected delete failure # Delete Posts Source: https://docs.genviral.io/api-reference/delete-post POST /api/partner/v1/posts/delete Delete one or many posts with a single request Delete one or many posts in a single request. The endpoint enforces key scope, cleans up provider-side artifacts, and reports a per-ID result so your integration can surface partial successes. ## Body Parameters Array of post UUIDs (minimum 1, maximum 50). Duplicates are removed before processing. The API accepts both `postIds` (preferred) and the snake\_case alias `post_ids`. All IDs must belong to the authenticated key scope. IDs outside scope are treated as `skipped`. ## Status Guardrails * **Deletable:** `draft`, `pending`, `scheduled`, `retry`, `failed` * **Blocked:** `posted`, `canceled`, `partial` Posts that already finished publishing (`posted`) or that have been canceled / partially published are immutable. They are returned inside `blockedStatuses` so you can surface the exact status that blocked deletion. ## Response IDs that were permanently removed. Order matches the request order after duplicates are stripped. Entries of `{(id, status)}` describing posts that failed the status guardrail. IDs that were not found under key scope (already deleted, outside scope, or never existed). Optional. Keyed by post ID when the database delete fails. A batch can still return `200` with a populated `errors` map. ## Request Example ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/posts/delete \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "postIds": [ "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "c218bffa-96db-4d9b-aa66-2b77797fc8bb", "93f546e4-4fb1-4013-8e40-89a93c789e90" ] }' ``` ## Response Example ```json Response theme={null} { "ok": true, "code": 200, "message": "Posts deleted", "data": { "deletedIds": [ "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "c218bffa-96db-4d9b-aa66-2b77797fc8bb" ], "blockedStatuses": [ { "id": "93f546e4-4fb1-4013-8e40-89a93c789e90", "status": "posted" } ], "skipped": ["ce02a150-0d9a-46d5-a3e4-5a3a1bdacf6f"], "errors": { "52fd0fa4-b19f-4a02-a5a5-5ed78a55ce72": "Failed to delete post" } } } ``` ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - missing/invalid `postIds` * `400 too_many_post_ids` - more than 50 IDs supplied * `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 bulk_delete_failed` - unexpected server error (all attempted IDs are still reported in the response body when possible) # Delete Slideshow Source: https://docs.genviral.io/api-reference/delete-slideshow DELETE /api/partner/v1/slideshows/{slideshowId} Delete a slideshow in authenticated key scope Permanently deletes a slideshow in scope. ## Path Parameters Slideshow UUID. ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/slideshows/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Slideshow deleted", "data": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "deleted": true } } ``` ## Error Responses * `400 missing_slideshow_id` - path param missing * `404 not_found` - slideshow is missing/outside key scope * `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 delete_failed` - unexpected delete failure # Delete Template Source: https://docs.genviral.io/api-reference/delete-template DELETE /api/partner/v1/templates/{templateId} Delete a mutable template Deletes a template you own and can mutate in current scope. ## Path Parameters Template UUID. ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/templates/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Template deleted", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "deleted": true } } ``` ## Error Responses * `400 missing_template_id` - path param missing * `404 not_found` - template not found in readable scope * `403 forbidden_template` - template exists but mutation is not allowed in current scope * `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 template_delete_failed` - unexpected delete failure # Duplicate Slideshow Source: https://docs.genviral.io/api-reference/duplicate-slideshow POST /api/partner/v1/slideshows/{slideshowId}/duplicate Create a draft copy of an existing slideshow Creates a draft copy of a slideshow in the same key scope. ## Path Parameters Source slideshow UUID. Duplicated slideshows are always returned as `draft`, with rendered artifacts cleared on the copy. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/slideshows/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0/duplicate \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Slideshow duplicated", "data": { "id": "f173a6d2-0f9a-4d78-9335-d62b9b862d73", "title": "Copy of 5 discipline quotes", "status": "draft", "preview_image_url": null, "last_rendered_at": null, "slide_count": 5 } } ``` ## Error Responses * `400 missing_slideshow_id` - path param missing * `404 not_found` - slideshow is missing/outside key scope * `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 duplicate_failed` - unexpected duplication failure # Folders Source: https://docs.genviral.io/api-reference/folders Organize partner-scoped files and slideshows in nested folders Use folders to group partner files (`ai_image`, `ai_video`, `upload`) and slideshows (`slideshow`) in the authenticated key scope. ## Endpoints | Operation | Endpoint | Reference | | --------------------- | -------------------------------------------------- | ------------------------------------------------------------- | | List folders | `GET /api/partner/v1/folders` | [List Folders](/api-reference/list-folders) | | Create folder | `POST /api/partner/v1/folders` | [Create Folder](/api-reference/create-folder) | | Get folder | `GET /api/partner/v1/folders/{folderId}` | [Get Folder](/api-reference/get-folder) | | Move folder | `PATCH /api/partner/v1/folders/{folderId}` | [Move Folder](/api-reference/move-folder) | | Delete folder | `DELETE /api/partner/v1/folders/{folderId}` | [Delete Folder](/api-reference/delete-folder) | | List folder ancestors | `GET /api/partner/v1/folders/{folderId}/ancestors` | [List Folder Ancestors](/api-reference/list-folder-ancestors) | | List folder items | `GET /api/partner/v1/folders/{folderId}/items` | [List Folder Items](/api-reference/list-folder-items) | | Add folder items | `POST /api/partner/v1/folders/{folderId}/items` | [Add Folder Items](/api-reference/add-folder-items) | | Remove folder items | `DELETE /api/partner/v1/folders/{folderId}/items` | [Remove Folder Items](/api-reference/remove-folder-items) | ## Notes * Folder visibility is always scope-safe: * workspace key -> only that workspace * personal key -> only personal rows (`workspace_id = null`) * `slideshow` folders accept slideshow IDs. * `ai_image`, `ai_video`, and `upload` folders accept file IDs. # Generate Slideshow Source: https://docs.genviral.io/api-reference/generate-slideshow POST /api/partner/v1/slideshows/generate Generate AI-powered slideshows for TikTok photo carousels, Instagram carousels, Pinterest pins, and more. Supports AI generation from prompts, manual slide configuration, and mixed mode. The core content creation endpoint for OpenClaw agents. Create a new slideshow in the authenticated key scope. This is the core content creation endpoint - your OpenClaw agent or automation script generates slideshows here, then publishes them to media-capable accounts via [Create Post](/api-reference/create-post). `generate` supports: * AI generation from a prompt/product context * manual initial slide setup (`skip_ai=true`) * mixed setup with explicit `slide_config` ## Body Parameters Prompt used for AI text generation. Required unless `skip_ai=true` or `product_id` is provided. Optional product reference. Must exist in the authenticated key scope. Optional global image pack ID. Required whenever any generated slide uses `image_pack` and no per-slide `pack_assignments` are provided. Optional target slide count (`1-10`). Default: `5`. Optional: `educational` or `personal`. Default: `educational`. Optional: `9:16`, `1:1`, or `4:5`. Default: `4:5`. Optional language hint (2-32 chars). Optional text styling defaults. `default`, `small`, or a numeric size (`8-200`). Preset mapping for generated slides: `default` = title/single `48px`, body `40px`; `small` = title/single `40px`, body `33px` (rounded from `0.83x`). If users ask for “a bit bigger than small”, use a numeric midpoint such as `44`. Text style preset (for example `tiktok`). `default` or `narrow`. Optional. When `true`, generation skips AI text creation and uses your `slide_config` input. Optional explicit per-slide setup. Integer `1-10`. Exact-length array matching `total_slides`. Values: `image_pack` or `custom_image`. Map of slide index -> custom image payload. Required for every `custom_image` slide. Optional map of slide index -> pinned image URL. Optional map of slide index -> text string. Optional map of slide index -> text element array (`content`, `x`, `y`, optional `id`, `font_size`, `width`). Optional map of slide index -> pack UUID. Valid only for `image_pack` slides. All `slide_config` map keys must be 0-based numeric indices in range. `image_pack` slides must resolve a pack via `pack_id` or `pack_assignments[index]`. ## Examples ### AI mode (prompt + global pack) ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/slideshows/generate \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "prompt": "5 discipline quotes", "pack_id": "11111111-1111-1111-1111-111111111111", "slide_count": 1, "slideshow_type": "educational", "aspect_ratio": "4:5", "language": "en", "advanced_settings": { "font_size": 44, "text_preset": "tiktok", "text_width": "default" } }' ``` ### Manual mode (`skip_ai=true`) ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/slideshows/generate \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "skip_ai": true, "slide_config": { "total_slides": 2, "slide_types": ["custom_image", "image_pack"], "custom_images": { "0": { "image_url": "https://cdn.example.com/custom-0.jpg", "image_id": "hero_0" } }, "slide_text_elements": { "0": [ { "content": "Hook text", "x": 50, "y": 25 } ] }, "pack_assignments": { "1": "11111111-1111-1111-1111-111111111111" } } }' ``` ## Response Returns `201` with the full slideshow object (same structure as [Get Slideshow](/api-reference/get-slideshow)). ```json Response theme={null} { "ok": true, "code": 201, "message": "Slideshow generated", "data": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "title": "5 discipline quotes", "status": "draft", "slideshow_type": "educational", "product_id": null, "original_prompt": "5 discipline quotes", "preview_image_url": null, "created_at": "2026-02-14T09:20:00.000Z", "updated_at": "2026-02-14T09:20:00.000Z", "last_rendered_at": null, "slide_count": 1, "settings": { "image_pack_id": "11111111-1111-1111-1111-111111111111", "aspect_ratio": "4:5", "slideshow_type": "educational", "advanced_settings": { "font_size": 44, "text_preset": "tiktok", "text_width": "default" }, "pack_assignments": null }, "slides": [ { "index": 0, "image_url": "https://cdn.example.com/slides/discipline-1.jpg", "rendered_image_url": null, "text_elements": [ { "id": "9fd0b2bd-a95a-4488-8b7a-bf1d18d2bcdf", "content": "Discipline beats motivation", "x": 50, "y": 25, "font_size": 48, "width": 70, "height": null, "editable": true, "style_preset": "tiktok", "font_family": null, "background_color": null, "text_color": null, "border_radius": null } ], "grid_images": null, "grid_type": null, "background_filters": null, "image_overlays": null } ] } } ``` ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema/validation failed * `404 pack_not_found` - referenced pack is missing or outside key scope * `422 pack_empty` - selected pack has no images * `422 pack_required` - one or more `image_pack` slides are missing pack resolution * `404 product_not_found` - `product_id` does not exist * `403 forbidden_product_access` - `product_id` exists but is outside key scope * `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 generate_failed` - unexpected generation failure # Generate Studio Image Source: https://docs.genviral.io/api-reference/generate-studio-image POST /api/partner/v1/studio/images/generate Generate an AI Studio image with normalized params and optional model-specific raw passthrough. Generate one image synchronously and return its hosted output URL. ## Body Parameters Image model ID from [Get Studio Models](/api-reference/get-studio-models). Generation prompt. Optional input/reference image URLs. Genviral may mirror public reference URLs to its own CDN before handing them to the upstream model so image-edit requests are less sensitive to third-party hotlinking or fetch-policy issues. Normalized image params. Supported keys: `aspect_ratio`, `resolution`, `output_format`, `size`, `max_images`, `sequential_image_generation`, `scale_factor`. Optional model-specific passthrough params. ## Example ```json theme={null} { "model_id": "google/nano-banana", "prompt": "A cinematic beach sunset with palm trees", "params": { "aspect_ratio": "16:9", "output_format": "jpeg" } } ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Image generated", "data": { "file_id": "11111111-1111-1111-1111-111111111111", "output_url": "https://cdn.example.com/partner-api/workspaces/ws_1/studio/images/image-123.jpeg", "preview_url": "https://cdn.example.com/partner-api/workspaces/ws_1/studio/images/image-123.jpeg", "provider": "fal", "model_id": "google/nano-banana", "credits_used": 1 } } ``` ## Error Responses * `400 invalid_json` - request body is not valid JSON * `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 * `403 insufficient_credits` * `422 invalid_payload` (includes unsupported `model_id`) * `422 invalid_input` - upstream model rejected the provided prompt/image combination * `422 model_requires_image` * `422 model_input_not_supported` * `500 generation_failed` # Generate Studio Video Source: https://docs.genviral.io/api-reference/generate-studio-video POST /api/partner/v1/studio/videos/generate Start async AI Studio video generation and return canonical `video_id` for polling. Starts async video generation and returns a canonical `video_id`. Poll `GET /api/partner/v1/studio/videos/{videoId}` for final state. ## Body Parameters Video model ID from [Get Studio Models](/api-reference/get-studio-models). Optional generation prompt. For prompt-driven models such as `openai/sora-2` and `google/veo-3`, include any desired spoken dialogue directly in this field. Optional speech text for explicit talking-head/lipsync models only (currently `veed/fabric-1.0` and `creatify/lipsync`). Prompt-driven models such as `openai/sora-2` and `google/veo-3` do not accept `speech_text`; use `prompt` instead. Optional voice identifier used with `speech_text` for explicit talking-head/lipsync flows. Prompt-driven models such as `openai/sora-2` and `google/veo-3` do not accept `voice_id`. For supported speech-input models, use Genviral voice IDs (for example `george`, `sarah`, `aria`); if omitted or unrecognized, Genviral falls back to a default voice. Optional input image URL for image-to-video models. Optional input video URL for video-to-video models. Optional external audio URL for models that explicitly accept audio input (currently talking-head/lipsync flows and `bytedance/seedance-2.0` reference-to-video). For Seedance, provide at least one image or video reference when sending audio. Optional Seedance 2.0 reference images. Up to 9 images. Refer to them in the prompt as `@Image1`, `@Image2`, etc. Optional Seedance 2.0 reference videos. Up to 3 videos. Refer to them in the prompt as `@Video1`, `@Video2`, etc. Optional Seedance 2.0 reference audio clips. Up to 3 clips. Refer to them in the prompt as `@Audio1`, `@Audio2`, etc. Requires at least one image or video reference. Optional negative prompt. Normalized video params. Supported keys: `resolution`, `duration_seconds`, `fps`, `aspect_ratio`, `generate_audio`. Optional model-specific passthrough params. ## Example ```json theme={null} { "model_id": "bytedance/seedance-2.0", "prompt": "Cinematic product reveal using @Image1 as the product style reference", "reference_image_urls": ["https://example.com/product.jpg"], "params": { "duration_seconds": 5, "aspect_ratio": "9:16", "generate_audio": true } } ``` ```json Response theme={null} { "ok": true, "code": 202, "message": "Video generation started", "data": { "video_id": "22222222-2222-2222-2222-222222222222", "prediction_id": "pred_abc123", "provider": "fal", "model_id": "openai/sora-2", "status": "processing", "output_url": null, "credits_used": 5 } } ``` ## Error Responses * `400 invalid_json` - request body is not valid JSON * `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 * `403 insufficient_credits` * `422 invalid_payload` (includes unsupported `model_id`) * `422 invalid_input` * `500 generation_failed` # Get Accounts Source: https://docs.genviral.io/api-reference/get-accounts GET /api/partner/v1/accounts List connected social accounts and discover per-account posting capabilities, media limits, caption limits, and settings schemas for the Genviral social media API. List the social accounts available in the authenticated key scope - including both your own connected accounts (BYO) and hosted creator accounts. Use this endpoint to discover the `account_id` you need when calling [Create Post](/api-reference/create-post), and to inspect what each account can publish before you build a payload. ## Behavior * The API merges active BYO social accounts with active hosted accounts in scope. * Workspace keys return workspace accounts. Personal keys return personal accounts (`user_id = owner_user_id`, `workspace_id IS NULL`). * IDs are stable UUIDs constrained to key scope. If no active accounts exist, the endpoint returns an empty array. * Each account is normalized to expose the platform (`tiktok`, `instagram`, etc.) and a simplified `type` of `byo` or `hosted`. * Each account includes a `capabilities` object with supported content kinds, caption limits, media limits, unsupported reasons, and a machine-readable provider settings schema. * Text-only posting is available only for accounts whose `capabilities.supported_content_kinds` includes `text_only`. ## Response Authenticated key scope: `workspace` or `personal`. Resolved owner user ID for the authenticated key. Workspace ID for workspace keys, otherwise `null`. List of social accounts available for posting. UUID account ID returned by `/accounts`. Use this in the `accounts` array when creating a post. The platform name (e.g., `instagram`, `tiktok`). Type of account: `byo` (connected by user) or `hosted` (managed virtual account). The handle or username. The display name of the account. Current status of the account connection. Workspace that owns the account, or `null` in personal scope. Posting capabilities for this account. Content kinds this account can publish through Genviral. Values include `text_only`, `single_image`, `multi_image`, and `single_video`. Maximum caption length for this account after platform/account policy is applied. Media limits such as `max_images`, `max_videos`, and optionally `max_video_seconds`. Reason strings keyed by unsupported content kind. JSON Schema for the dashboard composer. Field names may differ from the Partner API. For create payloads, use the field tables in [`Create Post`](/api-reference/create-post) for YouTube, TikTok, and Pinterest — not `settings_schema`. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/accounts \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Accounts retrieved", "data": { "scope": "workspace", "owner_user_id": "7be83ba4-14e5-47ba-a662-0ad42d852111", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "accounts": [ { "id": "54c22677-43f4-415f-a820-7dfd1fcd4bd5", "platform": "twitter", "type": "byo", "username": "jamesgrowth_", "display_name": "james", "status": "active", "workspace_id": null, "capabilities": { "supported_content_kinds": ["text_only", "single_image", "multi_image", "single_video"], "caption_limit": 4000, "media_limits": { "max_images": 4, "max_videos": 1 }, "unsupported_content_kind_reasons": {}, "settings_schema": { "type": "object", "additionalProperties": false, "properties": { "who_can_reply_post": { "type": "string", "enum": ["everyone", "following", "mentionedUsers", "subscribers", "verified"] }, "made_with_ai": { "type": "boolean" }, "paid_partnership": { "type": "boolean" } }, "required": ["who_can_reply_post"] } } }, { "id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "platform": "tiktok", "type": "hosted", "username": "ugc_trends_42", "display_name": "Hosted Trends 42", "status": "active", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "capabilities": { "supported_content_kinds": ["single_image", "multi_image", "single_video"], "caption_limit": 500, "media_limits": { "max_images": 35, "max_videos": 1 }, "unsupported_content_kind_reasons": { "text_only": "Text-only posts are only available for connected BYO accounts; hosted accounts require media." }, "settings_schema": { "type": "object", "additionalProperties": false, "properties": {} } } } ] } } ``` ## Error Responses * `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 accounts_failed` - Database query failed unexpectedly # Get Analytics Refresh Source: https://docs.genviral.io/api-reference/get-analytics-refresh GET /api/partner/v1/analytics/refreshes/{id} Check refresh status for an analytics refresh request Use this endpoint to poll refresh progress after triggering `POST /analytics/targets/{id}/refresh`. ## Path Parameters Refresh ID returned by the refresh endpoint. ## Response Returns the scoped refresh status payload from `social_analytics_refreshes`. Refresh ID. Analytics target ID. Current refresh status (`pending`, `processing`, `completed`, `failed`). Workspace scope for refresh billing/context. Credits charged for this refresh (`0` when free). Whether the free refresh window was used. Refresh start timestamp. Completion timestamp if finished. Error message when status is `failed`. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/analytics/refreshes/fd8f615f-971e-4f9f-85dc-f8e55fb0a413 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics refresh status retrieved", "data": { "id": "fd8f615f-971e-4f9f-85dc-f8e55fb0a413", "target_id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "status": "completed", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "credits_used": 1, "free_refresh_used": false, "started_at": "2026-02-14T16:05:11.025Z", "completed_at": "2026-02-14T16:05:45.120Z", "error": null } } ``` ## Error Responses * `400 missing_refresh_id` - missing path parameter * `404 refresh_not_found` - refresh ID not visible in authenticated key scope * `401`/`403` - authentication failed or key lacks required scope access * `500` - unexpected database lookup error All error responses from analytics endpoints include an `error_code` field. # Get Analytics Summary Source: https://docs.genviral.io/api-reference/get-analytics-summary GET /api/partner/v1/analytics/summary Fetch social media analytics for TikTok, Instagram, and YouTube - including views, likes, comments, shares, engagement rates, and posting streaks. Feed performance data back to your OpenClaw agent for strategy optimization. Returns the same summary dataset used by the internal analytics dashboard: KPI deltas, interaction timeline, engagement series, posting heatmap, posting streak, and content mix. Use this endpoint to feed real performance data back to your OpenClaw agent or AI automation - enabling data-driven content strategy that adapts based on what actually performs across TikTok, Instagram, and YouTube. ## Billing * This endpoint is read-only and does **not** consume credits. * Credits are only consumed by analytics seat billing and paid refreshes (see [Refresh Analytics Target](/api-reference/refresh-analytics-target)). ## Query Parameters Date preset. Supported values: `14d`, `30d`, `90d`, `1y`, `all`. Custom range start date (`YYYY-MM-DD`). Must be used with `end`. Custom range end date (`YYYY-MM-DD`). Must be used with `start`. Comma-separated platform filter (for example: `tiktok,instagram`). You can also use `platform`. Comma-separated analytics target IDs. You can also use `account_ids`. ## Response Selected date range with `start` and `end`. Applied platform/account filters. KPI values and deltas for published videos, active accounts, views, likes, comments, shares, saves, and engagement rate. Per-day interaction totals (`views`, `likes`, `comments`, `shares`). Per-day engagement rate values. Per-day posting counts used for the calendar heatmap. Current consecutive posting-day streak ending at the selected range end date. Posts split by platform with percentage share. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/analytics/summary?range=30d&platforms=tiktok,instagram' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics summary retrieved", "data": { "range": { "start": "2026-01-15", "end": "2026-02-14" }, "filters": { "platforms": ["tiktok", "instagram"], "accountIds": [] }, "kpis": { "publishedVideos": { "value": 41, "delta": 8 }, "activeAccounts": { "value": 5, "delta": 1 }, "views": { "value": 815422, "delta": 102881 }, "likes": { "value": 51220, "delta": 6412 }, "comments": { "value": 2941, "delta": 318 }, "shares": { "value": 1822, "delta": 205 }, "saves": { "value": 1104, "delta": 121 }, "engagementRate": { "value": 6.86, "delta": 0.42 } }, "interactionSeries": [], "engagementSeries": [], "postingHeatmap": [], "postingStreak": 3, "contentMix": [] } } ``` ## Error Responses * `400` - invalid `range`, invalid date parameters (`start`/`end`), invalid date ordering, or invalid `platforms` filter values * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected analytics query error All error responses from analytics endpoints include an `error_code` field. # Get Analytics Workspace Suggestions Source: https://docs.genviral.io/api-reference/get-analytics-workspace-suggestions GET /api/partner/v1/analytics/workspace-suggestions List other workspace/personal analytics scopes with available tracked accounts Returns cross-scope suggestions for analytics account ownership (same behavior as the internal workspace-suggestions endpoint used in the UI). ## Response Each item includes: Suggested workspace ID (`null` for personal scope). Workspace display name (or `Personal`). Number of tracked analytics targets in that scope. Whether this suggestion points to personal scope. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/analytics/workspace-suggestions \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics workspace suggestions retrieved", "data": [ { "workspaceId": "bbf64a28-a2fd-48f4-88ef-bf7480f18402", "workspaceName": "Archived Brand Workspace", "totalItems": 12, "isPersonal": false } ] } ``` ## Error Responses * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected workspace suggestion lookup error All error responses from analytics endpoints include an `error_code` field. # Get Folder Source: https://docs.genviral.io/api-reference/get-folder GET /api/partner/v1/folders/{folderId} Retrieve one folder in the authenticated key scope Returns a single folder summary, including counts and preview files. ## Path Parameters Folder UUID. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder retrieved", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Product Shots", "media_type": "upload", "parent_folder_id": null, "path": "/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "file_count": 12, "subfolder_count": 2, "preview_files": [ { "id": "file_1", "url": "https://cdn.vireel.io/...", "content_type": "image/jpeg" } ], "created_at": "2026-03-01T09:00:00.000Z", "updated_at": "2026-03-01T09:00:00.000Z" } } ``` ## Error Responses * `400 missing_folder_id` - missing route parameter * `422 invalid_path` - `folderId` is not a valid UUID * `422 invalid_folder_media_type` - folder media type is not supported by Partner folder APIs * `404 not_found` - folder not visible in key scope * `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 folder_get_failed` - unexpected failure # Get Pack Source: https://docs.genviral.io/api-reference/get-pack GET /api/partner/v1/packs/{packId} Retrieve pack metadata and ordered image list Fetches one pack visible to the authenticated key scope. ## Path Parameters Pack UUID. ## Response Ordered by `created_at` ascending. AI-generated enrichment for retrieval quality. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/packs/11111111-1111-1111-1111-111111111111 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Pack retrieved", "data": { "id": "11111111-1111-1111-1111-111111111111", "name": "Motivation Pack", "image_count": 2, "is_public": false, "created_at": "2026-02-14T09:00:00.000Z", "images": [ { "id": "22222222-2222-2222-2222-222222222222", "url": "https://cdn.example.com/packs/motivation/01.jpg", "metadata": { "status": "completed", "description": "Woman lifting dumbbells in a bright, minimal gym environment.", "keywords": ["fitness", "workout", "strength", "gym", "motivation", "healthy lifestyle"], "model": "gpt-4.1-nano", "generated_at": "2026-02-17T11:02:00.000Z", "error": null } }, { "id": "33333333-3333-3333-3333-333333333333", "url": "https://cdn.example.com/packs/motivation/02.jpg", "metadata": { "status": "pending", "description": null, "keywords": [], "model": "gpt-4.1-nano", "generated_at": null, "error": null } } ] } } ``` ## Error Responses * `400 missing_pack_id` - path param missing * `404 not_found` - pack is missing/outside key scope * `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 pack_get_failed` - unexpected retrieval failure # Get Post Source: https://docs.genviral.io/api-reference/get-post GET /api/partner/v1/posts/{postId} Retrieve details for a specific post Return the canonical status for a single post, including every account’s state (`pending`, `posted`, `failed`, retry metadata, published URLs, etc.) plus normalized media metadata. Media mirrors the create payload (`type` + `url`/`urls`) or returns `null` for text-only posts, and surfaces `music_url` alongside it. ## Path Parameters The UUID of the post to retrieve (e.g., `11111111-1111-1111-1111-111111111111`). ## Response Post ID. Post caption. Overall status of the post. Returned in the same shape as create/update payloads, or `null` for text-only posts. `video` or `slideshow`. Present for `video`. Present for `slideshow`. TikTok post URL when music is applied; `null` otherwise. Optional TikTok settings when present on the post; otherwise `null`. Optional Pinterest settings (`board_id`, `link`, `title`, `tags`) when present on the post; otherwise `null`. List of per-account status objects including `status`, `error_message`, `published_at`, `last_attempted_at`, and `published_url`. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/posts/11111111-1111-1111-1111-111111111111 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Post retrieved", "data": { "id": "11111111-1111-1111-1111-111111111111", "caption": "Holiday drops start Monday!", "status": "scheduled", "scheduled_at": "2025-02-01T15:00:00Z", "created_at": "2025-01-20T12:05:11.205Z", "media": null, "music_url": null, "tiktok": null, "pinterest": { "board_id": "123456789012345678", "title": "Dinner board", "link": "https://example.com/recipes", "tags": ["Italian recipes", "easy dinner"] }, "accounts": { "total": 2, "states": [ { "account_id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "status": "scheduled", "published_at": null, "error_message": null, "last_attempted_at": null, "published_url": null }, { "account_id": "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089", "status": "scheduled", "published_at": null, "error_message": null, "last_attempted_at": null, "published_url": null } ] } } } ``` ## Error Responses * `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 * `404 not_found` - post does not exist in the authenticated key scope * `500 get_failed` - unexpected database/serialization error # Get Slideshow Source: https://docs.genviral.io/api-reference/get-slideshow GET /api/partner/v1/slideshows/{slideshowId} Retrieve a full slideshow payload Fetch one slideshow, including settings and slide-level content. ## Path Parameters Slideshow UUID. ## Response Slideshow-level settings. Ordered slide list. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/slideshows/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0 \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Slideshow retrieved", "data": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "title": "5 discipline quotes", "status": "draft", "slideshow_type": "educational", "product_id": null, "original_prompt": "5 discipline quotes", "preview_image_url": null, "created_at": "2026-02-14T09:20:00.000Z", "updated_at": "2026-02-14T09:20:00.000Z", "last_rendered_at": null, "slide_count": 1, "settings": { "image_pack_id": "11111111-1111-1111-1111-111111111111", "aspect_ratio": "4:5", "slideshow_type": "educational", "advanced_settings": { "font_size": 44, "text_preset": "tiktok", "text_width": "default" }, "pack_assignments": null }, "slides": [ { "index": 0, "image_url": "https://cdn.example.com/slide-1.jpg", "rendered_image_url": null, "text_elements": [ { "id": "9fd0b2bd-a95a-4488-8b7a-bf1d18d2bcdf", "content": "Discipline beats motivation", "x": 50, "y": 25, "font_size": 48, "width": 70, "height": null, "editable": true, "style_preset": "tiktok", "font_family": null, "background_color": null, "text_color": null, "border_radius": null } ], "grid_images": null, "grid_type": null, "background_filters": null, "image_overlays": null } ] } } ``` ## Error Responses * `400 missing_slideshow_id` - path param missing * `404 not_found` - slideshow is missing or outside key scope * `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 get_failed` - unexpected retrieval failure # Get Studio Models Source: https://docs.genviral.io/api-reference/get-studio-models GET /api/partner/v1/studio/models List Studio image/video models with normalized capabilities, params, and credit notes. Returns partner-ready model metadata for Studio generation. ## Query Parameters Optional filter: `image`, `video`, or `all`. Omit to return both (`all`). ## Response Successful requests return `200` with: * `mode` - `all`, `image`, or `video` * `total` - number of returned models * `models` - normalized model list ### Model Object | Field | Type | Description | | ---------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | `id` | `string` | Model ID used in generation requests | | `mode` | `image \| video` | Model category | | `name` | `string` | Display name | | `provider` | `string` | Provider label | | `description` | `string` | Short model description | | `credits.default_cost` | `number` | Default credit estimate | | `credits.notes` | `string` | Credit calculation notes | | `capabilities` | `object` | Supported input/output modes | | `inputs` | `object` | Required and optional fields, plus conditional rules (for example `one_of:motion,prompt` or `one_of:audio_url,speech_text`) when applicable | | `params` | `object` | Normalized params and options | | `raw_available_params` | `object \| null` | Raw internal options for advanced mapping | `inputs.optional` only lists `speech_text`, `voice_id`, and `audio_url` when the selected model supports explicit speech, lipsync input, or Seedance 2.0 reference audio. Prompt-driven models such as `openai/sora-2` and `google/veo-3` are prompt-only: include spoken dialogue inside `prompt` and do not send separate speech fields. ```json Response theme={null} { "ok": true, "code": 200, "message": "Studio models retrieved", "data": { "mode": "image", "total": 1, "models": [ { "id": "google/nano-banana", "mode": "image", "name": "Nano Banana", "provider": "Google", "credits": { "default_cost": 1, "notes": "Credits match in-app AI Studio cost calculation." } } ] } } ``` ## Error Responses * `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 * `422 invalid_query` - unsupported `mode` # Get Studio Video Source: https://docs.genviral.io/api-reference/get-studio-video GET /api/partner/v1/studio/videos/{videoId} Poll canonical video generation status by `video_id`. Returns status for a Studio video job in the authenticated key scope. ## Path Parameters Video UUID returned by `POST /api/partner/v1/studio/videos/generate`. ## Status Values * `processing` * `succeeded` * `failed` ## Example ```bash theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/studio/videos/22222222-2222-2222-2222-222222222222' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Video status retrieved", "data": { "video_id": "22222222-2222-2222-2222-222222222222", "prediction_id": "pred_abc123", "model_id": "openai/sora-2", "provider": "fal", "status": "succeeded", "output_url": "https://cdn.example.com/videos/video-22222222.mp4", "error": null, "created_at": "2026-02-19T10:20:30.000Z", "context": "partner_api", "source": "partner_api_studio" } } ``` ## Error Responses * `404 video_not_found` * `422 invalid_path` * `401` - authentication failed (missing/invalid/revoked token) # Get Subscription Source: https://docs.genviral.io/api-reference/get-subscription GET /api/partner/v1/subscription Return subscription status, credits, and renewal dates for the authenticated key scope Returns the core subscription attached to the authenticated key owner (workspace owner for workspace keys, owner user for personal keys). Use this endpoint to show customers: * current tier and subscription status * credits limit, credits used, and credits remaining * next renewal period end (`renews_at`) and credits reset date ## Response Authenticated key scope: `workspace` or `personal`. Workspace ID for workspace keys, otherwise `null`. Active core subscription details. Subscription status (for example `active`, `canceled`). Tier resolved from `polar_product_id` (`small`, `medium`, `large`, etc.). Billing cadence: `month` or `year`. ISO timestamp for current billing period start. ISO timestamp for current billing period end. Renewal timestamp (same as `current_period_end`). Credits usage summary. Monthly credits available for this subscription. Credits consumed in the current period. Credits currently available (`limit - used_this_period`). Planned credits reset timestamp. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/subscription \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Subscription retrieved", "data": { "scope": "workspace", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "subscription": { "status": "active", "tier": "small", "billing_interval": "month", "current_period_start": "2026-02-01T00:00:00.000Z", "current_period_end": "2026-03-01T00:00:00.000Z", "renews_at": "2026-03-01T00:00:00.000Z", "canceled_at": null, "is_paused": false, "paused_until": null, "credits": { "limit": 500, "used_this_period": 123, "remaining": 377, "reset_at": "2026-03-01T00:00:00.000Z" } } } } ``` ## Error Responses * `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 subscription_lookup_failed` - unexpected subscription lookup failure # Get Template Source: https://docs.genviral.io/api-reference/get-template GET /api/partner/v1/templates/{templateId} Retrieve one template in scope Fetches one template visible to the authenticated key scope. ## Path Parameters Template UUID. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/templates/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Template retrieved", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Hooks Template", "description": "2-slide educational format", "visibility": "workspace", "preview_url": null, "use_count": 12, "last_used_at": "2026-02-14T09:11:00.000Z", "created_at": "2026-02-12T15:00:00.000Z", "updated_at": "2026-02-14T09:11:00.000Z", "is_owner": true, "config": { "version": 1, "structure": { "slides": [ { "type": "pack", "id": "11111111-1111-1111-1111-111111111111", "pinnedImageUrl": null, "textElements": [ { "id": "22222222-2222-2222-2222-222222222222", "content": "Hook text", "x": 50, "y": 25, "fontSize": 48, "width": 70 } ] } ] }, "content": { "mode": "manual", "productId": null, "language": "en", "slideshowType": "educational" }, "visuals": { "packId": null, "aspectRatio": "4:5", "textSettings": { "fontSize": "default", "preset": "tiktok", "width": "default" } } } } } ``` ## Error Responses * `400 missing_template_id` - path param missing * `404 not_found` - template is missing/outside key scope * `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 template_get_failed` - unexpected retrieval failure # Get Trend Brief Source: https://docs.genviral.io/api-reference/get-trend-brief GET /api/partner/v1/trends/brief Generate a one-call TikTok trend brief for a keyword with top hashtags, sounds, creators, posting windows, hook angles, and sample videos. Generate an AI-agent ready TikTok trend brief from one keyword. This endpoint consolidates trend videos, search results, and sounds into one response so OpenClaw or custom agents can plan content without making multiple trend calls. Brief aggregation is keyword-relevance aware: when matching videos are found, the summary/recommendation output prioritizes those matches over unrelated high-view videos. Public trends access is brief-only. Use this endpoint instead of any legacy atomic trends routes. ## Query Parameters Platform target. Current supported value is `tiktok`. Required trend query seed (for example `morning routine`, `fitness`, `grwm`). Number of videos returned in `data.evidence.sample_videos`. Range: `1..30`. Trend window: `24h`, `7d`, or `30d`. ## Response Echoed platform (`tiktok`). Normalized keyword used to build the brief. Applied range window (`24h`, `7d`, `30d`). ISO timestamp indicating when the brief payload was generated. Aggregated trend snapshot. Ready-to-use planning suggestions. Supporting sample videos from the analyzed pool. ## Caching * Cached for 3 hours by `platform + keyword + range`. * `limit` affects only `sample_videos` length in the final response. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/trends/brief?platform=tiktok&keyword=morning%20routine&limit=10&range=7d' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Trend brief retrieved", "data": { "platform": "tiktok", "keyword": "morning routine", "range": "7d", "generated_at": "2026-02-19T06:30:00.000Z", "summary": { "analyzed_video_count": 42, "top_hashtags": [ { "hashtag": "morningroutine", "videos_count": 29, "avg_views": 157217 } ], "top_sounds": [ { "sound_id": "sound:summer", "sound_name": "Summer", "usage_count": 1, "avg_views": 2244030, "sample_url": null } ], "top_creators": [ { "handle": "chaseridgewayy", "videos_count": 1, "total_views": 2244030, "avg_views": 2244030, "top_video_url": "https://www.tiktok.com/@chaseridgewayy/video/7606308970243968270" } ], "posting_windows_utc": [ { "day_of_week_utc": 5, "hour_utc": 11, "videos_count": 1, "avg_views": 2244030 } ] }, "recommendations": { "hook_angles": ["Ummmm should I..."], "hashtags": ["#morningroutine"], "sounds": [{ "sound_id": "sound:summer", "sound_name": "Summer" }] }, "evidence": { "sample_videos": [ { "video_id": "7606308970243968270", "url": "https://www.tiktok.com/@chaseridgewayy/video/7606308970243968270", "views": 2244030, "likes": 298790, "comments": 613, "shares": 17460, "caption": "Ummmm should I....move here????", "hashtags": [], "sound_id": "sound:summer", "sound_name": "Summer", "author_handle": "chaseridgewayy", "published_at": "2026-02-13T11:23:51.000Z" } ] } } } ``` ## Error Responses * `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 * `422` - invalid query parameters (for example unsupported `platform`, missing `keyword`, or invalid `limit`) * `500 trends_brief_failed` - unexpected trend brief generation failure # Genviral Partner API Source: https://docs.genviral.io/api-reference/introduction REST API for posting, scheduling, slideshows, Studio AI, analytics, and automation across your connected social accounts. Post and schedule content to your connected accounts from one REST API. Use it from scripts, cron jobs, or AI agents. ## For AI agents Give your agent the doc index instead of pasting pages manually: ```text theme={null} https://docs.genviral.io/llms.txt ``` Copy that URL into a system prompt, MCP config, or skill file. Mintlify generates it automatically from these docs. Need machine-readable API operations? Use [`openapi.json`](https://docs.genviral.io/api-reference/openapi.json). For full page content in one file, use [`llms-full.txt`](https://docs.genviral.io/llms-full.txt). Prefer a ready-made integration? Use the [OpenClaw skill](https://github.com/fdarkaou/genviral-skill). ## Base URL ```text theme={null} https://www.genviral.io/api/partner/v1 ``` ## Authentication 1. Sign in at [genviral.io](https://www.genviral.io) and open **API Keys**. 2. Create a **Personal** or **Workspace** key and store the token (shown once). 3. Send it on every request: ```bash theme={null} Authorization: Bearer . ``` Keys are single-scope. Workspace keys only see that workspace; personal keys only see personal accounts. Requests never fall through to unscoped data. Partner API access requires an active Creator, Professional, or Business plan. ## Quick start ```bash theme={null} # 1. List accounts in your key scope curl -s https://www.genviral.io/api/partner/v1/accounts \ -H "Authorization: Bearer $GENVIRAL_TOKEN" # 2. Create a post curl -X POST https://www.genviral.io/api/partner/v1/posts \ -H "Authorization: Bearer $GENVIRAL_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "caption": "Hello from the Partner API", "media": { "type": "video", "url": "https://cdn.example.com/clip.mp4" }, "accounts": [{ "id": "YOUR_ACCOUNT_ID" }] }' ``` Before posting, call [`GET /accounts`](/api-reference/get-accounts) and read each account's `capabilities` — they tell you whether the account supports text-only posts, video, slideshows, and provider-specific settings. ## Typical workflow 1. **Discover accounts** — `GET /accounts` 2. **Create posts** — `POST /posts` with caption, optional media, and `settings.` 3. **Track status** — `GET /posts` or `GET /posts/{id}` 4. **Update or retry** — `PATCH /posts/{id}` or `POST /posts/retry` Details live on the endpoint pages below. Start with [Create Post](/api-reference/create-post) and [Response Pattern](/api-reference/response). ## Supported platforms | Platform | Posting | Analytics | | --------- | ------- | --------- | | TikTok | Yes | Yes | | Instagram | Yes | Yes | | YouTube | Yes | Yes | | Facebook | Yes | - | | Pinterest | Yes | - | | LinkedIn | Yes | - | | X/Twitter | Yes | - | | Threads | Yes | - | | Bluesky | Yes | - | | Reddit | Yes | - | Not every account supports every content type. Always check `capabilities` from `/accounts`. ## Postman Import the collection and set `base_url` plus `partner_api_token`: * [Download collection](https://www.genviral.io/postman-collection.json) ## Next steps * [Response Pattern](/api-reference/response) — `ok` / `code` / `message` envelope * [Data Structures](/api-reference/data-structures) — shared object shapes * [Get Accounts](/api-reference/get-accounts) — account IDs and capabilities * [Create Post](/api-reference/create-post) — captions, media, provider settings * [Slideshows](/api-reference/slideshows) — generate and render carousels * [Studio AI](/api-reference/studio-ai) — generate images and videos # List Analytics Posts Source: https://docs.genviral.io/api-reference/list-analytics-posts GET /api/partner/v1/analytics/summary/posts Fetch paginated post-level analytics across tracked accounts Returns post analytics from the same summary-posts dataset used in the internal analytics UI, including platform/account context, sortable metrics, and partner-post correlation fields when the analytics post can be matched back to an originating Genviral post. ## Query Parameters Date preset: `14d`, `30d`, `90d`, `1y`, `all`. Custom range start date (`YYYY-MM-DD`). Must be paired with `end`. Custom range end date (`YYYY-MM-DD`). Must be paired with `start`. Comma-separated platform filter (for example: `tiktok,instagram`). You can also use `platform`. Comma-separated analytics target IDs. You can also use `account_ids`. Sort field: `published_at`, `views`, `likes`, `comments`, `shares`. You can also use `sort_by`. Sort direction: `asc` or `desc`. You can also use `sort_order`. Page size, max `100`. Offset for pagination. Maximum `10000`. ## Response Array of analytics post rows with metrics and account context. Total matching rows for the selected filters. Whether additional rows are available. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/analytics/summary/posts?range=90d&sortBy=views&sortOrder=desc&limit=25&offset=0' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics posts retrieved", "data": { "posts": [ { "id": "88c2011d-c620-4b61-8486-78e8f182e8ec", "analyticsId": "88c2011d-c620-4b61-8486-78e8f182e8ec", "platformPostId": "7391929919011", "genviralPostId": "d0e1d35c-d34c-4503-afe8-3c56e4aa1911", "externalId": "automation-1e93c16b-4866-43ed-82e9-ea55bae5e1ab", "publishedAt": "2026-02-12T17:42:02.000Z", "caption": "How to prep your first week of content", "permalink": "https://www.tiktok.com/@brand/video/7391929919011", "thumbnailUrl": "https://cdn.example.com/thumb.jpg", "durationSeconds": 27, "metrics": { "views": 14120, "likes": 812, "comments": 67, "shares": 29, "saves": 14 }, "engagementRate": 6.42, "platform": "tiktok", "accountIdentifier": "@brand", "accountDisplayName": "Brand HQ", "accountAvatarUrl": null } ], "totalCount": 192, "hasMore": true } } ``` ## Notes * `id` is the analytics-row ID for this analytics dataset. * `analyticsId` is the explicit analytics-row ID. `id` is retained as a legacy alias for backward compatibility. * `platformPostId` is the platform-native post/video ID when available. * `genviralPostId` is the originating Genviral post ID when the analytics row can be correlated back to a created/scheduled post. * `externalId` is the originating Partner API `external_id` when available. * `genviralPostId` and `externalId` are `null` when a row cannot be correlated back to a Partner/Genviral-created post. * For BYO TikTok `MEDIA_UPLOAD`, correlation is best-effort for recent unresolved drafts after the human publishes in TikTok. Older rows can remain uncorrelated until a future async reconciliation/backfill exists. ## Error Responses * `400` - invalid query parameters (`range`, `platforms`, `sortBy`, `sortOrder`, `limit`, `offset`, `start/end`) * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected analytics query error All error responses from analytics endpoints include an `error_code` field. # List Files Source: https://docs.genviral.io/api-reference/list-files GET /api/partner/v1/files List partner files in the authenticated key scope Retrieve a paginated list of partner files in the authenticated key scope. This includes: * direct uploads created via `POST /files` * media ingested by partner posting flows ## Query Parameters Number of files to return. Must be an integer between `1` and `100`. Number of files to skip for pagination. Must be an integer `>= 0`. Filter by file type: `image` or `video`. Optional context filter. * Omit `context` to keep default partner-only results. * Use a single context (example: `ai-studio`) or comma-separated contexts (example: `ai-studio,media-upload`). * Use `context=all` to return all file contexts in your authenticated key scope. ## Response Successful requests return `200` with: * `files` - Array of file objects * `total` - Total count of matching files * `limit` - Applied limit * `offset` - Applied offset ### File Object | Field | Type | Description | | ------------- | -------- | ---------------------- | | `id` | `string` | Unique file identifier | | `url` | `string` | CDN URL for the file | | `contentType` | `string` | MIME type of the file | | `filename` | `string` | Original filename | | `size` | `number` | File size in bytes | | `createdAt` | `string` | ISO 8601 timestamp | ## Examples ### List all files ```javascript theme={null} import fetch from "node-fetch"; const response = await fetch( "https://www.genviral.io/api/partner/v1/files?limit=20", { headers: { Authorization: "Bearer ", }, } ); const { data } = await response.json(); console.log(`Found ${data.total} files`); data.files.forEach((file) => { console.log(`${file.filename}: ${file.url}`); }); ``` ```bash theme={null} # List all files curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/files?limit=20' \ --header 'Authorization: Bearer ' # List only videos curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/files?type=video&limit=10' \ --header 'Authorization: Bearer ' # Paginate through results curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/files?limit=20&offset=20' \ --header 'Authorization: Bearer ' # Query AI Studio + media uploads contexts curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/files?context=ai-studio,media-upload&limit=20' \ --header 'Authorization: Bearer ' # Query all file contexts in key scope curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/files?context=all&limit=20' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Files retrieved", "data": { "files": [ { "id": "abc123", "url": "https://cdn.vireel.io/partner-api/workspaces/workspace_123/abc123.mp4", "contentType": "video/mp4", "filename": "promo-video.mp4", "size": 15728640, "createdAt": "2025-01-20T14:30:00.000Z" }, { "id": "def456", "url": "https://cdn.vireel.io/partner-api/workspaces/workspace_123/def456.jpg", "contentType": "image/jpeg", "filename": "slide-1.jpg", "size": 524288, "createdAt": "2025-01-20T14:25:00.000Z" } ], "total": 42, "limit": 20, "offset": 0, "context": null } } ``` ## Error Responses * `400 invalid_query_parameter` - one or more query parameters are invalid * `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 list_failed` - Failed to retrieve files (retry) Use the `type` filter to quickly find videos or images when building slideshows or selecting media for posts. # List Folder Ancestors Source: https://docs.genviral.io/api-reference/list-folder-ancestors GET /api/partner/v1/folders/{folderId}/ancestors Get breadcrumb ancestors for a folder Returns the folder lineage from root to immediate parent. ## Path Parameters Folder ID. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/folders/{{folder_id}}/ancestors \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder ancestors retrieved", "data": { "folder_id": "cccccccc-cccc-cccc-cccc-cccccccccccc", "ancestors": [ { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "All Uploads", "parent_folder_id": null, "depth": 0 }, { "id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "name": "Campaigns", "parent_folder_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "depth": 1 } ] } } ``` ## Error Responses * `400 missing_folder_id` - missing route parameter * `422 invalid_path` - `folderId` is not a valid UUID * `422 invalid_folder_media_type` - folder media type is not supported by Partner folder APIs * `404 not_found` - folder not visible in key scope * `500 folder_ancestors_list_failed` - unexpected lookup failure # List Folder Items Source: https://docs.genviral.io/api-reference/list-folder-items GET /api/partner/v1/folders/{folderId}/items List files or slideshows contained in a folder Folder item behavior depends on folder media type: * `slideshow` folder → item IDs are slideshow IDs * `ai_image`, `ai_video`, and `upload` folders → item IDs are file IDs ## Path Parameters Folder UUID. ## Query Parameters Number of items to return (`1-100`). Number of items to skip. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/items?limit=20&offset=0' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder items retrieved", "data": { "media_type": "upload", "item_type": "file", "items": [ { "id": "11111111-1111-1111-1111-111111111111", "url": "https://cdn.vireel.io/...", "content_type": "image/jpeg", "filename": "slide-1.jpg", "size": 524288, "created_at": "2026-03-01T09:00:00.000Z", "added_at": "2026-03-01T09:05:00.000Z" } ], "total": 1, "limit": 20, "offset": 0 } } ``` ## Error Responses * `400 invalid_query_parameter` - invalid `limit` or `offset` * `422 invalid_path` - `folderId` is not a valid UUID * `422 invalid_folder_media_type` - folder media type is not supported by Partner folder APIs * `404 not_found` - folder not visible in key scope * `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 folder_items_list_failed` - unexpected failure # List Folders Source: https://docs.genviral.io/api-reference/list-folders GET /api/partner/v1/folders List folders by media type and parent level in the authenticated key scope Returns paginated folders for one `media_type` at a hierarchy level. ## Query Parameters Folder media type: `ai_image`, `ai_video`, `upload`, or `slideshow`. Optional parent folder ID. Omit to list root-level folders. Number of folders to return (`1-100`). Number of folders to skip. ## Response * `folders` - Folder summaries (`file_count`, `subfolder_count`, `preview_files`) * `total` - Total matching folders * `limit` / `offset` - Applied pagination values ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/folders?media_type=upload&limit=20&offset=0' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folders retrieved", "data": { "folders": [ { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Product Shots", "media_type": "upload", "parent_folder_id": null, "path": "/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "file_count": 12, "subfolder_count": 2, "preview_files": [ { "id": "file_1", "url": "https://cdn.vireel.io/...", "content_type": "image/jpeg" } ], "created_at": "2026-03-01T09:00:00.000Z", "updated_at": "2026-03-01T09:00:00.000Z" } ], "total": 1, "limit": 20, "offset": 0 } } ``` ## Error Responses * `400 invalid_query_parameter` - query validation failed * `401` / `403` - auth/entitlement failure * `500 folders_list_failed` - unexpected folder query failure # List Packs Source: https://docs.genviral.io/api-reference/list-packs GET /api/partner/v1/packs List visible packs with pagination and optional public-pack inclusion Returns pack summaries visible to the authenticated key scope. ## Query Parameters Optional case-insensitive filter across pack name and AI image metadata (description + keywords). Number of packs to return. Range: `1-100`. Number of packs to skip. Include public packs in addition to scope-owned packs. `include_public` only accepts literal query values `true` or `false`. Any other value falls back to default behavior (`true`). ## Response Pack summary list. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/packs?search=motivation&include_public=false&limit=20' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Packs retrieved", "data": { "packs": [ { "id": "11111111-1111-1111-1111-111111111111", "name": "Motivation Pack", "image_count": 24, "preview_image_url": "https://cdn.example.com/packs/motivation/cover.jpg", "is_public": false, "created_at": "2026-02-14T09:00:00.000Z" } ], "total": 1, "limit": 20, "offset": 0 } } ``` ## Error Responses * `422 invalid_query` - query params failed validation * `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 packs_list_failed` - unexpected listing failure # List Posts Source: https://docs.genviral.io/api-reference/list-posts GET /api/partner/v1/posts Fetch recent Partner API posts with delivery status and summary. Track your automated posting pipeline across supported platforms. Fetch the latest posts in key scope along with a summary. Use this to track delivery status across supported platforms, including posts scheduled by your OpenClaw agent or automation scripts. Only posts that include deliveries for accounts owned by the authenticated key scope are returned. Hosted and BYO accounts are both supported, and deliveries for accounts outside scope are filtered out automatically. ## Query Parameters Filter by status: `draft`, `pending`, `scheduled`, `posted`, `failed`, `partial`, `retry`. When omitted, all statuses are returned. Number of posts to return. Values below 1 are treated as 1; values above 100 are clamped to 100. ISO timestamp. Only include posts created on or after this time. ISO timestamp. Only include posts created on or before this time. `since` and `until` must be valid ISO 8601 strings. Invalid values return a `400` response with `error_code` set to `invalid_since` or `invalid_until`. ## Response Returns a `summary` object with counts and a `posts` array. Scope-wide counters. List of post objects. Each post may include `tiktok` and/or `pinterest` settings when those platform-specific payloads were provided at create/update time. `media` is `null` for text-only posts. `summary.by_status` tracks canonical lifecycle buckets (`draft`, `pending`, `scheduled`, `posted`, `partial`, `failed`). Provider-facing statuses (for example `deleted`/`void`) can still appear on individual post/account `status` fields and are included in `summary.total`. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/posts?status=scheduled&limit=20' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Posts retrieved", "data": { "summary": { "total": 193, "pending": 58, "published": 120, "by_status": { "draft": 15, "pending": 12, "scheduled": 46, "posted": 118, "failed": 4 } }, "posts": [ { "id": "11111111-1111-1111-1111-111111111111", "caption": "Holiday drops start Monday!", "status": "deleted", "scheduled_at": "2025-02-01T15:00:00Z", "created_at": "2025-01-20T12:05:11.205Z", "media": null, "music_url": null, "tiktok": null, "pinterest": { "board_id": "123456789012345678", "title": "Dinner board", "tags": ["Italian recipes"] }, "accounts": { "total": 2, "states": [ { "account_id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812", "status": "deleted", "published_at": null, "error_message": null, "last_attempted_at": null, "published_url": null }, { "account_id": "6b0c8c9c-55ac-4fcb-85ec-70b5a8b0d089", "status": "scheduled", "published_at": null, "error_message": null, "last_attempted_at": null, "published_url": null } ] } } ] } } ``` ## Error Responses * `400 invalid_since` or `400 invalid_until` - query params failed ISO parsing * `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 list_failed` - Database query or summary computation failed # List Slideshows Source: https://docs.genviral.io/api-reference/list-slideshows GET /api/partner/v1/slideshows List slideshow summaries in authenticated key scope Returns paginated slideshow summaries for the authenticated workspace/personal scope. ## Query Parameters Optional status filter (for example `draft` or `rendered`). Optional title search (case-insensitive partial match). Number of records to return. Range: `1-100`. Number of records to skip. ## Response Slideshow summary list. Total matching rows before pagination. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/slideshows?status=draft&limit=20&offset=0' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Slideshows retrieved", "data": { "slideshows": [ { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "title": "5 discipline quotes", "status": "draft", "slideshow_type": "educational", "product_id": null, "preview_image_url": null, "slide_count": 5, "has_rendered_slide": false, "created_at": "2026-02-14T09:20:00.000Z", "updated_at": "2026-02-14T09:20:00.000Z" } ], "total": 1, "limit": 20, "offset": 0 } } ``` ## Error Responses * `422 invalid_query` - query params failed validation * `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 slideshows_list_failed` - unexpected listing failure # List Templates Source: https://docs.genviral.io/api-reference/list-templates GET /api/partner/v1/templates List templates visible to authenticated key scope Returns templates visible to the current key scope (owner/private + workspace + public as applicable). ## Query Parameters Optional name filter (case-insensitive partial match). Number of templates to return. Range: `1-100`. Number of templates to skip. ## Response Template list. ```bash cURL theme={null} curl --request GET \ --url 'https://www.genviral.io/api/partner/v1/templates?search=hooks&limit=20' \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Templates retrieved", "data": { "templates": [ { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Hooks Template", "description": "2-slide educational format", "visibility": "private", "preview_url": null, "use_count": 3, "last_used_at": "2026-02-14T09:11:00.000Z", "created_at": "2026-02-12T15:00:00.000Z", "updated_at": "2026-02-14T09:11:00.000Z", "is_owner": true, "config": { "version": 1, "structure": { "slides": [ { "type": "pack", "id": "11111111-1111-1111-1111-111111111111", "pinnedImageUrl": null, "textElements": [ { "id": "22222222-2222-2222-2222-222222222222", "content": "Hook text", "x": 50, "y": 25, "fontSize": 48, "width": 70 } ] } ] }, "content": { "mode": "manual", "productId": null, "language": "en", "slideshowType": "educational" }, "visuals": { "packId": null, "aspectRatio": "4:5", "textSettings": { "fontSize": "default", "preset": "tiktok", "width": "default" } } } } ], "total": 1, "limit": 20, "offset": 0 } } ``` ## Error Responses * `422 invalid_query` - query params failed validation * `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 templates_list_failed` - unexpected listing failure # llms.txt Source: https://docs.genviral.io/api-reference/llms-txt Machine-readable doc index for AI agents and coding tools. # Get Analytics Target Source: https://docs.genviral.io/api-reference/manage-analytics-target GET /api/partner/v1/analytics/targets/{id} Fetch detail + summary for one tracked analytics target Returns a single analytics target plus its summary data (profile, totals, timeline, top posts, and refresh status). ## Path Parameters Analytics target ID. Use [Update Analytics Target](/api-reference/update-analytics-target) to update target settings and [Delete Analytics Target](/api-reference/delete-analytics-target) to remove a target. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/analytics/targets/e7a7f417-56db-4320-9e9a-fd6fdabda09d \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics target retrieved", "data": { "target": { "id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "platform": "tiktok", "identifier": "@brand", "display_name": "Brand HQ" }, "summary": { "profile": {}, "totals": {}, "timeline": [], "topPosts": [], "latestRefresh": null } } } ``` ## Error Responses * `400 missing_target_id` - missing path parameter * `404` - target not found in current key scope * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected database/analytics service error All error responses from analytics endpoints include an `error_code` field. # List Analytics Targets Source: https://docs.genviral.io/api-reference/manage-analytics-targets GET /api/partner/v1/analytics/targets List tracked analytics accounts in the authenticated key scope Returns tracked analytics targets for the authenticated key scope (workspace or personal). ## Behavior * Returns all targets scoped to the API key. * Includes last snapshot/refresh status and `free_refresh_available`. * This dataset is the source of account-level stats shown in the internal analytics UI. Use [Create Analytics Target](/api-reference/create-analytics-target) to add a new account to track. ## Response Workspace tied to the API key (`null` for personal-scope keys). Analytics targets with status metadata and last snapshot totals. Each row includes canonical `id` and a backward-compatible `target_id` alias. ```bash cURL theme={null} curl --request GET \ --url https://www.genviral.io/api/partner/v1/analytics/targets \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics targets retrieved", "data": { "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "targets": [ { "id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "target_id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "platform": "tiktok", "identifier": "@brand", "display_name": "Brand HQ", "free_refresh_available": true } ] } } ``` ## Error Responses * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected database/analytics service error All error responses from analytics endpoints include an `error_code` field. # Move Folder Source: https://docs.genviral.io/api-reference/move-folder PATCH /api/partner/v1/folders/{folderId} Move a folder to a new parent or to the root level Reparents a folder. This endpoint only moves folders; it does not rename them. ## Path Parameters Folder UUID. ## Body Parameters Destination parent folder ID. Send `null` to move the folder to the root level. ```bash cURL theme={null} curl --request PATCH \ --url https://www.genviral.io/api/partner/v1/folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "parent_folder_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder updated", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Product Shots", "media_type": "upload", "parent_folder_id": "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", "path": "/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "file_count": 12, "subfolder_count": 2, "preview_files": [], "created_at": "2026-03-01T09:00:00.000Z", "updated_at": "2026-03-01T10:00:00.000Z" } } ``` ## Error Responses * `400 invalid_json` - malformed JSON body * `422 invalid_path` - `folderId` is not a valid UUID * `422 invalid_payload` - missing or invalid `parent_folder_id` * `404 not_found` - folder or parent folder not visible in key scope * `409 folder_move_conflict` - invalid move (self, descendant, or destination name conflict) * `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 folder_update_failed` - unexpected failure # Refresh Analytics Target Source: https://docs.genviral.io/api-reference/refresh-analytics-target POST /api/partner/v1/analytics/targets/{id}/refresh Trigger an analytics refresh for a target and return refresh metadata Queues and executes an analytics refresh for a target in the authenticated key scope (workspace or personal). ## Billing * If the target is still inside its free refresh window, `wasFree=true` and `credits_used=0`. * When the free window is exhausted, refresh consumes credits (`credits_used > 0`). * Seat billing (target upkeep) is charged separately from per-refresh billing. ## Path Parameters Analytics target ID. ## Response Refresh row with status, timestamps, and billing metadata. Indicates whether this refresh used the free daily refresh window. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/analytics/targets/e7a7f417-56db-4320-9e9a-fd6fdabda09d/refresh \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics refresh queued", "data": { "refresh": { "id": "fd8f615f-971e-4f9f-85dc-f8e55fb0a413", "target_id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "workspace_id": "4cb5d3fd-8c8f-4ed7-a17c-4ffb52c7e6ac", "status": "pending", "free_refresh_used": true, "credits_used": 0, "started_at": "2026-02-14T16:05:11.025Z", "completed_at": null, "error": null }, "wasFree": true } } ``` ## Error Responses * `400 missing_target_id` - missing path parameter * `402` - insufficient credits for paid refresh * `409 analytics_conflict` - another refresh is already running for this target * `429 rate_limited` - too many refresh requests for the same target in a short window * `404` - target not found in authenticated key scope * `401`/`403` - authentication failed or key lacks required scope access * `500` - unexpected refresh execution error All error responses from analytics endpoints include an `error_code` field. # Regenerate Slide Source: https://docs.genviral.io/api-reference/regenerate-slide POST /api/partner/v1/slideshows/{slideshowId}/slides/{slideIndex}/regenerate Regenerate text for one slide while preserving layout Regenerates text content for a single slide. The service keeps the original text-element structure and only replaces content, so positional and style metadata remain intact. ## Path Parameters Slideshow UUID. Zero-based slide index (`0, 1, 2, ...`). Must be a non-negative integer. ## Body Parameters Optional rewrite instruction (`1-500` chars), for example `"Make it punchier"`. Empty body is valid (`{}` behavior). In that case the API generates a subtle variation automatically. ## Preconditions * Slideshow must be in `draft` status. * Slideshow must have `original_prompt`. * Target slide index must exist. * Target slide must contain at least one text element. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/slideshows/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0/slides/0/regenerate \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "instruction": "Make the hook shorter and more direct" }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Slide regenerated", "data": { "slide_index": 0, "slideshow": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "status": "draft", "slide_count": 5 } } } ``` ## Error Responses * `400 missing_slideshow_id` - path param missing * `400 missing_slide_index` - path param missing * `400 invalid_slide_index` - `slideIndex` is not a non-negative integer * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - body schema validation failed * `404 not_found` - slideshow is missing/outside key scope * `422 regeneration_precondition_failed` - slideshow/slide failed preconditions (non-draft status, missing prompt, out-of-range index, or slide has no text) * `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 regenerate_failed` - unexpected regeneration failure # Remove Folder Items Source: https://docs.genviral.io/api-reference/remove-folder-items DELETE /api/partner/v1/folders/{folderId}/items Remove files or slideshows from a folder Removes one or more folder items. The request body is required even though the HTTP method is `DELETE`. ## Path Parameters Folder UUID. ## Body Parameters UUIDs of files or slideshows to remove (`1-200` items). ```bash cURL theme={null} curl --request DELETE \ --url https://www.genviral.io/api/partner/v1/folders/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/items \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "item_ids": [ "11111111-1111-1111-1111-111111111111" ] }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Folder items removed", "data": { "folder_id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "item_type": "file", "removed_count": 1 } } ``` ## Error Responses * `400 invalid_json` - malformed JSON body * `422 invalid_path` - `folderId` is not a valid UUID * `422 invalid_payload` - missing or invalid `item_ids` * `404 not_found` - folder not visible in key scope * `404 item_not_found` - one or more item IDs are not in the folder * `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 folder_items_remove_failed` - unexpected failure # Render Slideshow Source: https://docs.genviral.io/api-reference/render-slideshow POST /api/partner/v1/slideshows/{slideshowId}/render Render slideshow slides into final images ready for posting to media-capable accounts. Produces optimized slide assets with text overlays that your OpenClaw agent can immediately publish via the Create Post endpoint. Triggers slideshow rendering and returns the refreshed slideshow payload. ## Path Parameters Slideshow UUID. This endpoint has no request body. ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/slideshows/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0/render \ --header 'Authorization: Bearer ' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Slideshow rendered", "data": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "status": "rendered", "preview_image_url": "https://cdn.example.com/slideshows/2ab58b/preview.jpg", "last_rendered_at": "2026-02-14T09:31:44.000Z", "slide_count": 5 } } ``` ## Error Responses * `400 missing_slideshow_id` - path param missing * `404 not_found` - slideshow is missing/outside key scope * `422 no_slides` - slideshow has no slides to render * `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 render_failed` - unexpected render failure # Response Pattern Source: https://docs.genviral.io/api-reference/response Consistent JSON response envelope and error handling for the Genviral social media posting API. Every endpoint - TikTok, Instagram, YouTube, Pinterest, LinkedIn, Facebook - returns the same typed structure for easy parsing by AI agents and automation scripts. > Understanding responses, error handling, and status codes for every Partner API endpoint. Designed for easy parsing by OpenClaw agents, Claude Code, and any programmatic integration. ## Envelope Structure All endpoints return the same typed envelope so you can branch on `ok` once and stay type-safe everywhere else. Indicates success (`true`) or failure (`false`). Mirrors the HTTP status code returned by the endpoint. Human-readable context for logs or UI surfaces. Present only when `ok` is `true`. Contains the payload documented for each endpoint. Optional machine-friendly identifier for failures (e.g., `invalid_token`, `subscription_required`). ```json theme={null} { "ok": true, "code": 200, "message": "Accounts retrieved", "data": { "...": "..." } } ``` ```json theme={null} { "ok": false, "code": 401, "message": "API key not found", "error_code": "invalid_token" } ``` When `ok` is `true`, `data` is guaranteed to exist. When `ok` is `false`, `data` is omitted, so you can rely on that distinction for strict typing in TypeScript, Swift, or Go clients. ## Sample Handling ```ts theme={null} const res = await fetch('https://www.genviral.io/api/partner/v1/accounts', { headers: { Authorization: `Bearer ${process.env.GENVIRAL_TOKEN}` }, }); const payload = await res.json(); if (!payload.ok) { throw new Error(`Genviral error ${payload.code} – ${payload.message}`); } payload.data.accounts.forEach((account: any) => { console.log(account.id, account.type, account.platform); }); ``` ```python theme={null} response = requests.get( "https://www.genviral.io/api/partner/v1/posts", headers={"Authorization": f"Bearer {GENVIRAL_TOKEN}"} ) data = response.json() if data["ok"]: print(data["data"]["summary"]) else: raise RuntimeError(f"Genviral error {data['code']}: {data['message']}") ``` ## Common Status Codes | Code | Meaning | Notes | | ----- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `200` | Success | Returned for GET/PATCH requests, `POST /posts/retry`, and idempotent `POST /posts` replays that return an existing post instead of creating a new one. | | `201` | Created | Returned by fresh create-style requests such as `POST /posts`, `POST /slideshows/generate`, `POST /packs`, `POST /packs/:pack_id/images`, `POST /templates`, `POST /templates/from-slideshow/:slideshow_id`, and `POST /slideshows/:slideshow_id/duplicate`. | | `202` | Accepted | Returned for async-start endpoints such as `POST /studio/videos/generate`. | | `400` | Bad Request | Missing/invalid parameters, unreachable media URLs, etc. | | `401` | Unauthorized | API key missing, malformed, or revoked. | | `402` | Subscription Required | Active Creator, Professional, or Business subscription is required. | | `403` | Forbidden | Tier or scope denied (e.g., `tier_not_allowed` for Scheduler). | | `404` | Not Found | Resource doesn’t exist in the authenticated key scope. | | `422` | Unprocessable Entity | JSON body parsed but failed schema validation (invalid payload). | | `429` | Too Many Requests | Back off-respect concurrency guidance (3–10 requests at a time). | | `500` | Internal Server Error | Rare; retry with exponential backoff and contact support if needed. | `error_code` is the fastest way to branch on known issues (e.g., `invalid_media_url`, `post_not_mutable`). Log both `code` and `error_code` for observability. ## Handling `422 invalid_payload` When payload validation fails at the schema/shape layer, Partner API returns `422` with schema-level and field-level details. Account-aware rules that require resolved account metadata (for example a caption that is valid for Facebook but too long for a selected Pinterest account, or a text-only post sent to TikTok) surface later as `400 validation_failed`. Unknown provider keys under `settings` are rejected here because Partner API fails closed on unrecognized platform settings. ```json theme={null} { "ok": false, "code": 422, "message": "Invalid payload", "error_code": "invalid_payload", "issues": { "formErrors": [], "fieldErrors": { "settings": ["Unrecognized key(s) in object: 'mastodon'"], "caption": ["String must contain at most 63206 character(s)"], "accounts": ["Expected object, received string"] } } } ``` Recommended handling: 1. Detect `code === 422` and `error_code === "invalid_payload"`. 2. Read both `issues.formErrors` and `issues.fieldErrors`. 3. Fix field shape/data and retry. 4. Do not retry unchanged payloads. ```ts theme={null} if (!payload.ok && payload.code === 422 && payload.error_code === 'invalid_payload') { const formErrors = payload.issues?.formErrors ?? []; const fieldErrors = payload.issues?.fieldErrors ?? {}; console.error('Invalid payload', { formErrors, fieldErrors }); throw new Error('Fix payload shape before retrying'); } ``` # Retry Post Source: https://docs.genviral.io/api-reference/retry-posts POST /api/partner/v1/posts/retry Retry failed or partial posts Reset failed/partial posts so they re-enter the scheduler. This lets you retrigger multiple posts (or a subset of their accounts) after fixing upstream media/platform issues. ## Body Parameters Array of post IDs (min 1, max 20) to retry. Each post must belong to your authenticated key scope; IDs outside scope are rejected per entry. Optional filter (max 10). When provided, only the matching account rows on each post are retried; otherwise, all failed/partial accounts are reset. Must contain at least one UUID when present. ## Behavior ```bash cURL theme={null} curl --request POST \ --url https://www.genviral.io/api/partner/v1/posts/retry \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "post_ids": [ "11111111-1111-1111-1111-111111111111", "22222222-2222-2222-2222-222222222222" ], "account_ids": ["0f4f54d4-8cce-4fb7-8c7b-befbcb8af812"] }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Posts queued for retry", "data": { "retried": 2, "ids": [ "11111111-1111-1111-1111-111111111111", "22222222-2222-2222-2222-222222222222" ], "failed": ["33333333-3333-3333-3333-333333333333"], "errors": { "33333333-3333-3333-3333-333333333333": "Only failed or partial posts can be retried" } } } ``` The `errors` object only appears when at least one post/account could not be retried. Successful responses without failures omit the field entirely. ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - payload failed schema rules (missing IDs, too many IDs) * `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 retry_failed` - unexpected database error (partial successes are still reported in the body) # Slideshow Packs – Reusable Image Collections for Automated Content Source: https://docs.genviral.io/api-reference/slideshow-packs Manage reusable image packs for AI slideshow generation. Upload images, organize collections, and reference them across TikTok slideshows, Instagram carousels, and automated posting workflows. Built for OpenClaw agents and programmatic content creation. > Organize images into reusable packs that power AI slideshow generation across TikTok, Instagram, Pinterest, and more. Your OpenClaw agent can manage packs programmatically - uploading images, creating collections, and referencing them in automated slideshow workflows. Packs are reusable image collections used by slideshow generation. For AI agents automating content creation on TikTok, Instagram, YouTube, Pinterest, LinkedIn, or Facebook, packs let you organize brand assets, product photos, or AI-generated images into named collections that can be referenced across multiple slideshows. ## Scope Rules * Workspace keys can mutate only packs in their workspace. * Personal keys can mutate only personal packs (`workspace_id IS NULL`). * Public packs are readable (when included) but not mutable unless owned in scope. ## Endpoint Index | Operation | Endpoint | Docs | | ---------------- | -------------------------------------------------------- | ----------------------------------------------------- | | List packs | `GET /api/partner/v1/packs` | [List Packs](/api-reference/list-packs) | | Get pack | `GET /api/partner/v1/packs/{packId}` | [Get Pack](/api-reference/get-pack) | | Create pack | `POST /api/partner/v1/packs` | [Create Pack](/api-reference/create-pack) | | Update pack | `PATCH /api/partner/v1/packs/{packId}` | [Update Pack](/api-reference/update-pack) | | Delete pack | `DELETE /api/partner/v1/packs/{packId}` | [Delete Pack](/api-reference/delete-pack) | | Add image by URL | `POST /api/partner/v1/packs/{packId}/images` | [Add Pack Image](/api-reference/add-pack-image) | | Delete image | `DELETE /api/partner/v1/packs/{packId}/images/{imageId}` | [Delete Pack Image](/api-reference/delete-pack-image) | ## Choosing Image Input Mode Use this decision rule when adding pack images: 1. If you already have a public image URL, call [Add Pack Image](/api-reference/add-pack-image) directly. 2. If you only have file bytes, call [Upload File](/api-reference/upload-file) first, then call [Add Pack Image](/api-reference/add-pack-image) with the returned `url`. 3. `Add Pack Image` stores a URL reference in the pack. It does not fetch or re-host the file at add time. ## Typical Flows ### Flow A: Existing image URLs 1. Create a pack. 2. Add images by URL (`POST /api/partner/v1/packs/{packId}/images`). 3. Use the pack ID in slideshow generation (`pack_id` / `pack_assignments`). ### Flow B: Local files / raw bytes 1. Create a pack. 2. Call `POST /api/partner/v1/files` to get `uploadUrl` + `url`. 3. Upload bytes with `PUT uploadUrl`. 4. Call `POST /api/partner/v1/packs/{packId}/images` with `image_url = url`. 5. Use the pack ID in slideshow generation (`pack_id` / `pack_assignments`). # Slideshow Templates – Reusable Blueprints for Automated Slideshow Creation Source: https://docs.genviral.io/api-reference/slideshow-templates Create and manage reusable slideshow templates for consistent TikTok photo carousels, Instagram carousels, and cross-platform content. Perfect for OpenClaw agents and AI automation workflows that need repeatable, brand-consistent slideshow formats. > Save winning slideshow formats as templates and reuse them across automated posting workflows. Your OpenClaw agent can create templates from high-performing slideshows, then generate new content at scale using proven layouts for TikTok, Instagram, Pinterest, LinkedIn, YouTube, and Facebook. Templates are reusable slideshow blueprints (`config`) you can apply across generation flows. When your AI agent finds a slideshow format that performs well on TikTok or Instagram, save it as a template and reuse the layout, text positioning, and slide structure for future content. ## Visibility Model * `private`: visible to owner scope (workspace keys also read owner-private templates in current workspace context). * `workspace`: visible in that workspace only. * `public`: globally readable. ## Endpoint Index | Operation | Endpoint | Docs | | --------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------- | | List templates | `GET /api/partner/v1/templates` | [List Templates](/api-reference/list-templates) | | Get template | `GET /api/partner/v1/templates/{templateId}` | [Get Template](/api-reference/get-template) | | Create template | `POST /api/partner/v1/templates` | [Create Template](/api-reference/create-template) | | Update template | `PATCH /api/partner/v1/templates/{templateId}` | [Update Template](/api-reference/update-template) | | Delete template | `DELETE /api/partner/v1/templates/{templateId}` | [Delete Template](/api-reference/delete-template) | | Create from slideshow | `POST /api/partner/v1/templates/from-slideshow/{slideshowId}` | [Create Template From Slideshow](/api-reference/create-template-from-slideshow) | ## Config Contract (v1) Template `config` must follow versioned structure: * `version` * `structure.slides[]` (+ optional `structure.packAssignments`) * `content` * `visuals` See full examples on [Create Template](/api-reference/create-template). # Slideshows – AI Slideshow Generation API for TikTok, Instagram & More Source: https://docs.genviral.io/api-reference/slideshows Generate AI-powered slideshows for TikTok photo carousels, Instagram carousels, Pinterest pins, and more. Create, edit, render, and post slideshows programmatically - perfect for OpenClaw agents and automated content pipelines. > Generate AI slideshows, render them as TikTok photo carousels or Instagram carousels, and post them to media-capable accounts - all through the API. The full content creation pipeline for OpenClaw agents and AI-driven social media automation. Use slideshow endpoints to build slide content, iterate on text/layout, render final images, and publish those images through the posting API. This is the core of the automated content pipeline - your OpenClaw agent or automation script generates slideshows, and the [Create Post](/api-reference/create-post) endpoint distributes them to media-capable accounts. All slideshow routes are key-scoped and use the standard Partner API envelope documented in [Response Pattern](/api-reference/response). ## Scope Rules * Workspace keys read and write slideshow rows in their workspace. * Personal keys read and write rows where `user_id = owner_user_id` and `workspace_id IS NULL`. * Cross-scope resources return `404 not_found`. ## How It Works Think of slideshows as a content object you can refine before posting: 1. Create a slideshow (`generate`) with AI/manual config, or copy from TikTok. 2. Iterate on draft content (`get`, `update`, optional `regenerate` per slide). 3. Render when ready (`render`) to produce final slide assets. 4. Publish those slide image URLs using [Create Post](/api-reference/create-post) with `media.type = "slideshow"` and `media.urls`. ## Typical End-to-End Flow 1. Create/choose a pack (optional but common): [Slideshow Packs](/api-reference/slideshow-packs) 2. Generate a slideshow: [Generate Slideshow](/api-reference/generate-slideshow) 3. Refine the draft: [Get Slideshow](/api-reference/get-slideshow), [Update Slideshow](/api-reference/update-slideshow), [Regenerate Slide](/api-reference/regenerate-slide) 4. Render finalized slides: [Render Slideshow](/api-reference/render-slideshow) 5. Send rendered slide URLs to posts API: ```json theme={null} { "caption": "My slideshow post", "media": { "type": "slideshow", "urls": ["https://cdn.example.com/slide-1.jpg", "https://cdn.example.com/slide-2.jpg"] }, "accounts": [{ "id": "0f4f54d4-8cce-4fb7-8c7b-befbcb8af812" }] } ``` For publishing, use final image URLs from the slideshow response. After render, this is typically `slides[].rendered_image_url` (fallback to `image_url` if a rendered URL is not present). ## Endpoint Index | Operation | Endpoint | Docs | | -------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------- | | Preview TikTok copy | `POST /api/partner/v1/slideshows/copy-tiktok/preview` | [Preview TikTok Slideshow Copy](/api-reference/copy-tiktok-preview) | | Import TikTok copy | `POST /api/partner/v1/slideshows/copy-tiktok/import` | [Import TikTok Slideshow Copy](/api-reference/copy-tiktok-import) | | Generate slideshow | `POST /api/partner/v1/slideshows/generate` | [Generate Slideshow](/api-reference/generate-slideshow) | | List slideshows | `GET /api/partner/v1/slideshows` | [List Slideshows](/api-reference/list-slideshows) | | Get slideshow | `GET /api/partner/v1/slideshows/{slideshowId}` | [Get Slideshow](/api-reference/get-slideshow) | | Update slideshow | `PATCH /api/partner/v1/slideshows/{slideshowId}` | [Update Slideshow](/api-reference/update-slideshow) | | Delete slideshow | `DELETE /api/partner/v1/slideshows/{slideshowId}` | [Delete Slideshow](/api-reference/delete-slideshow) | | Render slideshow | `POST /api/partner/v1/slideshows/{slideshowId}/render` | [Render Slideshow](/api-reference/render-slideshow) | | Duplicate slideshow | `POST /api/partner/v1/slideshows/{slideshowId}/duplicate` | [Duplicate Slideshow](/api-reference/duplicate-slideshow) | | Regenerate one slide | `POST /api/partner/v1/slideshows/{slideshowId}/slides/{slideIndex}/regenerate` | [Regenerate Slide](/api-reference/regenerate-slide) | ## Validation Highlights * Standard generation and manual setup both support `1-10` slides. * Any `image_pack` slide must resolve a pack through global `pack_id` or per-slide `pack_assignments[index]`. * `slide_config` index maps must use 0-based numeric keys inside range. * Updating `slides` on a rendered slideshow resets it to `draft` and clears rendered artifacts. ## Packs and Templates * Reusable image sets: [Slideshow Packs](/api-reference/slideshow-packs) * Reusable slideshow blueprints: [Slideshow Templates](/api-reference/slideshow-templates) ## UI vs API * UI is usually the fastest way to curate packs and templates manually. * API is better for automation, external pipelines, and agent-driven workflows - especially when using OpenClaw agents or Claude Code to automate TikTok slideshow creation and posting. * Both paths use the same underlying slideshow, pack, and template models. ## Agent Automation Tips If you're building an OpenClaw agent or AI automation pipeline, here's the optimal slideshow workflow: 1. **Batch generate** - Create multiple slideshows in sequence, each with different hooks and styles 2. **Render in bulk** - Render all slideshows before posting to avoid delays 3. **Post as drafts to TikTok** - Use `tiktok.post_mode: "MEDIA_UPLOAD"` so you can add trending sounds manually 4. **Post directly to other platforms** - Instagram, YouTube, Pinterest, LinkedIn, and Facebook support direct posting 5. **Track analytics** - Use the [Analytics endpoints](/api-reference/get-analytics-summary) to see what's performing and feed that data back to your agent See our [official OpenClaw skill](https://github.com/fdarkaou/genviral-skill) for a ready-made implementation of this workflow. # Studio AI Source: https://docs.genviral.io/api-reference/studio-ai Generate AI Studio images and videos through Partner API with model parity, credits parity, and canonical video polling. Use Studio AI endpoints to generate images and videos with the same model catalog and credit rules used in-app. All routes are key-scoped and use the standard Partner API response envelope documented in [Response Pattern](/api-reference/response). ## Endpoints | Endpoint | Path | Docs | | -------------- | --------------------------------------------- | ------------------------------------------------------------- | | List models | `GET /api/partner/v1/studio/models` | [Get Studio Models](/api-reference/get-studio-models) | | Generate image | `POST /api/partner/v1/studio/images/generate` | [Generate Studio Image](/api-reference/generate-studio-image) | | Generate video | `POST /api/partner/v1/studio/videos/generate` | [Generate Studio Video](/api-reference/generate-studio-video) | | Video status | `GET /api/partner/v1/studio/videos/{videoId}` | [Get Studio Video](/api-reference/get-studio-video) | ## Polling Flow 1. Call `POST /studio/videos/generate`. 2. Store `data.video_id`. 3. Poll `GET /studio/videos/{videoId}` until `data.status` is `succeeded` or `failed`. 4. Use `data.output_url` once status is `succeeded`. ## Input Contract Studio generation endpoints use normalized partner input (`snake_case`) with optional `raw_params` for model-specific passthrough: * `params`: stable normalized keys (`aspect_ratio`, `duration_seconds`, etc.) * `raw_params`: optional advanced passthrough object This keeps client payloads consistent while still allowing model-specific controls. # Update Analytics Target Source: https://docs.genviral.io/api-reference/update-analytics-target PATCH /api/partner/v1/analytics/targets/{id} Update display/refresh/favorite settings for one target Updates settings for a single tracked analytics target. ## Path Parameters Analytics target ID. ## Body Parameters At least one of these fields is required: New display name (nullable). Refresh policy patch (for example `freeDailyRefresh`, `maxPosts`). Favorite toggle stored in metadata. ```bash cURL theme={null} curl --request PATCH \ --url https://www.genviral.io/api/partner/v1/analytics/targets/e7a7f417-56db-4320-9e9a-fd6fdabda09d \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "favorite": true }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Analytics target updated", "data": { "id": "e7a7f417-56db-4320-9e9a-fd6fdabda09d", "platform": "tiktok", "identifier": "@brand", "display_name": "Brand HQ", "metadata": { "favorite": true } } } ``` ## Error Responses * `400 missing_target_id` - missing path parameter * `400 invalid_json` - PATCH body is not valid JSON * `400 invalid_payload` - PATCH body must be a JSON object * `400` - no valid updates provided * `404` - target not found in current key scope * `401`/`403` - authentication failed or key lacks workspace access * `500` - unexpected database/analytics service error All error responses from analytics endpoints include an `error_code` field. # Update Pack Source: https://docs.genviral.io/api-reference/update-pack PATCH /api/partner/v1/packs/{packId} Update pack name and/or visibility Updates mutable pack fields. ## Path Parameters Pack UUID. ## Body Parameters Optional new name (`1-120` chars after trimming). Optional visibility update. Provide at least one field (`name` or `is_public`). Empty payloads return `422 invalid_payload`. ```bash cURL theme={null} curl --request PATCH \ --url https://www.genviral.io/api/partner/v1/packs/11111111-1111-1111-1111-111111111111 \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Motivation Pack v2", "is_public": true }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Pack updated", "data": { "id": "11111111-1111-1111-1111-111111111111", "name": "Motivation Pack v2", "image_count": 24, "is_public": true, "created_at": "2026-02-14T09:00:00.000Z", "images": [] } } ``` ## Error Responses * `400 missing_pack_id` - path param missing * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed * `404 not_found` - pack is missing/outside mutable key scope * `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 pack_update_failed` - unexpected update failure # Update Post Source: https://docs.genviral.io/api-reference/update-post PATCH /api/partner/v1/posts/{postId} Update a scheduled or pending post Update the caption, media, accounts, schedule, or external metadata for a scheduled/pending post. Only posts in `draft`, `pending`, `scheduled`, `retry`, or `failed` status can be edited. Posted, partial, or canceled posts are immutable. ## Path Parameters The ID of the post to update. ## Body Parameters Optional replacement caption. At least one field in the body is required; empty payloads are rejected with `422 invalid_payload`. Target-specific limit. Hosted Accounts targets cap captions at 500 characters. BYO caps follow the selected platform and media type: Facebook 63,206; Instagram 2,200; TikTok 2,200 for video captions / 4,000 for photo-post descriptions; LinkedIn 3,000; Pinterest 800; YouTube 5,000 bytes. Optional replacement media payload. Follows the same structure as [Create Post](/api-reference/create-post). Pass `null` to clear media and make the post text-only. Text-only updates fail when any selected account requires media. Optional list of account IDs (max 10). **Replaces** the previous targeting list completely. Optional canonical provider settings update keyed by provider/platform. Unknown provider keys fail closed with `422 invalid_payload`. Use the `settings_schema` returned by [`GET /accounts`](/api-reference/get-accounts) to build these objects. For YouTube, send `settings.youtube.title` to set the video title explicitly; if omitted, Genviral derives the title from the first non-empty caption line. Optional TikTok settings update. This is a v1 convenience alias for TikTok settings; new clients can also send provider-keyed settings under `settings.tiktok`. Supported only when every targeted account is a TikTok BYO account. Optional TikTok title override. Video posts: max 2,200 UTF-16 runes. Photo/slideshow posts: max 90 UTF-16 runes. Optional TikTok description override. Photo/slideshow posts: max 4,000 UTF-16 runes. For video posts, Genviral keeps this field for compatibility and uses it as a fallback source when deriving the TikTok title if `title` is blank. `DIRECT_POST` or `MEDIA_UPLOAD` (uploads to TikTok inbox/drafts). Optional privacy level (`PUBLIC_TO_EVERYONE`, `MUTUAL_FOLLOW_FRIENDS`, `FOLLOWER_OF_CREATOR`, `SELF_ONLY`). Optional comment toggle. Optional duet toggle (video posts). Optional stitch toggle (video posts). Optional video cover frame offset in milliseconds (video posts, `DIRECT_POST` only). TikTok uses this frame as the video thumbnail. Optional TikTok policy consent flag. Optional commercial-content flag. Optional branded-content flag for own brand. Optional branded-content flag for third-party promotion. Optional TikTok auto-music toggle (photo posts). Pass `null` to clear stored TikTok settings from the post. Optional Pinterest settings update. This is a v1 convenience alias for Pinterest settings; new clients can also send provider-keyed settings under `settings.pinterest`. Supported only when at least one targeted account is a Pinterest account. Optional Pinterest board ID (max 128 chars). Optional Pinterest pin title override (max 100 chars). Optional destination URL (max 2,048 chars). Optional Pinterest topic tags (up to 30). Multi-word tags are supported. Genviral appends these tags to the Pinterest description, so `caption + appended tags` must still fit Pinterest's 800-character description limit. Pass `null` to clear stored Pinterest settings from the post. Top-level `tiktok` and `pinterest` are preserved v1 aliases. Prefer `settings.` for new multi-platform clients, and only include settings for providers present in the selected accounts. Optional ISO timestamp. Pass `null` to push the post back into the "publish ASAP" queue (`pending`). Not mutable after creation. Partner API treats `external_id` as the create-time idempotency key, so PATCH requests that try to change it return `409 external_id_immutable`. Optional TikTok post URL for background music (e.g., `https://www.tiktok.com/@genviral/video/1234567890`). Pass `null` to remove existing music without changing media. This field is TikTok-only. Instagram's official publishing API does not support music/sound selection for carousels or Reels, so update requests that include Instagram accounts with `music_url` will be rejected. ## Limits * Caption: enforced against the targeted accounts. Hosted Accounts targets stay at 500 characters. BYO caps follow platform limits: Facebook 63,206; Instagram 2,200; TikTok 2,200 for video captions and 4,000 for photo-post descriptions; LinkedIn 3,000; Pinterest 800; YouTube 5,000 bytes. * Text-only: set `media` to `null` or omit media while updating other fields; every selected account must advertise `text_only` in `/accounts` capabilities. * 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: TikTok-only. Instagram's official API does not support music/sound selection for carousel posts or Reels, so requests with `music_url` are rejected 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). * `tiktok.video_cover_timestamp_ms`: supported for video `DIRECT_POST` requests. * Pinterest settings: supported only when at least one selected account is Pinterest. * `pinterest.tags`: up to 30 tags, each 1–100 characters (spaces allowed). Tags are appended to the final Pinterest description, which still must fit 800 characters. * Provider settings: send canonical settings under `settings.`. Unknown provider keys are rejected with `422 invalid_payload`. See the [Create Post platform matrix](/api-reference/create-post#platform-matrix) for the current per-platform mapping details and source gaps. ```bash cURL theme={null} curl --request PATCH \ --url https://www.genviral.io/api/partner/v1/posts/11111111-1111-1111-1111-111111111111 \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "caption": "Updated caption", "media": null, "music_url": null, "scheduled_at": "2025-03-01T17:00:00Z" }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Post updated", "data": { "id": "11111111-1111-1111-1111-111111111111", "status": "scheduled", "scheduled_at": "2025-03-01T17:00:00Z", "warnings": [ { "field": "media", "message": "Video size metadata is missing", "code": "VIDEO_METADATA_MISSING" } ] } } ``` Update responses can include `warnings` about missing media metadata. They are informational so you can re-upload media before Hosted Accounts reject it. ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - no editable fields provided or schema validation failed * `400 unknown_accounts` - one or more provided accounts are outside the authenticated key scope * `400 missing_accounts` - resolved account list is empty after validation * `400 validation_failed` - post is immutable or media/music/caption rules failed * `400 invalid_music_url` or `400 media_unreachable` - TikTok/music URLs failed validation * `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 * `404 not_found` - post ID is outside the authenticated key scope * `500 update_failed` - Database update failed unexpectedly # Update Slideshow Source: https://docs.genviral.io/api-reference/update-slideshow PATCH /api/partner/v1/slideshows/{slideshowId} Update slideshow metadata, settings, product link, or full slide content Apply a partial update to a slideshow. ## Path Parameters Slideshow UUID. ## Body Parameters Optional title override. Optional status update: `draft` or `rendered`. Optional: `educational` or `personal`. Optional product link update. Set `null` to detach. Optional partial settings patch. Also accepts legacy camelCase `imagePackId`. `9:16`, `1:1`, or `4:5`. `educational` or `personal`. `font_size` (`default`, `small`, or numeric `8-200`), `text_preset`, `text_width`. Per-slide pack overrides. Optional full slide replacement array. At least one element per slide. Element fields: `content`, `x`, `y`, optional `id`, `font_size`, `width`, `height`, `editable`, `style_preset`, `font_family`, `background_color`, `text_color`, `border_radius`. Background image URL or `null`. Optional collage image URLs. `2x2`, `1+2`, `vertical`, or `horizontal`. Optional filter object with numeric fields: `brightness`, `contrast`, `saturation`, `hue`, `blur`, `grayscale`, `sepia`, `invert`, `drop_shadow`, `opacity`. Optional overlays (`id`, `image_url`, `x`, `y`, `width`, `height`, `rotation`, `opacity`). Body cannot be empty. Send at least one updatable field. If you update `slides` on a slideshow currently in `rendered` status, the API automatically resets it to `draft` and clears render artifacts (`preview_image_url`, `last_rendered_at`, and per-slide `rendered_image_url`). ```bash cURL theme={null} curl --request PATCH \ --url https://www.genviral.io/api/partner/v1/slideshows/2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0 \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "title": "5 discipline quotes (v2)", "settings": { "aspect_ratio": "9:16", "advanced_settings": { "text_width": "narrow" } } }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Slideshow updated", "data": { "id": "2ab58bb0-0c39-45c0-a4d5-b6852f9d7fc0", "title": "5 discipline quotes (v2)", "status": "draft", "slideshow_type": "educational", "product_id": null, "slide_count": 5, "settings": { "image_pack_id": "11111111-1111-1111-1111-111111111111", "aspect_ratio": "9:16", "slideshow_type": "educational", "advanced_settings": { "font_size": 44, "text_preset": "tiktok", "text_width": "narrow" }, "pack_assignments": null } } } ``` ## Error Responses * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed or body is empty * `404 not_found` - slideshow is missing/outside key scope * `404 product_not_found` - referenced `product_id` does not exist * `403 forbidden_product_access` - referenced `product_id` is outside key scope * `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 update_failed` - unexpected update failure # Update Template Source: https://docs.genviral.io/api-reference/update-template PATCH /api/partner/v1/templates/{templateId} Update mutable template fields Updates a template you own and can mutate in the authenticated scope. ## Path Parameters Template UUID. ## Body Parameters Optional new name (`1-100` chars after trimming). Must remain owner-unique. Optional description update. Pass `null` to clear. Optional: `private` or `workspace`. Optional full config replacement. Must satisfy config v1 schema. Body cannot be empty. Send at least one updatable field. `visibility: "workspace"` requires workspace scope. ```bash cURL theme={null} curl --request PATCH \ --url https://www.genviral.io/api/partner/v1/templates/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "name": "Hooks Template v2", "description": null, "visibility": "workspace" }' ``` ```json Response theme={null} { "ok": true, "code": 200, "message": "Template updated", "data": { "id": "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name": "Hooks Template v2", "description": null, "visibility": "workspace", "preview_url": null, "use_count": 12, "last_used_at": "2026-02-14T09:11:00.000Z", "created_at": "2026-02-12T15:00:00.000Z", "updated_at": "2026-02-14T10:00:00.000Z", "is_owner": true, "config": { "version": 1, "structure": { "slides": [ { "type": "pack", "id": "11111111-1111-1111-1111-111111111111", "pinnedImageUrl": null, "textElements": [ { "id": "22222222-2222-2222-2222-222222222222", "content": "Hook text", "x": 50, "y": 25, "fontSize": 48, "width": 70 } ] } ] }, "content": { "mode": "manual", "productId": null, "language": "en", "slideshowType": "educational" }, "visuals": { "packId": null, "aspectRatio": "4:5", "textSettings": { "fontSize": "default", "preset": "tiktok", "width": "default" } } } } } ``` ## Error Responses * `400 missing_template_id` - path param missing * `400 invalid_json` - body is not valid JSON * `422 invalid_payload` - schema validation failed or body is empty * `404 not_found` - template not found in readable scope * `403 forbidden_template` - template exists but mutation is not allowed in current scope * `409 template_name_conflict` - name already exists for owner * `422 invalid_template_config` - provided config is invalid * `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 template_update_failed` - unexpected update failure # Upload File Source: https://docs.genviral.io/api-reference/upload-file POST /api/partner/v1/files Upload images and videos to Genviral CDN for use in TikTok slideshows, Instagram carousels, YouTube Shorts, Pinterest pins, LinkedIn posts, and Facebook content. Supports programmatic uploads from OpenClaw agents and automation pipelines. Upload media files directly to Genviral's CDN. The returned `url` can then be used when creating posts via `/posts` or attaching images to packs via `/packs/{packId}/images`. The stored path is derived from key scope (`partner-api/workspaces/{workspaceId}` or `partner-api/users/{ownerUserId}`). ## How It Works 1. Call this endpoint with the file's content type 2. Receive a presigned `uploadUrl` and the final `url` (CDN URL) 3. Upload your file directly to the `uploadUrl` using a PUT request 4. Use the `url` in post creation requests or pack-image attachment requests ## Body Parameters MIME type of the file. Supported types: * **Images**: `image/jpeg`, `image/png`, `image/gif`, `image/webp`, `image/heic`, `image/heif` * **Videos**: `video/mp4`, `video/quicktime`, `video/x-msvideo`, `video/webm`, `video/x-m4v` Original filename for reference (optional). Used for display purposes only. Optional video duration in seconds. Also accepts `duration_seconds`, `duration`, `durationSec`, or `video_duration_sec`. Stored with the CDN file record so `/posts` can hydrate validation metadata when you use the returned `url`. Optional file size in bytes. Also accepts `size`. ## Response Successful requests return `201` with: * `uploadUrl` - Presigned URL to upload your file (expires in 10 minutes) * `url` - CDN URL where your file will be accessible after upload * `contentType` - The content type you specified * `expiresIn` - Seconds until the upload URL expires (600) ## Using With Packs If your goal is to add a local file to a pack: 1. Call this endpoint and capture `data.uploadUrl` + `data.url`. 2. Upload your bytes to `data.uploadUrl` with `PUT`. 3. Call [Add Pack Image](/api-reference/add-pack-image) with `image_url = data.url`. ## Examples ### Upload a video ```javascript theme={null} import fetch from "node-fetch"; // Step 1: Get upload URL const response = await fetch("https://www.genviral.io/api/partner/v1/files", { method: "POST", headers: { Authorization: "Bearer ", "Content-Type": "application/json", }, body: JSON.stringify({ contentType: "video/mp4", filename: "my-video.mp4", duration_sec: 42, bytes: 8_000_000, }), }); const { data } = await response.json(); // data.uploadUrl = presigned S3 URL // data.url = https://cdn.vireel.io/partner-api/workspaces/{workspaceId}/... (or /users/{ownerUserId}/...) // Step 2: Upload file to presigned URL const fileBuffer = fs.readFileSync("./my-video.mp4"); await fetch(data.uploadUrl, { method: "PUT", headers: { "Content-Type": "video/mp4", }, body: fileBuffer, }); // Step 3: Use the CDN URL in your post await fetch("https://www.genviral.io/api/partner/v1/posts", { method: "POST", headers: { Authorization: "Bearer ", "Content-Type": "application/json", }, body: JSON.stringify({ caption: "Check this out!", media: { type: "video", url: data.url, // Use the CDN URL }, accounts: [{ id: "account-id" }], }), }); ``` ```bash theme={null} # Step 1: Get upload URL curl --request POST \ --url https://www.genviral.io/api/partner/v1/files \ --header 'Authorization: Bearer ' \ --header 'Content-Type: application/json' \ --data '{ "contentType": "video/mp4", "filename": "my-video.mp4", "duration_sec": 42, "bytes": 8000000 }' # Step 2: Upload to presigned URL (use uploadUrl from response) curl --request PUT \ --url "" \ --header 'Content-Type: video/mp4' \ --data-binary @my-video.mp4 # Step 3: Use url in your post request ``` ```json Response theme={null} { "ok": true, "code": 201, "message": "Upload URL generated", "data": { "uploadUrl": "https://storage.example.com/presigned-url...", "url": "https://cdn.vireel.io/partner-api/workspaces/workspace_123/abc123.mp4", "contentType": "video/mp4", "expiresIn": 600 } } ``` ## Error Responses * `400 invalid_json` - Request body is not valid JSON * `422 invalid_payload` - Invalid content type or missing required fields * `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` - Failed to initialize upload (retry) The presigned upload URL expires after 10 minutes. If it expires before you upload, simply request a new one. `POST /files` reserves the CDN URL and upload slot. The file is ready for use only after your `PUT` to `uploadUrl` succeeds (HTTP 200/204). If needed, verify reachability with a `HEAD`/`GET` against `data.url` before creating a post.