Quickstart
Draft and submit a social post for approval in four signed requests.
This walkthrough covers the end-to-end agent flow: find a channel, create a draft, submit it for approval, and read what's already live. Every request is signed — see Authentication for the details.
Set up a signing helper
These examples reuse the authHeaders helper from
Authentication — it refreshes x-date and the
signature before each call. They use Node's built-in fetch (Node 18+), so there are no
dependencies beyond crypto. Each step below runs inside an async function so it can
await the response.
const crypto = require("crypto");
const KEY = "<api key>";
const SECRET = "<secret>";
// Base URL from the environments table — already ends in /v1.
const BASE = "https://us-central1-seamless-pro.cloudfunctions.net/externalApi/v1";
// Recompute x-date + signature before every call (30s TTL).
function authHeaders() {
const xDate = new Date().toUTCString(); // e.g. Sun, 06 Dec 2020 12:59:11 GMT
const signature = crypto
.createHmac("sha256", SECRET)
.update(xDate)
.digest("hex");
return {
"x-api-key": KEY,
"x-date": xDate,
"x-signature": signature,
"x-api-version": "1", // required by the External API
};
}Find a channel id
List the channels your org can post to. Use a returned id as the key in a scheduled
post's channels map.
const res = await fetch(`${BASE}/channels`, { headers: authHeaders() });
const channels = await res.json();
console.log(channels);[
{ "id": "FB_PAGE_123456789012345", "type": "FB_PAGE", "name": "Acme Sports" }
]See GET /channels.
Create a draft
Create a scheduled post with status: "DRAFT". A draft is permissive — no validation
of media, caption, or date — so it's the safest way to stage content.
const res = await fetch(`${BASE}/scheduled-posts`, {
method: "POST",
headers: { ...authHeaders(), "Content-Type": "application/json" },
body: JSON.stringify({
status: "DRAFT",
media: "https://res.cloudinary.com/demo/image/upload/sample.png",
mediaType: "IMAGE",
scheduledFor: "2026-07-01T13:00:00.000Z",
channels: {
FB_PAGE_123456789012345: { mediumType: "FB_PAGE", caption: "Hello 👋" },
},
}),
});
const post = await res.json();
console.log(post.id); // keep this for the next stepThe response (201) returns the created post — keep its id. See
POST /scheduled-posts.
Submit it for approval
Move the draft into the approval queue. This runs full publish validation (a future
scheduledFor, a media URL, ≥1 channel, and a caption per channel).
const res = await fetch(`${BASE}/scheduled-posts/${post.id}/submit-for-approval`, {
method: "POST",
headers: authHeaders(),
});A human then reviews and schedules it in the app. See Submit for approval.
Read what's live
Read the posts already published on a channel, with their engagement metrics.
const res = await fetch(
`${BASE}/posts?mediumId=FB_PAGE_123456789012345&limit=10`,
{ headers: authHeaders() }
);
const posts = await res.json();
console.log(posts);See GET /posts.
The API can't take a post live. status may only be DRAFT or
WAITING_FOR_APPROVAL — the move to SCHEDULED (and publishing) is always done by a
human in the app. See the state machine.