---
title: "Content Workflow Tools"
description: "AI Twin personas, content calendar, captions, bulk reels, avatar rendering, and social publishing APIs"
canonical: "https://docs.ainative.studio/docs/api/content-workflow"
last-updated: "2026-11-02T18:12:43.468Z"
---

# Content Workflow Tools

Source: https://docs.ainative.studio/docs/api/content-workflow

> AI Twin personas, content calendar, captions, bulk reels, avatar rendering, and social publishing APIs

# Content Workflow Tools

**Base URL:** `https://api.ainative.studio/api/v1/public`

End-to-end API suite for AI-powered content creation and distribution. Create twin personas, schedule posts, auto-generate captions, batch-render reels, render avatar videos, and publish to social platforms — all async with `task_id` polling.

## Authentication

All endpoints require an API key:

```bash
-H "X-API-Key: sk_..."
```

## Async Pattern

Long-running operations return a `task_id`. Poll `GET /tasks/{task_id}` until `status` is `done` or `error`:

```json
{
  "task_id": "abc123",
  "status": "processing",   // queued | processing | done | error
  "progress": 42,           // 0–100
  "result": null            // populated when done
}
```

---

## AI Twin Library

Manage AI twin personas — the identity, voice, and style that drive generated content.

### Create a twin

```bash
POST /twins
```

```json
{
  "name": "Tech Creator",
  "voice_id": "en-US-Neural2-A",
  "style_tags": ["casual", "educational"],
  "persona_prompt": "You are an engaging tech educator who explains complex topics simply.",
  "platform_defaults": {
    "tiktok": { "caption_style": "short" },
    "instagram": { "caption_style": "hashtag-heavy" }
  }
}
```

**Response `201`:**
```json
{
  "id": "645b5b56-f879-4f2f-8ccc-106e8912f522",
  "name": "Tech Creator",
  "memory_namespace": "twin_645b5b56-f879-4f2f-8ccc-106e8912f522",
  "style_tags": ["casual", "educational"],
  "created_at": "2026-04-30T02:12:14Z"
}
```

The `memory_namespace` is pre-wired to [ZeroMemory](/docs/zeromemory/overview) — store twin-specific context there for personalized generation.

### List twins

```bash
GET /twins
```

### Get a twin

```bash
GET /twins/{twin_id}
```

### Update a twin

```bash
PATCH /twins/{twin_id}
Content-Type: application/json

{ "persona_prompt": "Updated persona..." }
```

### Delete a twin (soft delete)

```bash
DELETE /twins/{twin_id}
```

Returns `204 No Content`.

---

## Content Calendar

Schedule content for automatic publishing. The `publish_scheduled_content` Celery Beat task checks every minute and dispatches due items.

### Schedule a post

```bash
POST /content/calendar
```

```json
{
  "twin_id": "645b5b56-f879-4f2f-8ccc-106e8912f522",
  "title": "How AI is changing content creation",
  "platform": "instagram",
  "scheduled_at": "2026-05-01T10:00:00Z",
  "script": "AI is transforming how creators work..."
}
```

**Response `201`:**
```json
{
  "id": "096e3539-8d29-404b-882c-c554e05f1b26",
  "title": "How AI is changing content creation",
  "platform": "instagram",
  "status": "scheduled",
  "scheduled_at": "2026-05-01T10:00:00Z"
}
```

**Platform values:** `instagram`, `tiktok`, `linkedin`, `youtube`

### List calendar items

```bash
GET /content/calendar?platform=instagram&status=scheduled
```

**Query params:**
| Param | Type | Description |
|-------|------|-------------|
| `platform` | string | Filter by platform |
| `status` | string | `scheduled`, `published`, `failed` |
| `from_date` | ISO8601 | Start of date range |
| `to_date` | ISO8601 | End of date range |

### Get a calendar item

```bash
GET /content/calendar/{item_id}
```

### Delete a calendar item

```bash
DELETE /content/calendar/{item_id}
```

---

## Captions

Auto-generate captions from video or audio using Whisper. Returns SRT, VTT, and editable segments. Optional FFmpeg burn-in.

### Generate captions

```bash
POST /captions/generate
Content-Type: multipart/form-data
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `reel_url` | string | One of | URL to video/audio file |
| `audio_file` | file | One of | Direct upload |
| `language` | string | No | Default `en` |
| `burn_in` | bool | No | Burn captions into video |

```bash
curl -X POST https://api.ainative.studio/api/v1/public/captions/generate \
  -H "X-API-Key: sk_..." \
  -F "reel_url=https://cdn.example.com/video.mp4" \
  -F "language=en"
```

**Response `202`:**
```json
{
  "caption_id": "5bc41d91-867a-4ccb-821a-2b9544c45719",
  "task_id": "sync_5bc41d91-867a-4ccb-821a-2b9544c45719",
  "status": "queued"
}
```

### Get caption result

```bash
GET /captions/{caption_id}
```

**Response:**
```json
{
  "id": "5bc41d91...",
  "status": "done",
  "srt": "1\n00:00:00,000 --> 00:00:03,500\nAI is transforming how creators work.\n",
  "vtt": "WEBVTT\n\n00:00:00.000 --> 00:00:03.500\nAI is transforming...",
  "segments": [
    { "start": 0.0, "end": 3.5, "text": "AI is transforming how creators work." }
  ]
}
```

### Burn captions into video

```bash
POST /captions/{caption_id}/burn-in
Content-Type: application/json

{ "font_size": 24, "font_color": "white", "position": "bottom" }
```

Returns `{ "task_id": "...", "status": "queued" }` — poll until `done`, then download from `result.output_url`.

---

## Bulk Reel Generation

Upload a CSV of scripts and generate reels in parallel using a `celery.group()` fan-out. Max 100 rows per batch.

### Submit a batch

```bash
POST /bulk/reels
Content-Type: multipart/form-data
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `csv_file` | file | Yes | CSV with `script`, `platform`, `voice_id` columns |
| `twin_id` | string | Yes | Twin persona to use |
| `platform` | string | Yes | Target platform |

**CSV format:**
```csv
script,platform,voice_id
Hello world!,instagram,en-US
Check out this feature.,tiktok,en-US
```

```bash
curl -X POST https://api.ainative.studio/api/v1/public/bulk/reels \
  -H "X-API-Key: sk_..." \
  -F "csv_file=@scripts.csv;type=text/csv" \
  -F "twin_id=645b5b56-f879-4f2f-8ccc-106e8912f522" \
  -F "platform=instagram"
```

**Response `202`:**
```json
{
  "task_id": "dc1b2a5b-ecd7-4fa4-bfd0-48917fcf218a",
  "batch_id": "dc1b2a5b-ecd7-4fa4-bfd0-48917fcf218a",
  "status": "queued",
  "progress": 0,
  "total_rows": 50
}
```

### Get batch status

```bash
GET /bulk/reels/{batch_id}
```

Returns per-job progress and individual `result_url` values when each reel completes.

---

## Avatar Render

Generate talking-head avatar videos via HeyGen. Polls HeyGen's API for up to 5 minutes, then fires a webhook on completion.

### Submit a render job

```bash
POST /multimodal/avatar/generate
Content-Type: application/json
```

```json
{
  "twin_id": "645b5b56-f879-4f2f-8ccc-106e8912f522",
  "script": "Hello! I'm your AI twin. Let's create something amazing.",
  "voice_id": "en-US-Neural2-A",
  "avatar_id": "heygen_avatar_id_optional"
}
```

**Response `202`:**
```json
{
  "task_id": "b1ebec31-ce3e-45c2-8629-9ac12ff4ea3c",
  "celery_task_id": "b1ebec31-ce3e-45c2-8629-9ac12ff4ea3c",
  "provider": "heygen",
  "status": "queued",
  "progress": 0,
  "estimated_seconds": 45
}
```

### Get render status

```bash
GET /multimodal/avatar/{job_id}
```

**Response when done:**
```json
{
  "task_id": "b1ebec31...",
  "status": "done",
  "progress": 100,
  "result": {
    "video_url": "https://cdn.heygen.com/...",
    "duration_seconds": 12,
    "credits_billed": 120
  }
}
```

**Billing:** 10 credits per second of rendered video.

---

## Social Publishing

Connect OAuth accounts and publish content to social platforms.

### Connect a platform (OAuth)

```bash
GET /social/connect/{platform}?redirect_uri=https://yourapp.com/callback
```

Redirects to the platform's OAuth flow. Supported: `instagram`, `tiktok`, `linkedin`, `youtube`.

### OAuth callback

```bash
GET /social/callback/{platform}?code=...&state=...
```

Called by the platform after OAuth. Stores encrypted tokens internally.

### List connected accounts

```bash
GET /social/accounts
```

```json
[
  { "id": "...", "platform": "instagram", "handle": "@mycreator", "connected_at": "..." }
]
```

### Publish content

```bash
POST /social/publish
Content-Type: application/json
```

```json
{
  "platforms": ["instagram", "tiktok"],
  "reel_url": "https://cdn.example.com/reel.mp4",
  "caption": "Check out my latest video! #AIContent",
  "twin_id": "645b5b56-f879-4f2f-8ccc-106e8912f522"
}
```

**Response:**
```json
{
  "results": [
    { "platform": "instagram", "task_id": "abc123", "status": "queued" },
    { "platform": "tiktok",    "task_id": "def456", "status": "queued" }
  ]
}
```

Each `task_id` can be polled via `GET /social/publish/{job_id}`.

### Disconnect a platform

```bash
DELETE /social/disconnect/{platform}
```

---

## Social Analytics

Fetch engagement metrics, audience data, and export reports. Analytics sync every 6 hours via Celery Beat.

### Get analytics

```bash
GET /analytics/social?platform=instagram&days=30
```

**Query params:**
| Param | Type | Default | Description |
|-------|------|---------|-------------|
| `platform` | string | all | Filter by platform |
| `days` | int | 30 | Lookback window |
| `twin_id` | string | — | Filter by twin |

**Response:**
```json
[
  {
    "platform": "instagram",
    "date": "2026-04-29",
    "views": 15420,
    "likes": 823,
    "comments": 94,
    "shares": 201,
    "followers_gained": 47
  }
]
```

Format is Recharts-compatible — pass directly to `<AreaChart data={...}>`.

### Summary rollup

```bash
GET /analytics/social/summary?days=30
```

Returns totals and deltas across all connected platforms.

### Export report

```bash
POST /analytics/social/export
Content-Type: application/json

{ "format": "csv", "days": 30, "platform": "instagram" }
```

Returns a `task_id`. When done, `result.download_url` contains a signed S3 URL valid for 1 hour.

---

## Error Codes

| Code | Meaning |
|------|---------|
| `400` | Validation error — check request body |
| `401` | Missing or invalid API key |
| `403` | Plan limit reached (e.g. bulk reels > 100 rows) |
| `404` | Resource not found or soft-deleted |
| `422` | Schema validation failure |
| `500` | Server error — retry with exponential backoff |

---

## Frontend Integration

The `@ainative/react-sdk` doesn't expose `useTask` directly — build the polling hook with TanStack Query:

```ts
import { useQuery } from '@tanstack/react-query';

function useTask<T>(taskId: string | null) {
  return useQuery<{ status: string; progress: number; result: T }>({
    queryKey: ['task', taskId],
    queryFn: () =>
      fetch(`https://api.ainative.studio/api/v1/tasks/${taskId}`, {
        headers: { 'X-API-Key': process.env.NEXT_PUBLIC_AINATIVE_KEY! },
      }).then(r => r.json()),
    enabled: !!taskId,
    refetchInterval: q =>
      q.state.data?.status === 'done' || q.state.data?.status === 'error'
        ? false
        : 2000,
  });
}
```

See the [AI Kit Components](/docs/ai-kit/components) docs for `StreamingMessage` and `StreamingIndicator` to pair with script generation.
