Skip to main content
Public API release notes and documentation changes. Subscribe via RSS (feed URL below) or connect the feed to Slack or email automation tools. RSS feed: https://docs.mymarky.ai/changelog/rss.xml Email: We are wiring Mailchimp digests for workspace administrators on accounts that use the API. Until that is live, use RSS so you do not miss breaking changes or new endpoints.
workspacesbreaking
Workspaces keep their brand in strategy and design_style; the legacy brand fields are removed

Brand settings live in strategy and design_style

  • Removed from the workspace (GET/POST/PATCH on /api/workspaces and /api/businesses, and the get_workspace / update_workspace MCP tools): tone, audience_combined, caption_writing_rules, caption_suffix, custom_caption_prompt, custom_meme_prompt, custom_carousel_prompt, tagline, ctas, palettes, header_font, body_font, logo_url, logo_background_color, logo_width, include_stock, aspect_ratio, creative_types and use_logo_on_post.
  • Where they went — write brand voice, audience, caption rules and calls to action into strategy, and colours, fonts, logo placement, content mix, stock photos and aspect ratio into design_style. Both are markdown documents on the workspace.
  • No compatibility window — this applies to every Marky-Version. A request that names a removed field gets a 422 whose message names the field and says where it goes now. Reads no longer include the keys.
  • Kept — strategy, design_style, brand_style_grid_url, platform_writing_instructions, events and timezone. Settings saved in the Marky app are not deleted; the app still edits them.
  • Marky plugin users: update the plugin to its latest version.
workspacesdeprecation
Businesses are now workspaces: new names, old names still work

Businesses are now workspaces

  • New: workspace endpoints — GET /api/workspaces (list_workspaces), POST /api/workspaces (create_workspace), GET, PATCH and DELETE /api/workspaces/{workspace_id} (get_workspace, update_workspace, delete_workspace). Same request and response bodies as the business endpoints, including under a pinned Marky-Version.
  • Deprecated, still working — /api/businesses and /api/businesses/{business_id} (list_businesses, create_business, get_business, update_business, delete_business) are marked deprecated in the OpenAPI document. Nothing you call today stops working, and no removal date is set.
  • Media uploads take workspace_id — POST /api/media/from-url, /api/media/from-reference and /api/media/uploads accept workspace_id. business_id is deprecated but still accepted; if you send both, they must match or you get a 422.
  • MCP tools renamed — tools/list shows list_workspaces, get_workspace and update_workspace instead of list_businesses, get_business and update_business. The old names are hidden but still callable, so existing agents keep working. See MCP Server.
  • Unchanged — nested paths such as /api/businesses/{business_id}/posts, the business_id argument on MCP tools, and business_id in response bodies and webhook payloads. That value is the workspace ID.
  • Error messages say “Workspace not found” and “Workspace does not belong to this org”. Error codes are unchanged. Full mapping: Versioning.
sdksn8n
TypeScript and Python SDKs and an n8n node

SDKs and an n8n node

  • New: SDKs for TypeScript and Python (mymarky). Generated from this API’s OpenAPI document, so every endpoint is typed. They pin Marky-Version, raise typed errors, retry safely (create calls get an automatic Idempotency-Key), and verify webhook signatures (timestamp + replay window included).
  • New: n8n community node (n8n-nodes-marky): list workspaces and connected accounts, create and schedule posts, upload media from a URL, and a trigger for post.published. See n8n.
webhookssecurity
Webhook signatures are now verifiable and replay-safe; private URLs refused

Webhook signatures you can verify

  • X-Marky-Signature now verifies. It is sha256= + the hex HMAC-SHA256 of "{X-Marky-Timestamp}." + raw body, keyed with your webhook secret, and the body we send is byte-for-byte the one we signed. Previously the signed bytes and the sent bytes differed, so the signature could not be verified.
  • New X-Marky-Timestamp header — part of the signed string. Reject deliveries more than 5 minutes old so a captured request cannot be replayed. Retries are re-signed with a fresh timestamp.
  • Private addresses are refused — POST /api/webhooks returns 400 when the URL’s host resolves to a private, loopback, link-local or cloud-metadata address, and every delivery re-checks the address it connects to.
  • Verification code for Python and Node.js: Webhooks.
postsscheduling
Schedule a list of posts in one call, in order and in local time

Schedule a list of posts in one call

  • New: POST /api/businesses/{business_id}/posts/schedule (schedule_posts) — send up to 50 post_ids in the order they should go out. mode: "queue" (default) appends them to the posting queue in exactly that order; mode: "at" schedules each at the matching times_local (e.g. 2026-10-05T09:00), read in the business’s timezone with daylight saving handled for you. Each post gets its own answer: publish_time_local, warnings, a revision_id, or an error for that post only, so one bad post never fails the batch. Calling it again for posts already queued changes nothing (ALREADY_SCHEDULED).
  • queue_post is safe to call in parallel — several calls at once used to be able to land on the same place in line. They now always get distinct places. To keep a specific order, use schedule_posts with the list.
  • A post’s date is kept — a NEW post created with scheduled_publish_time is scheduled at that time by queue_post (and schedule_posts), instead of being moved to the next open slot. The response has scheduled_publish_time set and queue null.
  • Unfilled placeholders are refused — a caption that still contains a [[placeholder]] (for example [[phone number]]) is not queued: queue_post answers 409 CAPTION_HAS_PLACEHOLDER and leaves the post unchanged. Fill it in with update_post first.
  • queue.warnings on queued posts — get_post, list_posts and list_queued_posts now warn when a queued post’s caption says “today”, a weekday or “this week” but it lands on another day (DATE_BOUND_COPY), or when it lands more than 90 days out (FAR_FUTURE). list_queued_posts rows also gain estimated_publish_time_local. Warnings never block anything.
businesses
Renaming a business onto another business's name is refused

Renaming a business onto another business’s name is refused

  • PATCH /api/businesses/{business_id} returns 409 NAME_MATCHES_OTHER_WORKSPACE when the new name is already used by another business in your org. The match ignores case and surrounding spaces; otherwise it is exact. Nothing in the request is saved, including any other fields sent with the name.
  • Why — a rename onto a sibling’s name almost always means the content was meant for that other business. The error body carries other_business_id and other_business_name so you can send the update there instead.
  • Override — send allow_duplicate_name: true when you really do want two businesses with the same name (for example while swapping two names). Renames to a unique name, re-casing a business’s own name, and updates that don’t touch name behave exactly as before.
postsscheduling
Schedule accepts DRAFT (approved) posts

Schedule accepts approved (DRAFT) posts

  • POST /api/businesses/{business_id}/posts/{post_id}/schedule accepts DRAFT — a post you have approved sits in DRAFT, and scheduling it is the normal next step. The endpoint used to answer 400 invalid_request (“Cannot schedule a post with status ‘DRAFT’”), so integrations had to move the post back to NEW first, which threw away the approval. Now it schedules directly and the post becomes SCHEDULED.
  • Nothing else changes — NEW and SCHEDULED posts behave as before. REJECTED posts still return 400 invalid_request, and published posts still return 400 POST_ALREADY_PUBLISHED. If you worked around the old 400 by demoting posts to NEW, you can drop that step.
postsmedia
Set your own video cover (thumbnail) on a post

Set your own video cover

  • cover_media_id on create and update — pass the Marky media id of an image to use as the cover (the still shown before the video plays). Upload the still first with POST /api/media/from-url (or /api/media/uploads) and pass the id it returns. Instagram uses it as the Reel cover; Facebook and Pinterest use it too. Without it, Marky keeps making a cover from the video’s first frame, exactly as before.
  • video_cover_timestamp_ms — TikTok’s API only accepts a frame, never an image, so this picks the millisecond it uses (default 1000). Send it alone if TikTok is the only place you care about the cover.
  • Both fields come back on reads — get_post and list_posts now return cover_media_id and video_cover_timestamp_ms, so you can confirm what landed.
  • Nothing breaks — both fields are optional, and a post that omits them behaves exactly as it does today. LinkedIn, X, YouTube and Google Business choose their own cover and ignore these fields. Replacing a post’s media later does not clear the cover; send a new cover_media_id.
breakingbusinesses
List businesses: slimmer rows, name filter, higher default limit

List businesses returns summary rows now

  • Slimmer list payload (breaking) — GET /businesses (list_businesses) now returns summary rows only: id, name, description, industry, website, language, created_at, updated_at. The brand profile (tone, palettes, fonts, logo, writing rules) that used to ride along on every row is gone from the list. Action: if you read brand fields off the list, call get_business with the id instead. The list exists to find a business_id; get_business is the full profile.
  • Name filter — pass ?name= to filter by a case-insensitive substring (bakery matches Eli's Bakery). % and _ are matched literally, not as wildcards.
  • Higher default limit — the default page size is now 100 (max 100). A typical org fits in one page. If has_more is true, keep paging with next_cursor before concluding a business does not exist.
breakingposts
Breaking: publish_to renamed to restrict_publish_to

Breaking: publish_to → restrict_publish_to

  • Clearer name for platform targeting — the optional post field publish_to is now restrict_publish_to on create, update, schedule, and publish, and in responses. The name makes the behavior obvious: omit it or set it to null to publish to ALL connected platforms; provide a list (e.g. ["instagram", "facebook"]) to restrict to just those.
  • Action required — the old publish_to key is no longer accepted; sending it returns a 422 naming the unknown field. Rename it to restrict_publish_to in your requests, and read restrict_publish_to from responses. (It briefly shipped as an accepted alias; that alias is now removed.)
rate-limits
Daily and weekly rate limits added alongside the per-minute limit

Rate limits

  • Daily and weekly limits added — alongside the existing 100 requests/minute, each API key now also has a 2,000 requests/day and 10,000 requests/week limit. These are generous for normal automation (a weekly batch of 10 posts uses ~20 requests). A 429 response names the specific window you hit, and the X-RateLimit-* and Retry-After headers tell you when to retry. See Errors for header details.
breakingendpoints
Business-scoped endpoints are now nested under /api/businesses/{business_id}

Breaking changes

  • Business-scoped endpoints are now nested under the business — endpoints that operate on a single business now live under /api/businesses/{business_id}/... (for example POST /api/businesses/{business_id}/posts, GET /api/businesses/{business_id}/topics, POST /api/businesses/{business_id}/media). The business_id moves out of the JSON body and query string and into the URL path. Org-level endpoints (/api/businesses, /api/keys, /api/webhooks, /api/feedback, /api/health) are unchanged.
featurespublishingstatsintegrations
Publish-now, stats, and platform-post listing endpoints

New features

updatesmediareliability
Media upload improvements, reliability & billing fixes

Updates

  • Giphy URLs in media uploads — giphy.com is now an allowed host suffix when uploading media, so you can pass Giphy-hosted GIFs directly without re-uploading.
  • Better image upload handling — Images with malformed EXIF metadata no longer fail to upload, and image generation errors now return clearer messages.
  • Retry on transient errors — CRUD endpoints now automatically retry transient database errors, improving reliability for posts, businesses, topics, and media.
  • Restored analytics fields — Post-count and period columns are back on business analytics views, so business summaries return complete data.

Bug fixes

  • Fixed search fallback when looking up uploaded images — orphaned uploads are now correctly resolved.
  • Plan restrictions are kept in sync when Stripe sends subscription updates, preventing stale limits after plan changes.
  • Narrowed the checkout fraud block to true fraud signals so legitimate customers are no longer incorrectly blocked.
  • Stripe InvalidRequestError responses are now logged with full context before being surfaced as a 400, making billing failures easier to debug.

Security

  • Row-level security is now enforced on API keys and Canva connections, so credentials are only readable by the org that owns them.
featuresgenerationcanva
Creative formats, AI image styles & reference images

New features

  • Creative format selection — You can now specify a creative_formats array when creating posts to control the visual format of each post. Options include design, carousel, meme, image, gif, and video. Formats are cycled round-robin across your generated posts.
  • AI image style modifiers — Marky now applies style modifiers and creative type mapping when generating AI images, producing visuals that better match the tone and format of each post.
  • Reference images for video generation — Pass reference_images when generating videos to guide the visual style. Useful for maintaining brand consistency across video content.
  • Audio URL for shorts — You can now provide an audio_url when generating shorts, giving you direct control over the audio track instead of relying on auto-selected music.

Improvements

  • Canva design owner filtering — Marky now identifies the owner of imported Canva designs and surfaces a clearer error when a design is shared as view-only, so you know exactly what to fix.
  • Imagery preferences in media search — Your business-level imagery preferences are now consistently applied during media selection, so generated posts better reflect your visual brand.
  • Webhook reliability — Background poster generation and webhook delivery have been restored for more reliable post-publish notifications.
  • Analytics accuracy — Post analytics now correctly handle posts with zero views instead of treating them as missing data.

Bug fixes

  • Fixed thumbnail generation for deleted or inaccessible videos — the system now handles these gracefully instead of failing silently.
  • Improved video URL detection to support additional formats during thumbnail extraction.
  • Fixed an issue where providing a custom_idea without topics would not use the idea as the post topic.
  • Corrected the post restriction period reset schedule for subscription limits.
featuresvideopublishing
First comment, video upgrades, smart crop & more

New features

  • First comment on publish — When a post goes live on Facebook or LinkedIn, Marky can now automatically add the post link as the first comment. Useful for driving traffic without cluttering the caption.
  • Video speedup transforms — Speed up video clips during post generation. Marky applies the transform automatically when a faster pace fits the design.
  • Bring Your Own Key (BYOK) for Gemini — Connect your own Google Gemini API key to generate images and videos. BYOK orgs are not charged Marky credits for AI generation in Max chat.
  • Carousel video support — Video generation now works with multi-page (carousel) templates, so you can create richer video posts.
  • AppSumo credit allowance — AppSumo users now receive a monthly credit allowance.

Improvements

  • Smarter text sizing — Post designs now auto-cap character counts based on the actual box dimensions and a minimum readable font size. Overflowing CTA text is shortened automatically.
  • Layout-aware copy — The LLM receives max_lines and chars_per_line constraints so generated text fits the design without overflow.
  • Smart image cropping — Google Vision CROP_HINTS are used to frame subjects better across all generated images.
  • Better video music — Background music volume is reduced, and the music picker uses genre-based fallback instead of random selection when no audio tags are set.
  • Imagery preferences — Your business-level imagery preferences now influence the image keywords Marky generates, giving you more relevant visuals.
  • Onboarding improvements — New businesses generate 15 candidate posts during onboarding and surface the best 10 (previously 10 → 3).

Bug fixes

  • Fixed a crash caused by non-numeric letterSpacing values in post designs.
  • Fixed text vertical alignment in video rendering.
  • Fixed an issue where video audio tracks were not persisted when saving designs.
  • Corrected media type detection — uploaded videos are no longer incorrectly tagged as images.
  • Fixed empty-string UUID handling in post placeholder creation.
  • Resolved an error when extracting frames from inaccessible video URLs — the API now returns an actionable error message.
  • Fixed video foreground animations using an unsupported ZOOM effect.
documentationrate-limits
Docs & limits
  • Clarified rate limits in the Introduction and Errors guides (100 requests per minute).
  • Added this changelog with an RSS feed for integrations and automation tools.
launchapi
Public API preview
Public API (preview) — Automate posting and workspace management:
  • Org-scoped API keys (create in Organization Settings in the dashboard).
  • Posts, businesses, topics, library / media, webhooks, and feedback endpoints.
  • Base URL: https://api.mymarky.ai/api
The API remains in preview; see Terms and Versioning for how we handle changes.