# Viraly API Reference for AI Agents

> This document is optimized for LLMs and AI agents to build integrations with the Viraly API. It contains every endpoint, parameter, and response shape in a flat, parseable format.

## API Overview

- **Base URL**: `https://api.viraly.io/api/v1`
- **Auth**: Bearer token in `Authorization` header
- **Token formats**: API keys start with `vly_`, OAuth tokens start with `vat_`
- **Content-Type**: `application/json` for request bodies
- **All responses** are wrapped in `{ "data": ..., "meta": { "requestId": "..." } }`
- **Paginated responses** include `"meta": { "pagination": { "page", "perPage", "total", "totalPages" } }`
- **Errors** return `{ "error": { "code": "...", "message": "...", "requestId": "..." } }`
- **IDs** are 8-character alphanumeric strings
- **Dates** are ISO 8601 format (e.g. `2026-04-15T14:00:00Z`)

## Authentication

```
Authorization: Bearer vly_your_api_key_here
```

API keys are workspace-scoped. Each key has granular permissions that control which endpoints it can access.

## Permissions

Each endpoint requires a specific permission. If the API key lacks the required permission, the API returns HTTP 403.

Available permissions: `posts:read`, `posts:write`, `channels:read`, `channels:write`, `analytics:read`, `biolinks:read`, `biolinks:write`, `media:read`, `media:write`, `social_sets:read`, `social_sets:write`, `categories:read`, `categories:write`, `hashtags:read`, `hashtags:write`, `ideas:read`, `ideas:write`, `feeds:read`, `feeds:write`, `url_shortener:read`, `url_shortener:write`, `subscribers:read`, `workspace:read`

## Rate Limits

Per API key, by plan: Free 30/min, Influencer 60/min, Business 120/min, Agency 300/min, Enterprise 600/min. Returns HTTP 429 when exceeded.

## Pagination

List endpoints accept `page` (default 1) and `per_page` (default 25, max 100) query parameters.

---

## Posts

### GET /api/v1/posts

List posts with filtering and pagination. Permission: `posts:read`

Query parameters:
- `social_set_id` (string, optional) - Filter by social set
- `channel_id` (string, optional) - Filter by channel
- `status` (string, optional) - Filter: draft, scheduled, published, failed
- `start_date` (string, optional) - YYYY-MM-DD
- `end_date` (string, optional) - YYYY-MM-DD
- `page` (integer, optional) - Page number, default 1
- `per_page` (integer, optional) - Items per page, default 25, max 100
- `sort` (string, optional) - created_at, scheduled_at, or published_at
- `order` (string, optional) - asc or desc

Example request:
```
GET /api/v1/posts?status=published&per_page=2&sort=created_at&order=desc
Authorization: Bearer vly_your_api_key
```

Example response:
```json
{
  "data": [
    {
      "id": "NY7ObZ1D",
      "title": "Check out our latest update!",
      "status": "Published",
      "displayStatus": "Published",
      "scheduledAt": "2026-04-06T01:40:56Z",
      "createdAt": "2026-04-06T01:40:56Z",
      "channelId": "VQ4fKSE9",
      "timezone": "UTC",
      "queued": false,
      "categoryIds": [],
      "config": {
        "channelType": "Facebook",
        "facebook": {
          "contentOptions": {
            "postType": "Post",
            "message": "Check out our latest update!"
          }
        }
      },
      "metrics": {
        "updatedAt": "2026-04-08T12:20:16Z",
        "hasData": true,
        "facebook": {
          "reactionsTotal": 5,
          "impressions": 120,
          "reach": 85,
          "shares": 2,
          "comments": 1,
          "engagementRate": 6.7
        }
      },
      "postAttachments": [],
      "isEvergreen": false
    }
  ],
  "meta": {
    "requestId": "req_abc123",
    "pagination": {
      "page": 1,
      "perPage": 2,
      "total": 45,
      "totalPages": 23
    }
  }
}
```

### GET /api/v1/posts/{id}

Get a single post by ID. Permission: `posts:read`

Path parameters:
- `id` (string, required) - Post ID

Example request:
```
GET /api/v1/posts/NY7ObZ1D
Authorization: Bearer vly_your_api_key
```

### POST /api/v1/posts

Create a new post. Permission: `posts:write`

Example request:
```
POST /api/v1/posts
Authorization: Bearer vly_your_api_key
Content-Type: application/json

{
  "channelId": "VQ4fKSE9",
  "config": {
    "channelType": "Facebook",
    "facebook": {
      "contentOptions": {
        "postType": "Post",
        "message": "Exciting product launch coming soon!"
      }
    }
  },
  "scheduledAt": "2026-04-15T14:00:00Z",
  "timezone": "America/New_York"
}
```

### PUT /api/v1/posts/{id}

Update an existing post. Permission: `posts:write`

Path parameters:
- `id` (string, required) - Post ID

Request body: same shape as POST /api/v1/posts

### DELETE /api/v1/posts/{id}

Delete a post. Permission: `posts:write`

Path parameters:
- `id` (string, required) - Post ID

Returns empty response on success.

### PUT /api/v1/posts/{id}/schedule

Reschedule a post. Permission: `posts:write`

Path parameters:
- `id` (string, required) - Post ID

Example request:
```
PUT /api/v1/posts/NY7ObZ1D/schedule
Authorization: Bearer vly_your_api_key
Content-Type: application/json

{
  "scheduledAt": "2026-04-20T09:00:00Z",
  "timezone": "America/New_York"
}
```

### POST /api/v1/posts/{id}/publish

Publish a post immediately. Permission: `posts:write`

Path parameters:
- `id` (string, required) - Post ID

No request body required.

---

## Channels

### GET /api/v1/channels

List connected social media channels. Permission: `channels:read`

Query parameters:
- `social_set_id` (string, optional) - Filter by social set

Example response:
```json
{
  "data": [
    {
      "id": "VQ4fKSE9",
      "name": "My Brand Page",
      "type": "Facebook",
      "username": "",
      "accountType": "FacebookPage",
      "pictureUrl": "https://...",
      "status": "Active",
      "socialSetId": "TrrdKTIr",
      "createdAt": "2026-04-08T12:19:32Z",
      "order": 0,
      "scopes": ["pages_manage_posts", "read_insights"],
      "config": {}
    }
  ],
  "meta": { "requestId": "req_abc123" }
}
```

### GET /api/v1/channels/{id}

Get a single channel. Permission: `channels:read`

Path parameters:
- `id` (string, required) - Channel ID

### GET /api/v1/channels/{id}/stats

Get channel statistics. Permission: `channels:read`

Path parameters:
- `id` (string, required) - Channel ID

Example response:
```json
{
  "data": {
    "postsCount": 42
  },
  "meta": { "requestId": "req_abc123" }
}
```

---

## Social Sets

Social sets are logical groupings of channels (e.g., "Marketing Team", "Client A").

### GET /api/v1/social-sets

List all social sets. Permission: `social_sets:read`

Example response:
```json
{
  "data": [
    {
      "id": "TrrdKTIr",
      "name": "My Social Set",
      "code": "MS",
      "colorHex": "#9333EA",
      "timezone": "America/Chicago",
      "createdAt": "2026-04-04T19:26:41Z",
      "channels": [...]
    }
  ],
  "meta": { "requestId": "req_abc123" }
}
```

### GET /api/v1/social-sets/{id}

Get a social set. Permission: `social_sets:read`

### POST /api/v1/social-sets

Create a social set. Permission: `social_sets:write`

```json
{
  "name": "Client B",
  "timezone": "Europe/London"
}
```

### PUT /api/v1/social-sets/{id}

Update a social set. Permission: `social_sets:write`

---

## Bio Links

### GET /api/v1/biolinks

List bio link pages. Permission: `biolinks:read`

Query parameters:
- `social_set_id` (string, optional)

### GET /api/v1/biolinks/{id}

Get a bio link. Permission: `biolinks:read`

### POST /api/v1/biolinks

Create a bio link. Permission: `biolinks:write`

### PUT /api/v1/biolinks/{id}

Update a bio link. Permission: `biolinks:write`

### DELETE /api/v1/biolinks/{id}

Delete a bio link. Permission: `biolinks:write`

### GET /api/v1/biolinks/{id}/linked-channels

Get linked channels for a bio link. Permission: `biolinks:read`

### PUT /api/v1/biolinks/{id}/linked-channels

Update linked channels. Permission: `biolinks:write`

### GET /api/v1/biolinks/{id}/subscribers

List bio link subscribers (paginated). Permission: `subscribers:read`

Query parameters:
- `page` (integer, optional)
- `per_page` (integer, optional)

Example response:
```json
{
  "data": [
    {
      "id": "e4Yq9sL0",
      "email": "subscriber@example.com",
      "name": "Jane Doe",
      "createdAt": "2026-03-15T14:30:00Z"
    }
  ],
  "meta": {
    "requestId": "req_abc123",
    "pagination": { "page": 1, "perPage": 25, "total": 89, "totalPages": 4 }
  }
}
```

---

## Analytics

### POST /api/v1/analytics/{channelId}/sync

Trigger an analytics sync for a channel. Permission: `analytics:read`

Path parameters:
- `channelId` (string, required)

Example response:
```json
{
  "data": { "status": "sync_started", "channelId": "VQ4fKSE9" },
  "meta": { "requestId": "req_abc123" }
}
```

---

## Media

### GET /api/v1/media

List media items (paginated). Permission: `media:read`

Query parameters:
- `social_set_id` (string, optional)
- `collection_id` (string, optional)
- `page` (integer, optional)
- `per_page` (integer, optional)

Example response:
```json
{
  "data": [
    {
      "id": "f6Zr1tM2",
      "role": "PostAttachment",
      "type": "Image",
      "status": "Ready",
      "tags": [],
      "description": null,
      "socialSetId": "TrrdKTIr",
      "createdAt": "2026-04-01T08:15:00Z"
    }
  ],
  "meta": { "requestId": "req_abc123", "pagination": { ... } }
}
```

### GET /api/v1/media/{id}

Get a media item. Permission: `media:read`

### DELETE /api/v1/media/{id}

Delete a media item. Permission: `media:write`

### GET /api/v1/media/collections

List media collections. Permission: `media:read`

Query parameters:
- `social_set_id` (string, optional)

---

## Hashtags

### GET /api/v1/hashtags

List hashtag groups. Permission: `hashtags:read`

Query parameters:
- `social_set_id` (string, optional)

Example response:
```json
{
  "data": [
    {
      "id": "g8As3uN4",
      "topic": "Product Launch",
      "hashtags": "#launch #newproduct #excited"
    }
  ],
  "meta": { "requestId": "req_abc123" }
}
```

### POST /api/v1/hashtags

Create a hashtag group. Permission: `hashtags:write`

### PUT /api/v1/hashtags/{id}

Update a hashtag group. Permission: `hashtags:write`

### DELETE /api/v1/hashtags/{id}

Delete a hashtag group. Permission: `hashtags:write`

---

## Categories

### GET /api/v1/categories

List post categories. Permission: `categories:read`

Example response:
```json
{
  "data": [
    {
      "id": "jde4tXMA",
      "name": "Educational",
      "color": "#3B82F6",
      "sortOrder": 1,
      "postCount": 12,
      "createdAt": "2026-04-04T19:26:40Z"
    }
  ],
  "meta": { "requestId": "req_abc123" }
}
```

### POST /api/v1/categories

Create a category. Permission: `categories:write`

### PUT /api/v1/categories/{id}

Update a category. Permission: `categories:write`

### DELETE /api/v1/categories/{id}

Delete a category. Permission: `categories:write`

---

## Ideas

### GET /api/v1/idea-boards

List idea boards. Permission: `ideas:read`

Query parameters:
- `social_set_id` (string, optional)

Example response:
```json
{
  "data": [
    {
      "id": "EMgPC4hk",
      "name": "To Do",
      "description": "Ideas ready to work on",
      "colorHex": "#3B82F6",
      "displayOrder": 1,
      "socialSetId": "TrrdKTIr",
      "ideas": [
        {
          "id": "YtzAdCPI",
          "title": "Behind-the-scenes video",
          "caption": "Show the team working on the new feature",
          "status": "Draft",
          "displayOrder": 0,
          "boardId": "EMgPC4hk"
        }
      ]
    }
  ],
  "meta": { "requestId": "req_abc123" }
}
```

### GET /api/v1/idea-boards/{boardId}/ideas

List ideas in a board. Permission: `ideas:read`

### POST /api/v1/ideas

Create an idea. Permission: `ideas:write`

```json
{
  "boardId": "EMgPC4hk",
  "title": "Behind-the-scenes video",
  "caption": "Show the team working on the new feature",
  "notes": "Film during standup on Thursday"
}
```

### PUT /api/v1/ideas/{id}

Update an idea. Permission: `ideas:write`

### DELETE /api/v1/ideas/{id}

Delete an idea. Permission: `ideas:write`

---

## Feeds

### GET /api/v1/feed-collections

List feed collections. Permission: `feeds:read`

Query parameters:
- `socialSetId` (string, required)

### GET /api/v1/feeds

List feeds. Permission: `feeds:read`

Query parameters:
- `socialSetId` (string, required)

### POST /api/v1/feeds

Create a feed. Permission: `feeds:write`

### DELETE /api/v1/feeds/{id}

Delete a feed. Permission: `feeds:write`

---

## URL Shortener

### GET /api/v1/short-urls

List shortened URLs (paginated). Permission: `url_shortener:read`

Query parameters:
- `page` (integer, optional)
- `per_page` (integer, optional)
- `search` (string, optional)

Example response:
```json
{
  "data": [
    {
      "id": 1234,
      "shortCode": "abc123",
      "originalUrl": "https://example.com/long-url",
      "shortUrl": "https://vly.to/abc123",
      "totalClicks": 847,
      "createdAt": "2026-03-01T10:00:00Z"
    }
  ],
  "meta": { "requestId": "req_abc123", "pagination": { ... } }
}
```

### POST /api/v1/short-urls

Create a shortened URL. Permission: `url_shortener:write`

```json
{
  "url": "https://example.com/my-long-campaign-url?utm_source=social"
}
```

URL must be valid HTTP or HTTPS.

### GET /api/v1/short-urls/{shortCode}/clicks

Get click analytics for a shortened URL. Permission: `url_shortener:read`

Path parameters:
- `shortCode` (string, required)

---

## Workspace

### GET /api/v1/workspace

Get current workspace info. Permission: `workspace:read`

Example response:
```json
{
  "data": {
    "id": "YNFoJ08S",
    "name": "Acme Marketing",
    "plan": "Business",
    "status": "Active",
    "createdAt": "2025-06-01T00:00:00Z"
  },
  "meta": { "requestId": "req_abc123" }
}
```

### GET /api/v1/workspace/users

List workspace users (paginated). Permission: `workspace:read`

Returns name and role only (no email or PII).

Query parameters:
- `page` (integer, optional)
- `per_page` (integer, optional)

Example response:
```json
{
  "data": [
    {
      "id": "FZLJhAp0",
      "name": "John Smith",
      "role": "Owner",
      "status": "Active",
      "createdAt": "2025-06-01T00:00:00Z"
    }
  ],
  "meta": {
    "requestId": "req_abc123",
    "pagination": { "page": 1, "perPage": 25, "total": 3, "totalPages": 1 }
  }
}
```

---

## Supported Social Platforms

Viraly supports scheduling and analytics for: Facebook, Instagram, TikTok, Twitter/X, LinkedIn, YouTube, Pinterest, Threads, Bluesky, Mastodon, WordPress.

## Error Codes

| HTTP Status | Meaning |
|-------------|---------|
| 200 | Success |
| 400 | Bad request - invalid parameters |
| 401 | Unauthorized - invalid or missing API key |
| 403 | Forbidden - API key lacks required permission |
| 404 | Not found - resource doesn't exist or belongs to another workspace |
| 429 | Rate limit exceeded - check Retry-After header |
