Developers

Public Publishing API

Upload media, create posts, and publish to Facebook, Instagram, TikTok, LinkedIn, Threads, Pinterest, and X, all scoped to a product via a workspace API key. The recommended way to integrate is the Connect API, a small, flat/api/connect/v1 surface that most scheduling tools can target as-is.

Need full control: explicit publish, job-run polling, signed webhooks, batch, per-channel settings? The advanced /api/public/v1 API further down exposes all of it. Both share the same auth, products, and publishing pipeline; use only these versioned public routes (internal app routes require Firebase user auth and are not part of the public contract).

Facebook, Instagram, and TikTok are manual-first over the API. Posts for those channels default to manual_reminder delivery: Markaestro never calls the platform's API for them. Publishing moves the post to the workspace's To post queue, where the owner downloads the media, posts natively, and confirms, so posts appear exactly as if created by hand. Pass deliveryMode: "direct_publish" on create to opt a post into official-API publishing instead. On TikTok this uses the inbox handoff unless settings.postMode is direct_post. LinkedIn, Threads, Pinterest, and X publish programmatically by default.

Workspaces can have multiple products. When you create an API key you either bind it to one product, so calls target that product automatically and requests for any other product are rejected, or make it an all-brands key that names the product on each request.

Building an AI agent?

Start with the AI agent guide instead. It has copy-paste tool schemas, a system-prompt brief, the retry and error-handling rules an agent needs, and a four-command quickstart. Your agent can also read /llms.txt directly.

Machine-Readable Spec

Every endpoint, request shape, response shape, and error code, as OpenAPI 3.1. Generated from the same schemas the API validates against, so it cannot describe an API we do not serve.

Connect API
Recommended
The default way to integrate: a flat, snake_case surface at /api/connect/v1 that most scheduling tools can target as-is. It maps the common create-upload-url → PUT → post convention onto the same workspace, auth, products, and publishing pipeline as the full API below. Set the client base URL to /api/connect and authenticate with a product-scoped workspace API key (scopes posts.read, posts.write, media.write).
GET/api/connect/v1/social-accounts

Lists connected Facebook, Instagram, TikTok, and LinkedIn destinations as flat accounts, each labeled with its product so clients can group and disambiguate. Each channel is its own dedicated path, no cross-channel fan-out.

GET/api/connect/v1/products

Lists brands (wire name: products) with their connected accounts nested, a brand-first picker.

POST/api/connect/v1/media/create-upload-url

Returns a short-lived, single-use signed PUT url plus a media id.

PUT<upload_url>

Upload the raw image bytes to the signed url. No API key needed. The signature authorizes it.

POST/api/connect/v1/posts

Creates a draft per selected account. Set is_draft=false with scheduled_at to schedule delivery; TikTok uses the creator-inbox handoff.

GET/api/connect/v1/posts

Lists workspace posts with flat status, caption, and media urls.

# 1. List connected accounts
curl "$MARKAESTRO_URL/api/connect/v1/social-accounts" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY"

# 2. Request a signed upload url, then PUT the bytes
curl -X POST "$MARKAESTRO_URL/api/connect/v1/media/create-upload-url" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mime_type": "image/png", "size_bytes": 184320, "name": "slide-1.png" }'
curl -X PUT "<upload_url>" -H "Content-Type: image/png" --data-binary @slide-1.png

# 3. Create a draft post for one or more accounts
curl -X POST "$MARKAESTRO_URL/api/connect/v1/posts" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "caption": "New drop",
    "media": ["ast_111", "ast_222"],
    "social_accounts": ["prod_123#instagram:instagram:ig_123"]
  }'

Each account from /social-accounts is labeled with its product, the wire-format name for a brand (the same account can appear under multiple brands), and its id encodes productId#destinationId, pass it back verbatim in social_accounts, and the request fans out one post per account. Each key is bound to one brand, so it only sees and posts to that brand. Facebook, Instagram, and TikTok posts are manual-first, created as drafts and published natively by the workspace owner from the Markaestro To post queue, never via the platform's API. LinkedIn, Threads, Pinterest, and X publish programmatically after an explicit publish action. Post status is one of draft, processing, posted, or failed. Every channel is a dedicated destination; publishing to one never fans out to another. Track publishing state through GET /api/connect/v1/posts.

Advanced: Full Public API

The complete /api/public/v1 surface: explicit publish, async job runs, signed webhooks, batch create, and per-channel settings. Use it when the Connect API is not enough.

Meta & TikTok Are Manual-First
Facebook, Instagram, and TikTok posts default to the manual To post queue, no platform API call, the workspace owner posts natively and confirms. Opt into API publishing per post with deliveryMode.
Instagram Login Supported
Brands can expose standalone Instagram professional accounts even when no Facebook Page is linked.
TikTok Supports Two Opt-In Paths
API publishing uses the creator-inbox handoff by default. Set settings.postMode to direct_post with a privacy level to request Direct Post when your TikTok app is approved for it.
Async by Design
Every publish returns a run id. Poll runs or subscribe to signed webhooks instead of assuming synchronous completion.
Brands and Destinations
Discover the brands and publish destinations available to the API key. Brands are called products in the wire format, paths and payloads use products/productId for backwards compatibility, and POST bodies also accept brandId as an alias.
GET/api/public/v1/products

Lists the key's brands plus the channels currently available for each one.

GET/api/public/v1/products/:id/destinations

Lists the publish destinations for that brand, including standalone Instagram Login, Facebook Page, Threads, LinkedIn Profile/Page, and connected TikTok destinations.

Media
Upload images or videos to Markaestro-managed storage before creating posts. New integrations should use direct upload sessions; multipart remains compatible.
POST/api/public/v1/media/upload-sessions

Creates a 15-minute direct upload session from fileName, contentType, and exact sizeBytes, reserving one media-upload quota unit.

PUT<uploadSession.uploadUrl>

Uploads raw bytes directly to storage using the returned Content-Type header; do not send the API key.

POST/api/public/v1/media/upload-sessions/:id/finalize

Verifies the stored type and size and returns the reusable media asset. Completed sessions are retry-safe.

POST/api/public/v1/media

Compatibility multipart upload. Returns an asset id and hosted URL.

Posts
Create, list, inspect, publish, and delete posts for Facebook, Instagram, LinkedIn, Threads, Pinterest, TikTok, and X.
POST/api/public/v1/posts

Creates a draft in the workspace. Facebook, Instagram, and TikTok default to manual posting (deliveryMode manual_reminder); pass deliveryMode direct_publish to opt a post into API publishing.

GET/api/public/v1/posts

Lists posts, newest first. Filter with ?status=scheduled to see what is queued, and ?productId= to scope to one brand. A brand-bound key is always limited to its own brand and may omit productId.

GET/api/public/v1/posts/:id

Returns current post status, delivery mode, and publish results.

POST/api/public/v1/posts/:id/publish

Queues an async publish run. Manual posts land in the workspace's To post queue for native posting; LinkedIn, Threads, Pinterest, and X publish directly; opted-in Meta posts publish via the official API, and opted-in TikTok posts use the inbox handoff.

DELETE/api/public/v1/posts/:id

Deletes the post from Markaestro. Uses the existing posts.write scope. Returns 400 VALIDATION_POST_IS_PUBLISHING while a publish run is in flight. Deleting a published post does not retract the live platform copy.

Runs and Webhooks
Track async work with polling or signed webhook delivery.
GET/api/public/v1/job-runs/:id

Returns queued, running, succeeded, or failed.

POST/api/public/v1/webhook-endpoints

Registers a webhook destination using an API key, up to 25 active endpoints per workspace.

GET/api/public/v1/webhook-endpoints

Lists registered webhook destinations for that API key scope.

DELETE/api/public/v1/webhook-endpoints/:id

Disables a webhook destination.

1. List products
Discover which products this API key can target.
curl "$MARKAESTRO_URL/api/public/v1/products" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY"
2. Inspect destinations
See the linked pages and accounts for a product before creating the post. Use the returned destinationId when a product has multiple destinations, such as a LinkedIn Profile plus Pages.
curl "$MARKAESTRO_URL/api/public/v1/products/prod_123/destinations" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY"
3. Upload media
Each post references previously uploaded media assets. Prefer create-session → direct PUT → finalize for new integrations; multipart remains supported.
# 1. Create a 15-minute upload session
UPLOAD=$(curl -s -X POST "$MARKAESTRO_URL/api/public/v1/media/upload-sessions" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"launch-1.jpg","contentType":"image/jpeg","sizeBytes":184320}')

# 2. PUT bytes directly to uploadSession.uploadUrl with its returned headers
curl -X PUT "<upload_url>" -H "Content-Type: image/jpeg" --data-binary @launch-1.jpg

# 3. Finalize; the response contains asset.id for mediaAssetIds
curl -X POST "$MARKAESTRO_URL/api/public/v1/media/upload-sessions/<session_id>/finalize" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY"
4. Create a post
Create a draft using those asset ids. Instagram defaults to manual posting; add "deliveryMode": "direct_publish" to opt this post into official-API publishing.
curl -X POST "$MARKAESTRO_URL/api/public/v1/posts" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: post-001" \
  -d '{
    "channel": "instagram",
    "caption": "Launch day carousel",
    "mediaAssetIds": ["ast_123", "ast_124"],
    "productId": "prod_123",
    "destinationId": "instagram:instagram:ig_123"
  }'
TikTok Example
TikTok posts land as Markaestro drafts and default to manual posting from the To post queue. With deliveryMode: "platform_inbox" (or direct_publish), an explicit publish sends the draft to the creator's TikTok inbox instead.
curl -X POST "$MARKAESTRO_URL/api/public/v1/posts" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: post-tt-001" \
  -d '{
    "channel": "tiktok",
    "caption": "Spring drop teaser",
    "mediaAssetIds": ["ast_vid_123"],
    "productId": "prod_123",
    "destinationId": "tiktok:tiktok:tt_open_123"
  }'
5. Queue publish
Publishing creates an async run. Manual posts (the Facebook/Instagram/TikTok default) move to the To post queue and fire post.action_required; LinkedIn, Threads, Pinterest, X, and opted-in Meta posts publish directly; opted-in TikTok posts queue the inbox handoff.
curl -X POST "$MARKAESTRO_URL/api/public/v1/posts/pst_123/publish" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY" \
  -H "Idempotency-Key: publish-001"
6. Review the schedule and cancel
List what is queued for a brand, then delete anything you no longer want to go out. Both use scopes existing keys already carry: posts.read and posts.write.
# The key is already bound to one brand
curl "$MARKAESTRO_URL/api/public/v1/posts?status=scheduled&limit=100" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY"

# Cancel one
curl -X DELETE "$MARKAESTRO_URL/api/public/v1/posts/pst_123" \
  -H "Authorization: Bearer $MARKAESTRO_API_KEY"
Webhook Payload Example
Deliveries are signed with HMAC using your webhook secret.
{
  "id": "evt_123",
  "type": "post.action_required",
  "createdAt": "2026-04-08T18:06:10.000Z",
  "workspaceId": "ws_123",
  "data": {
    "postId": "pst_123",
    "channel": "instagram",
    "status": "platform_action_required",
    "nextAction": "post_manually_from_reminder"
  }
}
Channel Behavior
Validation and delivery rules enforced by the public API.

Facebook

Text-only, image, or video posts. Up to 10 images or 1 video per post. Manual posting by default; direct publish on opt-in.

Instagram

At least one image or video, up to 10 items. Single video publishes as a Reel. Manual posting by default; direct publish on opt-in.

TikTok

At least one image or video. Up to 35 images or 1 video. Manual posting by default; opted-in posts go to the creator's TikTok inbox, or straight to the profile with TikTok postMode direct_post.

LinkedIn

Text, single image, single video, or organic multi-image posts up to 20 images. Target either the connected Profile or a managed Page.

X

Text, up to four images, one GIF, or one video. Reply controls are applied per post, and publishing is blocked when the workspace X cost budget is exhausted.