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

# Social

> Identity-scoped social account setup, media publishing, analytics, and comments.

Every route is nested under an identity. `{identity}` accepts an `idn_…` id or the identity's name.

## Activate

`POST /v1/identities/{identity}/social/activate` — scope `billing:write`

Creates the identity's social workspace. The operation is idempotent: a later call returns the
existing workspace.

## Read status

`GET /v1/identities/{identity}/social` — scope `agents:read`

Returns `activated` and the provider's current connected accounts. Before activation it returns
`activated: false` and an empty account list.

## Open the account portal

`POST /v1/identities/{identity}/social/portal` — scope `billing:write`

```json theme={"system"}
{
  "redirect_url": "https://app.example.com/social/done",
  "platforms": ["linkedin", "x"]
}
```

Both fields are optional: `redirect_url` defaults to the persona's own studio screen on deployments
that configure one. The response contains a hosted `url` the account owner visits to authorize
one or more networks. The URL is short-lived provider state; do not store it as an account.

## List accounts

`GET /v1/identities/{identity}/social/accounts` — scope `agents:read`

Returns a [paginated list](https://vetta.sh/docs/api/pagination.md). This is a live reconciliation read, not a mirror of an
OAuth token or cached account table.

## Upload media

`POST /v1/identities/{identity}/social/uploads` — scope `sessions:write`

Pass `{ "url": "https://…" }`. The provider fetches the public image, video, or document and
returns a `social_upload`. Naive Managed Agents stores neither the source bytes nor a second upload record.

## Create or schedule a post

`POST /v1/identities/{identity}/social/posts` — scope `sessions:write`

```json theme={"system"}
{
  "content": "We shipped it.",
  "platforms": ["linkedin", "x"],
  "media_urls": ["https://cdn.example.com/launch.jpg"],
  "scheduled_at": "2026-09-01T14:00:00Z"
}
```

Omit `scheduled_at` to queue the post immediately. `title` is optional and defaults to the first 100
characters. `media_urls` accepts up to ten public URLs, and `file_ids` up to ten stored `fil_`
ids — each resolves to a short-lived signed URL fetched once, so a [stored file](https://vetta.sh/docs/api/files.md) posts
without ever needing a public address.

Which targets are legal depends on whether the post carries media. `bluesky`, `facebook`,
`linkedin`, `mastodon`, `threads`, and `x` take a post either way. `instagram`, `tiktok`, and
`youtube` publish media, not text, so naming one requires at least one `media_urls` or `file_ids`
entry — a text-only post to them is refused `validation_failed` on `platforms`, and the message
says which platform and what is missing. `pinterest` and `reddit` are connectable but not yet
postable: each needs a per-post destination (a board, a subreddit) this body has no field for, and
a post naming one is refused with that reason rather than accepted and ignored.

`visibility` is optional and says who may see the published post: `public`, `unlisted`, or
`private`. Only `mastodon` and `youtube` accept it — they are the two networks whose contract
carries all three — and naming any other platform alongside a `visibility` is refused
`validation_failed` on `visibility`, with the network named. Omit the field and nothing is sent
upstream: the post publishes at the connected account's own default, which on a video channel is
normally public. Managed Agents chooses no default of its own, so no existing integration changes behaviour.

```json theme={"system"}
{
  "content": "The one-minute version.",
  "platforms": ["youtube"],
  "file_ids": ["fil_262tzfys51n955rn6d1wdchyg8"],
  "visibility": "unlisted"
}
```

### AI-generated posts

Labelling is opt-in: set `ai_generated: true` to label a post. The post then carries the network's
own AI-content label where the network has one — TikTok, YouTube, Instagram, X and Pinterest — and
its caption ends with an `#AIgenerated` line on every network, kept inside the network's length limit
(a caption that already carries the `#AIgenerated` tag is left as written; a phrase such as
"AI-generated" in the text does not count). Nothing labels a post on its own: without
`ai_generated: true` it is posted unlabelled, including one that attaches a file rendered as a
[character](https://vetta.sh/docs/api/characters.md).

```json theme={"system"}
{
  "content": "Monday fit check",
  "platforms": ["tiktok", "instagram"],
  "file_ids": ["fil_262tzfys51n955rn6d1wdchyg9"],
  "ai_generated": true
}
```

One non-X post costs 13,000 µUSD. A post including X costs 32,500 µUSD, or 273,000 µUSD when its
text contains an HTTP(S) link. One multi-network post pays the base once. The balance is checked
before publishing and the debit appears under the `social` component.

## Read a post

`GET /v1/identities/{identity}/social/posts/{post}` — scope `agents:read`

The response reports `scheduled`, `processing`, `posted`, `failed`, or `deleted` state. A post id
owned by another identity returns `not_found`.

## Analytics, comments, and deletion

* `GET /v1/identities/{identity}/social/accounts/{account}/analytics`
* `GET /v1/identities/{identity}/social/posts/{post}/analytics`
* `GET /v1/identities/{identity}/social/posts/{post}/comments`
* `DELETE /v1/identities/{identity}/social/posts/{post}`

All four verify the remote resource belongs to the identity's workspace before returning data or
mutating it. Analytics payloads remain provider-shaped under `data`; comments use Managed Agents' ordinary
pagination envelope.

## List the company's posts

`GET /v1/posts` — scope `sessions:read`

Every post the company files, across personas and sessions, in one list. A post an agent filed is
held for a person to allow before it goes out; it is listed here as `pending` until someone decides.

| Query | |
| - | - |
| `status` | `pending`, `scheduled`, `posted` or `failed`. Omit for all. |
| `identity_id` | Only this persona's posts. |
| `agent_id` | Only posts this agent filed. |
| `limit` / `after` | [Pagination](https://vetta.sh/docs/api/pagination.md). |
| `reconcile` | `false` lists only what is stored and asks the network nothing. Default `true`. |

`scheduled` lists the soonest first; everything else lists newest first. Before a list that can show
sent posts, up to five posts that are due and not yet confirmed are checked with the network first,
each at most once every five minutes.

```json theme={"system"}
{
  "object": "post",
  "id": "ses_01H9AB...:tc_9f2c1a7e...",
  "status": "pending",
  "identity_id": "idn_01H9AB...",
  "agent_id": "agt_01H9AB...",
  "session_id": "ses_01H9AB...",
  "tool_call_id": "tc_9f2c1a7e...",
  "content": "Three ways to fold a fitted sheet",
  "platforms": ["youtube"],
  "file_ids": ["fil_01H9AB..."],
  "media_urls": [],
  "visibility": "unlisted",
  "scheduled_at": "2026-10-01T09:00:00.000Z",
  "posted_at": null,
  "url": null,
  "error": null,
  "metrics": null,
  "measured_at": null,
  "duplicate_of": null,
  "created_at": "2026-09-25T17:02:11.000Z"
}
```

* A `pending` post has no network id yet: its `id` is the session and the approval it waits on.
  Allow or deny it with [`POST /v1/sessions/{session_id}/tool_confirmations`](https://vetta.sh/docs/api/sessions) and its
  `tool_call_id`. What an agent filed is exactly what goes out — to change it, deny with a reason and
  the agent files it again.
* `posted` carries the permalink in `url` and, once an agent has read them, the latest numbers in
  `metrics` (`views`, `likes`, `comments`, …) with the time they were read in `measured_at`. Numbers
  are read at most once a day per post.
* `failed` carries the network's reason in `error`. A post that reached some networks before it
  failed keeps its link and its network ids for those. If the network later sends it after all, it
  reads `posted`.
* `duplicate_of` is set on a `pending` post that would publish a stored file the persona already
  has on the same network, or that an earlier approval is waiting to publish, in this session or
  another: `{ id, status, platform, at, agent_id, session_id }`. An agent's post of a file that is
  already out, scheduled or being sent on that network is refused rather than published twice —
  even when two approvals land at the same moment. Another persona posting the same file is not a
  repeat.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.