social.draftPost
Save a social post as a DRAFT for chosen connected accounts (zernioAccountId values from social.accounts), optionally with a placement per account, picture or video addresses (https) and a planned time so it sits on the Some calendar: when {day,time} (day is today, tomorrow, a weekday name in English or YYYY-MM-DD; nextWeek for the following week; time HH:MM) is resolved by the server in the workspace timezone, never worked out by you; or an exact plannedAt (ISO with offset). With no time given, propose one from social.week and say so; never guess. It publishes nothing: the person reads it and presses Publish in Selda. Placements per network: instagram feed|carousel|story|reel, facebook feed|story|reel, tiktok none (the media decides); other networks take none. A story, reel or carousel needs media.
| REST | POST /v1/social/draft-post |
| RPC form | POST /mcp/mutate with "fn": "social.draftPost" |
| Scope | write |
| Sandbox key | allowed |
| Extra entitlement | none |
| MCP tools | none, HTTP only |
| Internal function | social/posts:draftForKey |
A free sandbox key may call it.
Arguments
| Argument | Type | Required | Notes |
|---|---|---|---|
projectId | id<"projects"> | required | |
content | string | required | |
accountIds | string[] | required | |
placements | object[] | optional | |
mediaUrls | string[] | optional | |
plannedAt | string | optional | |
when | { day: string; time?: string; nextWeek?: boolean } | optional | |
timezone | string | optional |
What Selda fills in
You never send these. The HTTP layer overwrites them from the API key, which is what keeps one organisation’s data out of another’s reach.
| Argument | Filled in from |
|---|---|
orgId | your organisation, resolved from the API key |
userId | the user the API key belongs to |
Example request
Required arguments only. Values in angle brackets are yours to fill in; the optional ones are in the table above.
curl -X POST https://api.selda.ai/v1/social/draft-post \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "projectId": "<projectId>", "content": "<content>", "accountIds": ["<accountIds>"] }'{
"projectId": "<projectId>",
"content": "<content>",
"accountIds": ["<accountIds>"]
}The same call in the RPC form
One dispatcher answers both, so this is the same request at a different URL. Use it when you
are already posting to /mcp/*, or for a function that has no path of its own.
curl -X POST https://api.selda.ai/mcp/mutate \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "fn": "social.draftPost", "args": { "projectId": "<projectId>", "content": "<content>", "accountIds": ["<accountIds>"] } }'{
"fn": "social.draftPost",
"args": {
"projectId": "<projectId>",
"content": "<content>",
"accountIds": ["<accountIds>"]
}
}Example response
Every endpoint answers in the same envelope, whichever URL you called.
{
"value": <the function's return value>,
"request_id": "req_..."
}The shape of value is not documented here. social/posts:draftForKey declares no returns validator, so there is nothing to derive it from, and a shape written out by hand here is a shape that goes stale without anything noticing. Call it once against a test key and read what comes back.
On failure the body is { "error": { "type", "code", "message", "request_id" }, "request_id" } and request_id is echoed in the X-Request-Id header.
Generated from convex/lib/mcpRegistry.ts. Nothing in this reference sends: launchRun is in no
registry and never will be, so a script can prepare a campaign completely and a person still
presses send in the app.