Posting tools for your own AI agent
Building an agent that runs without a person in the chat? Skip MCP and wrap three REST endpoints as tools. You get every platform's publishing behind one API key, with refunds when a platform rejects a post.
Updated
The three tools most agents need
| Tool | Endpoint | Why |
|---|---|---|
list_accounts | GET /v1/accounts | So the agent posts to ids that exist |
publish_post | POST /v1/posts | Video, slideshow or image to many accounts, now or scheduled |
get_post | GET /v1/posts/{id} | Status and public URL per account, to report back or retry |
Add POST /v1/campaigns when the agent plans many posts at once, and GET /v1/stats when it should learn from results. Full shapes are in create a post and campaigns.
OpenAI Agents SDK (Python)
import os, requests
from agents import Agent, Runner, function_tool
H = {"Authorization": f"Bearer {os.environ['UGCAPI_KEY']}"}
@function_tool
def list_accounts() -> list[dict]:
"""Connected social accounts with id, platform and username."""
return requests.get("https://ugcapi.dev/v1/accounts", headers=H).json()["data"]
@function_tool
def publish_post(accounts: list[str], format: str, media: list[str], caption: str, scheduled_at: str | None = None) -> dict:
"""Publish or schedule a video, slideshow or image. format: video | slideshow | image."""
body = {"accounts": accounts, "format": format, "media": media, "caption": caption, "scheduled_at": scheduled_at}
return requests.post("https://ugcapi.dev/v1/posts", headers=H, json=body).json()
agent = Agent(name="UGC scheduler", instructions="Plan and schedule UGC. Never post sooner than 30 minutes from now.",
tools=[list_accounts, publish_post])
print(Runner.run_sync(agent, "Schedule hook-3.mp4 to every TikTok account tomorrow 9am UTC").final_output)Vercel AI SDK (TypeScript)
import { tool } from "ai";
import { z } from "zod";
export const publishPost = tool({
description: "Publish or schedule UGC to connected TikTok, Instagram, Facebook and YouTube accounts",
inputSchema: z.object({
accounts: z.array(z.string()),
format: z.enum(["video", "slideshow", "image"]),
media: z.array(z.string().url()),
caption: z.string(),
scheduled_at: z.string().optional(),
}),
execute: async (input) => {
const r = await fetch("https://ugcapi.dev/v1/posts", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.UGCAPI_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify(input),
});
return r.json();
},
});LangChain, CrewAI and the rest
Same idea: a function with a docstring and typed arguments that makes the HTTP call. LangChain's @tool decorator and CrewAI's BaseTool both take the Python function above with no changes beyond the decorator.
Design tips from teams running this in production
- Let code pick accounts, let the model write. Agents are great at captions and matching hooks to audiences; deterministic code is better at cadence. Use
POST /v1/campaignsfor spacing. - Pass an `external_id`. Tag posts with your own job or user id so the agent can find its own work later.
- Listen to webhooks, don't poll.
post.publishedandpost.failedtell your agent what happened. See webhooks. - Failed posts are refunded, so a retry loop on
platform_rejectedcosts nothing extra until it succeeds.
FAQ
MCP or REST for my agent?
MCP when a person chats with the agent and approves access with OAuth. REST with an API key when the agent runs on its own, in a job, a queue or your backend.
Is there an OpenAPI spec?
The docs list every endpoint with request and response examples, and llms.txt at ugcapi.dev/llms.txt gives agents a compact summary.
Can my users' agents post to their own accounts?
Yes. Create a connect link per user with their id as external_id, then pass the account ids they connected.