{"openapi":"3.1.0","info":{"title":"MarkMate Public REST API","version":"2026-08-04","description":"Scoped, workspace-aware REST access to the generally available MarkMate operation catalog. Read operations execute directly; writes are prepared for a human approval and can never be approved by an API key or AI agent."},"servers":[{"url":"https://markmateos.com/api","description":"MarkMate API over HTTPS"}],"externalDocs":{"description":"Human-facing API guide and approval rules","url":"https://markmateos.com/developers"},"security":[{"ApiKeyAuth":[]}],"paths":{"/v1/workspaces/{workspaceId}/operations":{"get":{"operationId":"listOperations","summary":"List operations available to the API key","description":"Returns only generally available operations allowed by this key. Private, disabled, and unallowlisted operations behave as unknown.","parameters":[{"$ref":"#/components/parameters/WorkspaceId"}],"responses":{"200":{"description":"Available operation summaries.","content":{"application/json":{"schema":{"type":"object","required":["operations"],"properties":{"operations":{"type":"array","items":{"$ref":"#/components/schemas/OperationSummary"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/workspaces/{workspaceId}/operations/{opId}":{"get":{"operationId":"getOperation","summary":"Inspect one operation and its exact schemas","description":"Returns the operation metadata plus the exact input_schema and output_schema exposed to this API key. Use this endpoint before constructing an execution request.","parameters":[{"$ref":"#/components/parameters/WorkspaceId"},{"$ref":"#/components/parameters/OperationId"}],"responses":{"200":{"$ref":"#/components/responses/OperationDetail"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/UnknownOperation"}}}},"/v1/workspaces/{workspaceId}/connections":{"get":{"operationId":"listConnections","summary":"List connections visible to the API key","description":"Returns same-workspace connections for providers represented by the available catalog and filtered by the key connection allowlist.","parameters":[{"$ref":"#/components/parameters/WorkspaceId"}],"responses":{"200":{"description":"Visible connection metadata. Credentials are never returned.","content":{"application/json":{"schema":{"type":"object","required":["connections"],"properties":{"connections":{"type":"array","items":{"type":"object","required":["id","provider","displayName","status"],"properties":{"id":{"type":"string"},"provider":{"type":"string"},"displayName":{"type":"string"},"status":{"type":"string"}}}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"}}}},"/v1/workspaces/{workspaceId}/operations/prepare-write":{"post":{"operationId":"prepareWriteOperation","summary":"Prepare a write for human approval","description":"Creates or reuses a bounded approval draft. This endpoint does not execute the operation and has no approval-decision capability.","parameters":[{"$ref":"#/components/parameters/WorkspaceId"}],"requestBody":{"$ref":"#/components/requestBodies/OperationRequest"},"responses":{"201":{"description":"Approval draft prepared.","content":{"application/json":{"schema":{"type":"object","required":["approvalId","status","statement","riskLevel"],"properties":{"approvalId":{"type":"string"},"status":{"type":"string"},"statement":{"type":"string"},"riskLevel":{"type":"string"}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"$ref":"#/components/responses/ValidationError"}}}},"/v1/workspaces/{workspaceId}/operations/run":{"post":{"operationId":"runOperation","summary":"Run a read or an already-approved write","description":"Read operations may run directly. Write operations require an existing human approvalId; this route never creates or decides approval on behalf of a caller.","parameters":[{"$ref":"#/components/parameters/WorkspaceId"}],"requestBody":{"$ref":"#/components/requestBodies/RunRequest"},"responses":{"200":{"description":"Operation result and settlement metadata.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/UnknownOperation"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/ValidationError"}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"MarkMate API key","description":"A workspace-scoped API key created by an owner or admin in Settings."}},"parameters":{"WorkspaceId":{"name":"workspaceId","in":"path","required":true,"schema":{"type":"string","minLength":1},"description":"Workspace bound to the API key."},"OperationId":{"name":"opId","in":"path","required":true,"schema":{"type":"string","minLength":1},"description":"Operation id returned by the list endpoint."}},"schemas":{"OperationSummary":{"type":"object","required":["id","platform","title","description","requiresApproval","sideEffect","creditCost"],"properties":{"id":{"type":"string","description":"Registry operation identifier."},"platform":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"requiresApproval":{"type":"boolean"},"sideEffect":{"type":"string"},"creditCost":{"type":"integer","minimum":0}}},"OperationRequest":{"type":"object","required":["opId","input"],"properties":{"opId":{"type":"string"},"input":{"type":"object","additionalProperties":true},"connectionId":{"type":"string"},"idempotencyKey":{"type":"string","minLength":1,"maxLength":200}}},"RunRequest":{"type":"object","required":["opId","input"],"properties":{"opId":{"type":"string"},"input":{"type":"object","additionalProperties":true},"connectionId":{"type":"string"},"approvalId":{"type":"string"},"idempotencyKey":{"type":"string","minLength":1,"maxLength":200}}},"Error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"string","description":"Stable MarkMate error code."},"message":{"type":"string"},"details":{"type":"object","additionalProperties":true}}}},"requestBodies":{"OperationRequest":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperationRequest"}}}},"RunRequest":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunRequest"}}}}},"responses":{"Unauthorized":{"description":"Missing, expired, revoked, or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The key or workspace role lacks the required capability or resource scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnknownOperation":{"description":"The operation is unavailable, disabled, or not allowlisted for this key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"ValidationError":{"description":"The operation input or request shape failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"The idempotency key or approval state conflicts with an existing execution.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"OperationDetail":{"description":"Operation metadata and exact schemas for this authenticated key.","content":{"application/json":{"schema":{"type":"object","required":["operation"],"properties":{"operation":{"allOf":[{"$ref":"#/components/schemas/OperationSummary"},{"type":"object","required":["input_schema","output_schema"],"properties":{"input_schema":{"type":"object","additionalProperties":true},"output_schema":{"type":"object","additionalProperties":true}}}]}}}}}}}},"x-markmate-available-operations":[{"id":"ai_analyze_data","platform":"ai","platformName":"MarkMate AI","title":"Analyze data with AI","description":"Give the AI a JSON dataset (e.g. a GA4/GSC report output) and a question; get a concise analyst answer. Ideal as the summarize step of a reporting workflow.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"starter"},{"id":"ai_ask","platform":"ai","platformName":"MarkMate AI","title":"Ask AI","description":"Send a prompt to the AI model and get a text reply. Use for drafting copy, brainstorming, rewriting, or answering marketing questions inside a workflow.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"starter"},{"id":"ai_structure","platform":"ai","platformName":"MarkMate AI","title":"Structured AI output","description":"Ask the AI to return JSON matching a JSON Schema you provide. Use to EXTRACT structured fields from text/data (sentiment, entities, categories) or GENERATE a structured object (ad copy, a brief). Returns the parsed object.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"starter"},{"id":"creative_clone_ad","platform":"creative","platformName":"Creative studio","title":"Adapt an existing ad","description":"Create bounded creative adaptations from an ad supplied by the workspace. It preserves the source claims, discloses what changed, and never scrapes or copies a third-party ad automatically.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"creative_competitor_ad_library","platform":"creative","platformName":"Creative studio","title":"Analyze competitor ad observations","description":"Normalize and summarize supplied competitor-ad observations by advertiser, platform, format, and theme. It makes no scraping or provider calls and does not imply ownership of third-party creative.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"creative_generate_ad_variants","platform":"creative","platformName":"Creative studio","title":"Generate ad creative variants","description":"Draft channel-aware ad copy and visual directions from a supplied brand kit. The AI call is rollout-gated and the result is always a reviewable draft; it never publishes or spends.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"free"},{"id":"creative_image_prompt","platform":"creative","platformName":"Creative studio","title":"Prepare an image-generation brief","description":"Turn a brand kit and campaign concept into a bounded, accessible image or multimodal brief with aspect ratio, safe-area guidance, and negative prompts. It does not call an image provider.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"cro_landing_page_audit","platform":"cro","platformName":"Conversion optimization","title":"Audit landing-page conversion readiness","description":"Run bounded, provider-independent CRO checks over a supplied landing-page snapshot: message clarity, CTA, form friction, trust, accessibility, reachability, and performance evidence. It is a readiness checklist, not a conversion prediction.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"cro_landing_page_vision_review","platform":"cro","platformName":"Conversion optimization","title":"Review landing-page screenshot with AI vision","description":"Use the explicitly enabled Anthropic vision capability to review a supplied landing-page screenshot for hierarchy, readability, CTA visibility, trust, form friction, accessibility, and layout observations. The image is untrusted evidence; the result is advisory and never approves a write.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"free"},{"id":"email_deliverability_audit","platform":"email","platformName":"Email lifecycle","title":"Audit email deliverability readiness","description":"Evaluate supplied MX, SPF, DKIM, and DMARC evidence with bounded deterministic checks. This read-only reducer makes no DNS/provider calls and cannot authorize a campaign send.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"ga4_fields","platform":"ga4","platformName":"Google Analytics 4","title":"GA4 available fields","description":"List common Google Analytics 4 metrics and dimensions for ga4_get_report. Prefer keyEvents over the legacy conversions metric and sessionDefaultChannelGroup for reports built from session metrics.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"ga4_get_report","platform":"ga4","platformName":"Google Analytics 4","title":"Get GA4 report","description":"Fetch Google Analytics 4 metrics (e.g. activeUsers, sessions, conversions) optionally broken down by dimensions, over a date range.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":true,"minimumPlan":"free"},{"id":"ga4_select_accounts","platform":"ga4","platformName":"Google Analytics 4","title":"Select GA4 properties","description":"List the Google Analytics 4 properties this connection can access. Use the returned property_id with ga4_get_report.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":true,"minimumPlan":"free"},{"id":"gsc_fields","platform":"gsc","platformName":"Google Search Console","title":"Search Console available fields","description":"List the Google Search Console dimensions (query, page, country, device, date, searchAppearance) and metrics (clicks, impressions, ctr, position) available in gsc_get_report.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"gsc_get_report","platform":"gsc","platformName":"Google Search Console","title":"Get Search Console report","description":"Fetch Google Search Console search performance (clicks, impressions, CTR, average position) broken down by query, page, country, or device.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":true,"minimumPlan":"free"},{"id":"gsc_select_accounts","platform":"gsc","platformName":"Google Search Console","title":"Select Search Console sites","description":"List the Google Search Console sites this connection can access. Use the returned site_url with gsc_get_report.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":true,"minimumPlan":"free"},{"id":"policy_ad_precheck","platform":"policy","platformName":"Policy","title":"Pre-check ad policy risks","description":"Run a bounded, provider-independent heuristic review of ad copy, audience notes, regulated category, claims, and landing URL before a human approval step. This never approves or executes an ad write.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"report_ai_summary","platform":"ai","platformName":"MarkMate AI","title":"Summarize a report with AI","description":"Turn a validated marketing report into a concise executive summary with key findings, risks, and next actions. Report content is treated as data, never as instructions.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"starter"},{"id":"report_campaign_postmortem","platform":"report","platformName":"Report artifacts","title":"Generate a campaign postmortem","description":"Build a bounded evidence-led campaign postmortem from supplied metrics, outcomes, incidents, and follow-up actions. It computes transparent comparisons, preserves uncertainty, and never infers causality or authorizes a campaign change.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"report_export_csv","platform":"report","platformName":"Report artifacts","title":"Export tabular data as a CSV file","description":"Store columns and rows (typically templated from upstream operation outputs) as a CSV workspace file artifact that opens in Excel or Google Sheets. Returns the file id and download path.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":false,"minimumPlan":"free"},{"id":"report_export_pdf","platform":"report","platformName":"Report artifacts","title":"Export a branded PDF report","description":"Create a native branded PDF with a cover, contents, metrics, charts, tables, and page numbers. Returns a checksum-managed workspace file artifact.","requiresApproval":false,"sideEffect":"read","creditCost":3,"connectionRequired":false,"minimumPlan":"free"},{"id":"report_export_pptx","platform":"report","platformName":"Report artifacts","title":"Export a branded PowerPoint report","description":"Create a native widescreen PowerPoint deck with a branded cover, section slides, metrics, charts, tables, and page numbers. Returns a workspace file artifact.","requiresApproval":false,"sideEffect":"read","creditCost":3,"connectionRequired":false,"minimumPlan":"free"},{"id":"report_export_xlsx","platform":"report","platformName":"Report artifacts","title":"Export tabular data as an XLSX file","description":"Store columns and rows as a native Excel workbook with a styled header, frozen headings, formula-injection protection, and optional rasterized chart sheets. Returns the file id and download path.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"free"},{"id":"report_render","platform":"report","platformName":"Report artifacts","title":"Render an HTML report artifact","description":"Compose text, KPI metrics, inline charts, and tables (typically templated from upstream operation outputs) into a branded, self-contained HTML report stored as a workspace file. Supports white-label brand name/colour/logo. Returns the file id and download path; prints cleanly to PDF.","requiresApproval":false,"sideEffect":"read","creditCost":2,"connectionRequired":false,"minimumPlan":"free"},{"id":"seo_ai_visibility_score","platform":"seo","platformName":"SEO toolkit","title":"Score AI search visibility","description":"Aggregate supplied AI-search observations into a transparent internal GEO evidence score by engine, query coverage, mentions, citations, position, sentiment, and share of voice. This read-only reducer makes no provider calls.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"seo_cluster_keywords","platform":"seo","platformName":"SEO toolkit","title":"Cluster keywords into topics","description":"Group a keyword list into topic clusters by token similarity — deterministic, no external API. Template in GSC query rows or any keyword list; singletons come back as `unclustered`. Feed the clusters to ai_analyze_data or ai_structure for intent labeling or a content plan.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"seo_competitor_share_of_voice","platform":"seo","platformName":"SEO toolkit","title":"Measure competitor SERP share of voice","description":"Compare supplied organic SERP observations for an owned domain and declared competitors using transparent rank-weighted visibility. This read-only reducer makes no provider calls and does not claim traffic, revenue, causality, or ranking guarantees.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"seo_content_gap","platform":"seo","platformName":"SEO toolkit","title":"Content gap vs competitors","description":"Compare your page against competitor pages and list the terms/topics a majority of them cover that you don't (plus shared topics and your unique angles). Template web_fetch_page outputs in as `own` and `competitors`, then feed the result to ai_analyze_data for a prioritized content plan.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"seo_generate_jsonld","platform":"seo","platformName":"SEO toolkit","title":"Generate JSON-LD schema","description":"Create validated, schema.org JSON-LD for a marketing page or entity. The result is deterministic, HTTPS-only, and ready for a reviewed page or report; it never publishes or changes a website.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"util_date_range","platform":"util","platformName":"Workflow utilities","title":"Resolve a date range","description":"Turn a named range like last_7_days or last_month into concrete start/end dates (YYYY-MM-DD) in a timezone. Feed the result into analytics report operations.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"util_take","platform":"util","platformName":"Workflow utilities","title":"Take first/last N of a list","description":"Return the first (or last) N items of a list. Use to cap an upstream list before an expensive per-item operation.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"web_audit_page","platform":"web","platformName":"Open web","title":"Audit page SEO","description":"Run an on-page audit of a URL covering technical SEO (title, meta description, canonical, indexability, H1s, viewport, language, alt coverage, content length, Open Graph), CRO (call-to-action presence, form friction, script/page weight), and AI visibility (JSON-LD structured data, section-heading structure). Returns a 0–100 score with graded findings. Compose with web_get_sitemap + a loop to audit a whole site.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":false,"minimumPlan":"free"},{"id":"web_audit_summary","platform":"web","platformName":"Open web","title":"Summarize SEO audits","description":"Aggregate web_audit_page results into a site-level SEO report: site score, worst pages ranked, and the most common issues across pages (with an example URL each). Template a loop's results in as `audits`, then feed this output to ai_analyze_data for a prioritized, human-readable action plan.","requiresApproval":false,"sideEffect":"read","creditCost":0,"connectionRequired":false,"minimumPlan":"free"},{"id":"web_check_url_status","platform":"web","platformName":"Open web","title":"Check URL status","description":"Fetch a URL and report its HTTP status, whether it redirected, and the final URL. Useful for monitoring site and landing-page health.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":false,"minimumPlan":"free"},{"id":"web_fetch_page","platform":"web","platformName":"Open web","title":"Fetch page text","description":"Download a web page and return its title and readable text content (HTML stripped, capped). Use for reading landing pages, articles, or competitor copy.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":false,"minimumPlan":"free"},{"id":"web_get_sitemap","platform":"web","platformName":"Open web","title":"Get sitemap URLs","description":"Fetch a sitemap.xml (or a site root, trying /sitemap.xml) and list the URLs it contains. Reports whether it is a sitemap INDEX whose entries are child sitemaps.","requiresApproval":false,"sideEffect":"read","creditCost":1,"connectionRequired":false,"minimumPlan":"free"},{"id":"web_post_webhook","platform":"web","platformName":"Open web","title":"Send webhook (POST)","description":"POST a JSON payload to an external webhook URL (Zapier, Slack/Discord incoming webhooks, your own endpoint). This CHANGES an external system, so it always pauses for human approval before sending. Reports the delivery status without failing on non-2xx responses.","requiresApproval":true,"sideEffect":"create","creditCost":1,"connectionRequired":false,"minimumPlan":"starter"}]}