Let AI agents draft Instagram and Threads posts via /api/v1/social and aveiro_social_* MCP tools. Requires social:write — approval stays in the dashboard.
Grant social:write on an API token or MCP OAuth connection. Agents list accounts, create and revise drafts, generate media, and read rejection feedback. Approve, reject, and publish remain dashboard-only — there is no scope that grants them.
What agents can and cannot do
Action
Agent (API / MCP)
Human (dashboard)
List connected accounts
Yes
Yes
Create and revise drafts
Yes — draft, pending_approval, rejected only
Yes
Generate or register media
Yes
Yes
Read feedback timeline
Yes
Yes
Approve, reject, post now
No — not exposed
Yes
Publish autonomously
No — structural guardrail
No without approval
This split is intentional. Social posts go to public profiles — Aveiro keeps approval in session-authenticated UI actions only.
Token setup
Enable Social publishing under Organization → Features
Open Organization → API tokens → Create token
Add the social:write scope (requires Audience manage capability)
Copy the av_live_… secret — shown once
For MCP in Cursor or Claude, connect via the hosted MCP server at https://www.aveiro.app/api/mcp/mcp. OAuth grants include social:write when you authorize social drafting tools.Default content tokens (sites:read, content:read, content:write) do not include social access — add social:write explicitly.See API tokens for the full scope list.
REST API
All routes live under /api/v1/social on your platform host. Full reference: Social API overview.
Method
Route
Purpose
GET
/api/v1/social/accounts
Connected Instagram/Threads accounts and IDs for targets
GET
/api/v1/social/posts
List posts — filter by status (e.g. rejected, pending_approval)
POST
/api/v1/social/posts
Create a draft (source=mcp); optional submitForReview
GET
/api/v1/social/posts/{postId}
One post with full event timeline
PATCH
/api/v1/social/posts/{postId}
Revise draft, pending, or rejected post
GET
/api/v1/social/media
Social media library (registered and generated assets)
GET
/api/v1/social/org-media
Browse org media storage (dashboard uploads — not yet in the social library)
POST
/api/v1/social/media
Register a public URL into the social library
POST
/api/v1/social/media/upload
Get a direct-upload target for a local image or video file
POST
/api/v1/social/media/generate
AI image generation — up to 4 aspectRatios per call (plan + credits per ratio)
Send Authorization: Bearer av_live_… on every request.
Typical agent loop
GET /api/v1/social/accounts — pick target account IDs
GET /api/v1/social/org-media (optional) — find an existing dashboard upload, then POST /api/v1/social/media to register its URL
POST /api/v1/social/media/generate or POST /api/v1/social/media — attach media
POST /api/v1/social/posts with targets, content, platformOverrides, proposedScheduleAt, and a note explaining the draft
Human approves in Dashboard → Social
If rejected, GET /api/v1/social/posts/{id} — read events, then PATCH with fixes and submitForReview: true
Caption limits enforced at validation: Instagram 2,200, Threads 500, X 280 characters.
LinkedIn PDF carousels
A LinkedIn carousel is a document post — one multi-page PDF the feed paginates into swipeable slides. Agents do not generate or upload that PDF: attach the slides as ordinary image assets in slide order and set postType: "document" on the LinkedIn target. Aveiro composes the document from those slides at publish time.
platformTitles.linkedin (max 400 chars) is the headline above the slides; without one, the first line of the LinkedIn caption is used. The caption and first comment publish as on a feed post, the 10-image cap does not apply (the slides become pages), a video cannot be a slide, and every other target still gets the same slides as images. Generate the deck at one aspect ratio — 1:1 or 4:5 read best, and a slide of a different shape is letterboxed rather than cropped.
Multi-aspect generation and per-platform crops
POST /api/v1/social/media/generate accepts aspectRatios — an array of up to four supported ratios (1:1, 4:5, 5:4, 3:4, 4:3, 2:3, 3:2, 9:16, 16:9). Default is ["16:9"]. Each ratio is generated independently with the same prompt and billed as one AI image generation.
The response is { assets, failures } — partial success is normal when one ratio fails quota or provider limits.
aveiro_social_generate_media accepts the same aspect_ratios parameter (max four).
Assign each returned asset to a platform. An assignment's platform field is what marks an asset as a crop: assets attached to a platform are that platform's own set and replace the generic one for it.
Assets left generic (platform: null) are the post's carousel, and every one of them publishes, in sortOrder. So attaching three ratios of the same picture without a platform no longer produces one auto-picked crop — it produces a three-slide carousel of the same picture on every target.
This used to be an auto-pick by aspect ratio. It was removed because it could not tell one visual in three crops apart from three different pictures, and it silently truncated the second case.
Org media storage — files uploaded through any dashboard dialog (brand assets, screenshots, AI images saved to the org library). Browse with GET /api/v1/social/org-media.
Social media library — assets registered or generated for social posts (GET /api/v1/social/media). Only these IDs attach to drafts.
To reuse an org upload on a social post, register its public URL with POST /api/v1/social/media (or aveiro_social_register_media). Registration is idempotent per URL. Use mediaType: "video" for Reels — the AI generator produces images only.
Direct uploads from agents
When the file only exists on the agent's side — an image generated on the caller's own AI plan, a video rendered locally — POST /api/v1/social/media/upload (or aveiro_social_prepare_upload) returns a short-lived upload target. The agent PUTs the raw bytes to it over plain HTTPS, then registers the returned publicUrl as usual. Images (JPEG/PNG/WebP/GIF, max 10MB) land in org image storage; videos (MP4/WebM/MOV, max 200MB) land directly on the video CDN, so no host: true is needed on the register call.
This flow needs a client that can make HTTP requests (shell or code execution, e.g. Claude Code or Cursor). Chat-only MCP clients can't push local bytes — they keep using register-by-URL. See the Social media API for the exact request/response shapes.
MCP tools
The Aveiro MCP server exposes thin wrappers around the same routes:
Tool
REST equivalent
aveiro_social_list_accounts
GET /api/v1/social/accounts
aveiro_social_list_posts
GET /api/v1/social/posts
aveiro_social_get_post
GET /api/v1/social/posts/{id}
aveiro_social_create_draft
POST /api/v1/social/posts
aveiro_social_update_draft
PATCH /api/v1/social/posts/{id}
aveiro_social_list_media
GET /api/v1/social/media
aveiro_social_list_org_media
GET /api/v1/social/org-media
aveiro_social_register_media
POST /api/v1/social/media
aveiro_social_prepare_upload
POST /api/v1/social/media/upload
aveiro_social_generate_media
POST /api/v1/social/media/generate
Tools are available on the hosted MCP server and the stdio server in mcp/aveiro. See the Aveiro MCP README in the repo for local setup.
Proposed vs confirmed schedule
Agents set proposedScheduleAt on create or update. The dashboard row shows this as a proposed time. It never triggers publish.
When a human clicks Approve, the datetime picker sets the confirmedscheduled_at. Only approved, scheduled posts reach Chirio via the dispatcher.
A proposal is not validated when the agent writes it, but it is when a human approves: approval requires a confirmed time in the future. A proposal that has since gone stale is refused at that point, and the reviewer picks a new time — it never becomes an instant publish.