Dynamo Developer DocsDynamo Developer Docs
Getting Started

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.

Signing helper
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 step

The 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.

On this page