# Workflows

Most Blotato API operations are asynchronous. You must follow the **Submit -> Poll -> Result** pattern.

## 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).
> 2. Loop every 2-5 seconds calling the `GET` endpoint with that ID.
> 3. Continue polling while status is processing (see status values below).
> 4. Succeed when status reaches a terminal state.
>
> **Terminal Status Values by Operation**:
>
> * **Sources**: `completed` (success) or `failed`
> * **Videos**: `done` (success) or `creation-from-template-failed`
> * **Posts**: `published` (success) or `failed`
>
> **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`.
>
> **Set `content.platform` and `target.targetType` to the same value** (e.g., both `"twitter"`).
>
> **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: <https://help.blotato.com/api/llm>

***

## 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 Every 2-5 Seconds
        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:

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

**Recommendation for Agents**: If freely deciding which template to use, carousel templates are versatile and work well for most content repurposing scenarios. See [all visual templates](/api/visuals.md) for IDs and specs.

### 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 Every 5 Seconds
        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": "5903b592-1255-43b4-b9ac-f8ed7cbf6a5f",
  "inputs": {},
  "prompt": "Create a 5-slide carousel about productivity tips",
  "render": true
}
```

### Example Payload (with Manual Inputs)

```json
{
  "templateId": "5903b592-1255-43b4-b9ac-f8ed7cbf6a5f",
  "inputs": {
    "title": "My Viral Video",
    "images": ["https://..."]
  },
  "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](/api/accounts.md))
* Subaccounts (for pageId): `GET /v2/users/me/accounts/:accountId/subaccounts` ([docs](/api/accounts.md#list-subaccounts-pages))

> **n8n / Make.com users**: Use the official Blotato **Get Post** node (select "Post" > "Get") instead of raw HTTP requests. It handles authentication and response parsing automatically. [Install guide](/api/start.md#n8n)

> \[!IMPORTANT] **Prerequisites**:
>
> 1. **`accountId`**: Get this from `GET /v2/users/me/accounts`. Always fetch the user's accounts before publishing. See [Accounts reference](/api/accounts.md).
> 2. **`pageId`** (Facebook/LinkedIn): Get this from `GET /v2/users/me/accounts/{accountId}/subaccounts`. See [How to Get the Right IDs](/api/accounts.md#how-to-get-the-right-ids-for-publishing).
> 3. **`mediaUrls`**: Pass URLs directly from the **Visual Workflow** (outputs `mediaUrl` and `imageUrls`). No upload step required.
> 4. **`content.platform` and `target.targetType`**: Set both to the same platform 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 Facebook/LinkedIn, also fetch subaccounts to get the `pageId`. For YouTube, fetch subaccounts to get `playlistIds`:

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

See [Accounts reference](/api/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 Every 2 Seconds
        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`. 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](/api/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 2-5 seconds, retry
4. videoId = POST /v2/videos/from-templates (use source.content in prompt)
5. LOOP: video = GET /v2/videos/creations/:videoId
   - IF status = "done": BREAK
   - IF status = "creation-from-template-failed": STOP, report error
   - ELSE: WAIT 5 seconds, retry
6. postId = POST /v2/posts (accountId from step 1, mediaUrl/imageUrls from step 5)
   - Set content.platform and target.targetType to the same value
7. LOOP: post = GET /v2/posts/:postId
   - IF status = "published": BREAK
   - IF status = "failed": STOP, check errorMessage
   - ELSE: WAIT 2 seconds, retry
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](/api/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](/api/schedule-slots.md))

### Schedule Response Shape

The `draft` field in a schedule is the same shape as the `post` object in [Publish Post](/api/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": "creation-from-template-failed"
  }
}
```

**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
* **Only retry on temporary network errors** (5xx status codes), not on 4xx errors

***

## 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: Querying This Documentation

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

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.blotato.com/api/workflows.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
