> For the complete documentation index, see [llms.txt](https://help.blotato.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://help.blotato.com/api-and-mcp-concepts/workflows.md).

# REST workflow examples

REST source, visual, and publication sequences with operation IDs, polling intervals, and completion checks.

Publishing, source extraction, and visual generation use the Submit → Poll → Result pattern. Read the [completion rules](/api-and-mcp-concepts/async-jobs-and-polling.md) for the exact operation. For task instructions using MCP, start with [Agent workflows](/agent-workflows/agent-workflows.md).

## System Prompt for Agents

You can use the following snippet to instruct your AI agent on how to interact with Blotato:

> **Blotato API Protocol**: All creation operations (posts, videos, sources) are **async**.
>
> 1. Call the `CREATE` endpoint. Record the ID from the 201 response (`id` for sources, `item.id` for videos, `postSubmissionId` for posts). A scheduled post also returns `scheduledTime`, the resolved UTC publish time.
> 2. Check the `GET` endpoint at least 10 seconds apart for posts and sources, and at least 15 seconds apart for visuals.
> 3. Continue polling while status is processing (see status values below).
> 4. Stop on a terminal result. Report success only for the operation's success state, not for a failure or draft.
> 5. Set a waiting limit. Preserve the operation ID if processing remains unfinished. Do not submit a duplicate write because a poll failed or time elapsed.
>
> **Terminal Status Values by Operation**:
>
> * **Sources**: `completed` (success) or `failed`
> * **Videos**: `done` (success), `creation-from-template-failed`, `insufficient-credits`, or any response with `item.error`. A `draft` is not a rendered result.
> * **Posts**: `published` (success), `failed`, or `scheduled` for a future publication. Stop immediate polling after a scheduled result.
>
> **All Video Status Values** (in order): `queueing` -> `generating-script` -> `script-ready` -> `generating-media` -> `media-ready` -> `exporting` -> `done`
>
> **Always fetch accounts first**: `GET /v2/users/me/accounts` to get `accountId` for publishing. To get `pageId` for Facebook/LinkedIn or `playlistIds` for YouTube, also fetch `GET /v2/users/me/accounts/{accountId}/subaccounts`.
>
> For social platforms, set `content.platform` and `target.targetType` to the same value (e.g., both `"twitter"`). The [REST webhook target](/rest-api-reference/publish-post.md#webhook) uses `other` with `webhook`.
>
> **Content Calendar**: `GET /v2/schedules` to list scheduled posts. `PATCH /v2/schedules/:id` to update content or time. `DELETE /v2/schedules/:id` to cancel. `GET /v2/schedule/slots` for recurring time slots.
>
> Full reference: [API reference for agents](/start-with-an-ai-agent/llm.md).

***

## 1. Create Source -> Get Source

**Goal**: Research a topic or extract content from a URL so Blotato can generate related visuals.

**Endpoints**:

* Create: `POST /v2/source-resolutions-v3`
* Poll: `GET /v2/source-resolutions-v3/:id`

**Source Types**:

* `youtube`, `tiktok`, `article`, `pdf`, `audio`, `twitter` - Extract content from a URL (each type requires a `url` field)
* `text` - Transform raw text content with optional AI instructions
* `perplexity-query` - AI-powered web research on any topic (requires a `text` field)

```mermaid
sequenceDiagram
    participant Agent
    participant API
    Note over Agent: 1. Submit Source (URL, Text, or Query)
    Agent->>API: POST /v2/source-resolutions-v3<br/>{ source: { sourceType: "...", ... } }
    API-->>Agent: 201 Created { id: "src_123" }

    Note over Agent: 2. Poll for Extraction
    loop At least 10 seconds between checks
        Agent->>API: GET /v2/source-resolutions-v3/src_123
        API-->>Agent: { status: "processing" }
        Note over Agent: Wait...
    end

    Note over Agent: 3. Receive Result
    API-->>Agent: { status: "completed", content: "Extracted text...", title: "..." }
```

### Example Payloads

**From YouTube URL**:

```json
{
  "source": {
    "sourceType": "youtube",
    "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  }
}
```

**From AI Research Query** (use this for agents to research topics):

```json
{
  "source": {
    "sourceType": "perplexity-query",
    "text": "latest trends in AI-generated content for social media"
  }
}
```

**From Text with Custom Instructions**:

```json
{
  "source": {
    "sourceType": "text",
    "text": "Your raw content here..."
  },
  "customInstructions": "Summarize in 5 bullet points for Instagram carousel"
}
```

***

## 2. Create Visual -> Get Visual

**Goal**: Generate a video or image from a template.

**Endpoints**:

* Create: `POST /v2/videos/from-templates`
* Poll: `GET /v2/videos/creations/:id`
* Templates: `GET /v2/videos/templates?fields=id,name,description,inputs`

### Discovering Templates

If you don't have a specific template ID, retrieve available templates:

```http
GET https://backend.blotato.com/v2/videos/templates?fields=id,name,description,inputs&search=carousel
```

Choose the template for the requested output. An image carousel, a narrated video, and a clip compilation use different templates and input structures. Inspect [the selected template's inputs](/rest-api-reference/create-video.md#inspect-inputs-without-generating-a-visual) before generating.

### Visual Generation Flow

```mermaid
sequenceDiagram
    participant Agent
    participant API
    Note over Agent: 0. (Optional) Discover Templates
    Agent->>API: GET /v2/videos/templates
    API-->>Agent: List of templates with IDs

    Note over Agent: 1. Submit Generation Request
    Agent->>API: POST /v2/videos/from-templates<br/>{ templateId: "...", inputs: {...}, prompt: "..." }
    API-->>Agent: 201 Created { item: { id: "vid_456", status: "queueing" } }

    Note over Agent: 2. Poll for Rendering
    loop At least 15 seconds between checks
        Agent->>API: GET /v2/videos/creations/vid_456
        API-->>Agent: { item: { status: "generating-media" } }
        Note over Agent: Wait...
    end

    Note over Agent: 3. Receive Media URLs
    API-->>Agent: { item: { status: "done", mediaUrl: "https://...", imageUrls: [...] } }
```

### Example Payload (with AI Prompt)

```json
{
  "templateId": "/base/v2/image-slideshow/5903b592-1255-43b4-b9ac-f8ed7cbf6a5f/v1",
  "inputs": {},
  "prompt": "Create a 5-slide carousel about productivity tips",
  "render": true
}
```

### Example Payload (with Manual Inputs)

Replace the example image URLs with the user's accessible media URLs before submitting. These URLs illustrate the request structure, not content to publish.

```json
{
  "templateId": "/base/v2/image-slideshow/5903b592-1255-43b4-b9ac-f8ed7cbf6a5f/v1",
  "inputs": {
    "slides": [
      {
        "imageSource": "https://example.com/slide-1.jpg",
        "textOverlay": "Introduction"
      },
      {
        "imageSource": "https://example.com/slide-2.jpg",
        "textOverlay": "Next step"
      }
    ]
  },
  "render": true
}
```

***

## 3. Create Post -> Get Post

**Goal**: Publish content to a social platform.

**Endpoints**:

* Create: `POST /v2/posts`
* Poll: `GET /v2/posts/:postSubmissionId`
* Account Lookup: `GET /v2/users/me/accounts` ([docs](/rest-api-reference/accounts.md))
* Subaccounts (for pageId): `GET /v2/users/me/accounts/:accountId/subaccounts` ([docs](/rest-api-reference/accounts.md#list-subaccounts-pages))

> **n8n / Make.com users**: Check the operations in your installed integration. If it exposes Get Post, pass the original `postSubmissionId`. Otherwise, use an HTTP request to `GET /v2/posts/{postSubmissionId}` with your Blotato credential. Do not assume all integration versions expose the same operations.

> \[!IMPORTANT] **Prerequisites**:
>
> 1. **`accountId`**: Get this from `GET /v2/users/me/accounts`. Always fetch the user's accounts before publishing. See [Accounts reference](/rest-api-reference/accounts.md).
> 2. **`pageId`**: Required for Facebook Pages and LinkedIn company Pages. Omit it for a LinkedIn personal-profile post. Get it from `GET /v2/users/me/accounts/{accountId}/subaccounts`. See [How to Get the Right IDs](/rest-api-reference/accounts.md#how-to-get-the-right-ids-for-publishing).
> 3. **`mediaUrls`**: Use existing accessible media URLs, or `mediaUrl` and `imageUrls` from an authorized visual-generation request. No upload step required for these URLs. Do not generate new media for a request to publish an existing file. Text-only posts omit media when the selected platform supports them.
> 4. **`content.platform` and `target.targetType`**: For social platforms, set both to the same value (e.g., both `"twitter"`).

### Step 0: Fetch Available Accounts (Always Do This First)

```
GET https://backend.blotato.com/v2/users/me/accounts
```

Response:

```json
{
  "items": [
    { "id": "98432", "platform": "twitter", "fullname": "Jane Smith", "username": "janesmith" }
  ]
}
```

For a Facebook Page or LinkedIn company Page, fetch subaccounts and select the requested `pageId`. Omit `pageId` for a LinkedIn personal profile. For YouTube, fetch subaccounts only when playlist placement is requested:

```
GET https://backend.blotato.com/v2/users/me/accounts/{accountId}/subaccounts
```

See [Accounts reference](/rest-api-reference/accounts.md) for full details.

### Publish Flow

```mermaid
sequenceDiagram
    participant Agent
    participant API
    Note over Agent: 0. Fetch User Accounts
    Agent->>API: GET /v2/users/me/accounts
    API-->>Agent: List of accounts with IDs

    Note over Agent: 1. Submit Post
    Agent->>API: POST /v2/posts<br/>{ post: { accountId, content, target } }
    API-->>Agent: 201 Created { postSubmissionId: "sub_789" }

    Note over Agent: 2. Poll for Publishing
    loop At least 10 seconds between checks
        Agent->>API: GET /v2/posts/sub_789
        API-->>Agent: { status: "in-progress" }
        Note over Agent: Wait...
    end

    Note over Agent: 3. Success
    API-->>Agent: { status: "published", publicUrl: "https://twitter.com/..." }
```

### Example Payload

```json
{
  "post": {
    "accountId": "98432",
    "content": {
      "text": "Hello world!",
      "mediaUrls": ["https://database.blotato.io/user_1/media/vid_456.mp4"],
      "platform": "twitter"
    },
    "target": {
      "targetType": "twitter"
    }
  }
}
```

Get `accountId` from `GET /v2/users/me/accounts`. For social platforms, set `content.platform` and `target.targetType` to the same value.

To schedule instead of publishing immediately, add `useNextFreeSlot` or `scheduledTime` as a top-level field (sibling of `post`, not inside it). See [Publish Post](/rest-api-reference/publish-post.md#request-body) for details.

***

## Complete End-to-End Workflow (Recommended for AI Agents)

This is the standard content creation sequence: research topic → create visual → publish to social.

```mermaid
graph TD
    A["Step 0: Fetch User Accounts<br/>GET /v2/users/me/accounts"] --> B["Step 1: Create Source<br/>POST /v2/source-resolutions-v3"]
    B --> C["Step 2: Poll Source<br/>GET /v2/source-resolutions-v3/:id<br/>Until status = completed"]
    C --> D["Step 3: Create Visual<br/>POST /v2/videos/from-templates<br/>Use source content in prompt"]
    D --> E["Step 4: Poll Visual<br/>GET /v2/videos/creations/:id<br/>Until status = done"]
    E --> F["Step 5: Create Post<br/>POST /v2/posts<br/>Use mediaUrl from step 4"]
    F --> G["Step 6: Poll Post<br/>GET /v2/posts/:postSubmissionId<br/>Until status = published"]
    G --> H["Done! Post is live"]

    style A fill:#e1f5ff
    style H fill:#c8e6c9
```

### Pseudocode for Agents

```
1. accountsList = GET /v2/users/me/accounts
2. sourceId = POST /v2/source-resolutions-v3 (specify sourceType: youtube, article, text, etc.)
3. LOOP: source = GET /v2/source-resolutions-v3/:sourceId
   - IF status = "completed": BREAK
   - IF status = "failed": STOP, report error
   - ELSE: WAIT at least 10 seconds, then check again
4. videoId = POST /v2/videos/from-templates (use source.content in prompt)
5. LOOP: video = GET /v2/videos/creations/:videoId
   - IF item.status = "done": BREAK
   - IF `item.error` is set: STOP, report error
   - IF item.status is "creation-from-template-failed" or "insufficient-credits": STOP, report error
   - IF item.status = "draft": STOP, report that no rendered result is available
   - ELSE: WAIT at least 15 seconds, then check again
6. postSubmissionId = POST /v2/posts (accountId from step 1, mediaUrl/imageUrls from step 5)
   - For social platforms, set content.platform and target.targetType to the same value
7. LOOP: post = GET /v2/posts/:postSubmissionId
   - IF status = "published": BREAK
   - IF status = "failed": STOP, check errorMessage
   - IF status = "scheduled": STOP, report scheduledTime
   - ELSE: WAIT at least 10 seconds, then check again
8. RETURN: post.publicUrl
```

***

## 4. Managing the Content Calendar

**Goal**: View, update, reschedule, or delete scheduled posts. Manage the recurring time slots that define your posting cadence.

**How Slots and Schedules Work Together**:

1. **Slots** define recurring time windows (e.g., "Monday 9 AM for Twitter"). Create them with `POST /v2/schedule/slots`.
2. When you publish with `useNextFreeSlot: true`, Blotato finds the next open slot matching the target platform and queues the post at that time.
3. A slot is "occupied" when a scheduled post is already queued at that time. The next `useNextFreeSlot` call skips occupied slots.
4. **Schedules** are the queued posts. **Slots** are the time windows. Manage them separately.

**Endpoints**:

* Schedules: `GET /v2/schedules`, `GET /v2/schedules/:id`, `PATCH /v2/schedules/:id`, `DELETE /v2/schedules/:id` ([docs](/rest-api-reference/schedules.md))
* Slots: `GET /v2/schedule/slots`, `POST /v2/schedule/slots`, `PATCH /v2/schedule/slots/:id`, `DELETE /v2/schedules/slots/:id`, `POST /v2/schedule/slots/next-available` ([docs](/rest-api-reference/schedule-slots.md))

### Schedule Response Shape

The `draft` field in a schedule is the same shape as the `post` object in [Publish Post](/rest-api-reference/publish-post.md):

```json
{
  "id": "sch_abc123",
  "scheduledAt": "2026-04-01T14:00:00.000Z",
  "account": {
    "id": "98432",
    "name": "Jane Smith",
    "username": "janesmith",
    "profileImageUrl": "https://...",
    "subaccountId": null,
    "subId": null,
    "subaccountName": null
  },
  "draft": {
    "accountId": "98432",
    "content": {
      "text": "Scheduled post content",
      "mediaUrls": [],
      "platform": "twitter"
    },
    "target": {
      "targetType": "twitter"
    }
  }
}
```

> **Note**: The response uses `scheduledAt` for the publish time. The update endpoint accepts `scheduledTime` as the input field name.

### Rescheduling to the Next Free Slot

The `PATCH /v2/schedules/:id` endpoint accepts `scheduledTime` but not `useNextFreeSlot`. To move a post to the next available slot:

```
1. POST /v2/schedule/slots/next-available
   Body: { "platform": "twitter", "accountId": "98432" }
   Response: { "slot": { "slotId": "slot_1", "slotTime": "2026-04-02T09:00:00Z" } }

2. PATCH /v2/schedules/sch_abc123
   Body: { "patch": { "scheduledTime": "2026-04-02T09:00:00Z" } }
```

### Pseudocode for Calendar Management

```
1. schedules = GET /v2/schedules (paginate with cursor if needed)
2. Display scheduled posts to user (scheduledAt, account info, draft content)
3. To reschedule: PATCH /v2/schedules/:id { patch: { scheduledTime: "new ISO 8601" } }
4. To update content: PATCH /v2/schedules/:id { patch: { draft: { full post object } } }
5. To delete: DELETE /v2/schedules/:id
6. To reschedule to next free slot:
   a. nextSlot = POST /v2/schedule/slots/next-available { platform, accountId }
   b. PATCH /v2/schedules/:id { patch: { scheduledTime: nextSlot.slot.slotTime } }
```

***

## Error Handling

All asynchronous operations can fail during processing. Handle failures gracefully:

### Status Failure

When polling returns `status: "failed"`, check the error message:

**Source Failure** (Get Source endpoint):

```json
{
  "status": "failed",
  "message": "Unable to extract content from URL"
}
```

**Video Failure** (Get Visual Status endpoint):

```json
{
  "item": {
    "status": "script-ready",
    "error": "2/5 media failed to generate. The video has been saved as a draft."
  }
}
```

**Post Failure** (Get Post Status endpoint):

```json
{
  "postSubmissionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "failed",
  "errorMessage": "Invalid account credentials"
}
```

### Retry Strategy

* **Don't retry automatically on failure** - most failures are permanent
* **Log the error message** and report it to the user
* **Inform user** that the operation failed and provide next steps
* Retry failed reads after a delay. For a create request with an uncertain result, check existing work before resubmitting. For `429`, wait and reduce request frequency. See [Errors and retries](/api-and-mcp-concepts/error-handling.md).

***

## Platform-Specific Setup

Different platforms require different fields and have different requirements. See detailed guides:

* [**Instagram Setup**](/settings/social-accounts/instagram.md) - Reels, Stories, Collaborators, Alt Text
* [**LinkedIn Setup**](/settings/social-accounts/linkedin.md) - Company Pages, Professional Network
* [**Facebook Setup**](/settings/social-accounts/facebook.md) - Page ID, Media Types
* [**Platform Requirements**](/tips-and-tricks/social-platform-requirements.md) - All platforms at a glance

When publishing, always include the required fields for the target platform:

* **Facebook**: `target.pageId` (required), `target.mediaType` -- required `"reel"` for videos (regular feed videos no longer supported), optional `"story"` for Stories, omit for text/image posts
* **LinkedIn**: `target.pageId` (optional)
* **Pinterest**: `target.boardId` (required)
* **YouTube**: `target.playlistIds` (optional - array of playlist IDs from subaccounts)
* **TikTok**: `target.privacyLevel`, `target.disabledComments`, etc. (required)
* **Instagram**: `target.mediaType` (optional - default is "reel")
* **Twitter, Threads, Bluesky**: Minimal required fields


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://help.blotato.com/api-and-mcp-concepts/workflows.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
