Skip to main content
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
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. 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. Vetta stores neither the source bytes nor a second upload record.

Create or schedule a post

POST /v1/identities/{identity}/social/posts — scope sessions:write
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 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. Vetta chooses no default of its own, so no existing integration changes behaviour.
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 Vetta’s ordinary pagination envelope.