> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nouvel.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish

> Publish completed video projects to social media platforms

The Publishing API lets you publish completed video projects to one or more social media platforms. You can publish immediately or schedule posts for the future. Both endpoints require the `publish` permission.

***

## Publish Video

<ParamField path="POST /api/v1/publish" type="endpoint">
  Publish or schedule a completed video project to social media platforms.
</ParamField>

### Authentication

<ParamField header="Authorization" type="string" required>
  Your Nouvel API key with `publish` permission. Format: `Bearer nvl_xxxx`
</ParamField>

### Request Body

<ParamField body="projectId" type="string" required>
  UUID of the project to publish. The project must:

  * Belong to the authenticated user
  * Have `status: "completed"`
  * Have a `final_output_url` (generated video)
</ParamField>

<ParamField body="platforms" type="array" required>
  Array of platform targets. At least one is required.

  <Expandable title="Platform object">
    <ParamField body="platforms[].platform" type="string" required>
      Platform identifier. One of: `instagram`, `tiktok`, `youtube`, `facebook`, `linkedin`, `pinterest`, `threads`, `twitter`, `reddit`
    </ParamField>

    <ParamField body="platforms[].accountId" type="string" required>
      Account ID from [GET /api/v1/accounts](/api-reference/accounts). The account must belong to your organization — accounts from other orgs are rejected with 403.
    </ParamField>

    <ParamField body="platforms[].caption" type="string">
      Post caption. Falls back to the project title if omitted.
    </ParamField>

    <ParamField body="platforms[].platformSettings" type="object">
      Platform-specific settings. See [Platform-Specific Notes](#platform-specific-notes) below.
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="scheduledAt" type="string">
  ISO 8601 datetime to schedule the post for. Must be in the future. If omitted, the post is published immediately.

  ```json theme={null}
  "2026-02-01T12:00:00Z"
  ```
</ParamField>

### Example Requests

<CodeGroup>
  ```bash cURL - Immediate Publish theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/publish \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "projectId": "550e8400-e29b-41d4-a716-446655440000",
      "platforms": [
        {
          "platform": "instagram",
          "accountId": "6612f1a2b3c4d5e6f7890123",
          "caption": "Check out our new protein powder! 💪"
        },
        {
          "platform": "tiktok",
          "accountId": "6612f1a2b3c4d5e6f7890456",
          "caption": "This changed my morning routine #fitness #protein"
        }
      ]
    }'
  ```

  ```bash cURL - Scheduled Publish theme={null}
  curl -X POST https://app.nouvel.ai/api/v1/publish \
    -H "Authorization: Bearer nvl_xxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "projectId": "550e8400-e29b-41d4-a716-446655440000",
      "platforms": [
        {
          "platform": "instagram",
          "accountId": "6612f1a2b3c4d5e6f7890123",
          "caption": "Coming soon..."
        }
      ],
      "scheduledAt": "2026-02-01T12:00:00Z"
    }'
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch('https://app.nouvel.ai/api/v1/publish', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.NOUVEL_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      projectId: '550e8400-e29b-41d4-a716-446655440000',
      platforms: [
        {
          platform: 'instagram',
          accountId: '6612f1a2b3c4d5e6f7890123',
          caption: 'Check out our new protein powder!',
        },
      ],
    }),
  });

  const { postId, status, platforms } = await response.json();
  console.log(`Post ${postId}: ${status}`);
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://app.nouvel.ai/api/v1/publish',
      headers={
          'Authorization': f'Bearer {api_key}',
          'Content-Type': 'application/json',
      },
      json={
          'projectId': '550e8400-e29b-41d4-a716-446655440000',
          'platforms': [
              {
                  'platform': 'instagram',
                  'accountId': '6612f1a2b3c4d5e6f7890123',
                  'caption': 'Check out our new protein powder!',
              }
          ],
      }
  )

  data = response.json()
  print(f"Post {data['postId']}: {data['status']}")
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "postId": "6612f1a2b3c4d5e6f7891234",
  "status": "publishing",
  "platforms": [
    { "platform": "instagram", "status": "pending" },
    { "platform": "tiktok", "status": "pending" }
  ]
}
```

<ResponseField name="postId" type="string" required>
  Unique identifier for the publish request. Use this to check status via [GET /api/v1/publish/:postId](#check-publish-status).
</ResponseField>

<ResponseField name="status" type="string" required>
  Overall post status. `"publishing"` for immediate publishes, `"scheduled"` for future-scheduled posts.
</ResponseField>

<ResponseField name="platforms" type="array" required>
  Per-platform status. Each entry shows the current publishing status for that platform target.
</ResponseField>

### Error Codes

| Code | Description                                                                 |
| ---- | --------------------------------------------------------------------------- |
| 400  | Missing required fields, invalid JSON, or invalid `scheduledAt`             |
| 401  | Invalid or missing API key                                                  |
| 402  | Plan doesn't include API access                                             |
| 403  | API key missing `publish` permission, or account doesn't belong to your org |
| 404  | Project not found or no organization found                                  |
| 422  | Project not completed or has no video output                                |
| 500  | Publishing provider error                                                   |

### Platform-Specific Notes

<AccordionGroup>
  <Accordion title="Pinterest">
    Requires `platformSettings.boardId` — a numeric board ID (not a slug). You can find board IDs in the Nouvel dashboard when configuring Pinterest publishing.

    ```json theme={null}
    {
      "platform": "pinterest",
      "accountId": "...",
      "caption": "Pin description",
      "platformSettings": {
        "boardId": "1119777963540158867"
      }
    }
    ```
  </Accordion>

  <Accordion title="Reddit">
    Requires `platformSettings.title` (max 300 characters) and `platformSettings.subreddit`. Video posts are published as link posts (Reddit API limitation).

    ```json theme={null}
    {
      "platform": "reddit",
      "accountId": "...",
      "platformSettings": {
        "title": "Check out this amazing protein powder",
        "subreddit": "supplements"
      }
    }
    ```
  </Accordion>

  <Accordion title="YouTube">
    Optionally accepts `platformSettings.title` and `platformSettings.description` for the video metadata.

    ```json theme={null}
    {
      "platform": "youtube",
      "accountId": "...",
      "platformSettings": {
        "title": "Best Protein Powder Review 2026",
        "description": "Full review of the best protein powders..."
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Check Publish Status

<ParamField path="GET /api/v1/publish/:postId" type="endpoint">
  Check the status of a previously submitted publish request.
</ParamField>

### Path Parameters

<ParamField path="postId" type="string" required>
  The `postId` returned from `POST /api/v1/publish`.
</ParamField>

### Example

<CodeGroup>
  ```bash cURL theme={null}
  curl https://app.nouvel.ai/api/v1/publish/6612f1a2b3c4d5e6f7891234 \
    -H "Authorization: Bearer nvl_xxxx"
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
    `https://app.nouvel.ai/api/v1/publish/${postId}`,
    { headers: { 'Authorization': `Bearer ${process.env.NOUVEL_API_KEY}` } }
  );

  const { status, platforms } = await response.json();
  platforms.forEach(p => {
    console.log(`${p.platform}: ${p.status} ${p.platformPostUrl || ''}`);
  });
  ```
</CodeGroup>

### Response

```json theme={null}
{
  "postId": "6612f1a2b3c4d5e6f7891234",
  "status": "published",
  "platforms": [
    {
      "platform": "instagram",
      "status": "published",
      "platformPostUrl": "https://instagram.com/p/ABC123",
      "error": null
    },
    {
      "platform": "tiktok",
      "status": "failed",
      "platformPostUrl": null,
      "error": "Video format not supported"
    }
  ]
}
```

<ResponseField name="platforms[].status" type="string" required>
  Per-platform status. One of: `pending`, `publishing`, `published`, `failed`, `scheduled`.
</ResponseField>

<ResponseField name="platforms[].platformPostUrl" type="string">
  URL to the published post on the social platform. Only present when `status` is `published`.
</ResponseField>

<ResponseField name="platforms[].error" type="string">
  Error message if the publish failed for this platform. `null` on success.
</ResponseField>

### Error Codes

| Code | Description                          |
| ---- | ------------------------------------ |
| 401  | Invalid or missing API key           |
| 403  | API key missing `publish` permission |
| 404  | Post not found                       |
