API reference
REST API at api.genieos.pro/v1 — endpoints, payloads, conventions.
The GenieOS REST API lives at https://api.genieos.pro/v1. It
speaks JSON, requires bearer auth, and follows the same conventions on
every endpoint. The OpenAPI 3.1 description is published at
/v1/openapi.json and is
the source of truth for the SDKs.
curl https://api.genieos.pro/v1/openapi.jsonThis page is a hand-curated tour. For an interactive playground, see app.genieos.pro → Developers → Playground.
Conventions
- Base URL:
https://api.genieos.pro/v1. Everything below is relative to that. - Auth:
Authorization: Bearer gos_live_*. See Authentication. - JSON in, JSON out: request and response are
application/jsonwith snake_case fields. - Idempotency: every mutating call accepts
Idempotency-Key. See Idempotency. - Pagination: list endpoints return
{ data: [...], next_cursor: "..." }. Pass?cursor=...&limit=...to page (limit max 100, default 25). - Errors: common envelope. See Errors.
- Rate limits: per-key + per-workspace. See Rate limits.
Workspace
GET /v1/workspace
Read the current workspace.
{
"id": "ws_01JABC...",
"name": "Indigo Studios",
"plan": "pro",
"created_at": "2025-08-09T11:33:00Z",
"limits": {
"monthly_send_budget": 200000,
"rate_limit_per_minute": 1800
},
"default_from": { "email": "team@indigostudios.com", "name": "Indigo Studios" }
}Brand
GET /v1/brand — list brands in the workspace.
GET /v1/brand/{id} — read one brand (default resolves the workspace default).
Scope: brand:read.
Templates
GET /v1/templates — list drafts and published emails.
GET /v1/templates/{key} — read one template (body, subject, variables).
GET /v1/templates/{key}/schema — variables + schema contract only.
POST /v1/templates/{key}/render — preview against variables (no send).
POST /v1/templates/{key}/send — send (idempotent; see Transactional).
Create a blank draft
POST /v1/templates · scope templates:write
Mints a blank draft (same seed as New email in the designer). Prefer
/v1/templates/compose when you have a brief.
{
"key": "welcome", // optional — server mints untitled_* if omitted
"name": "Welcome", // optional — defaults to "Untitled email"
"category": "transactional", // marketing | transactional | system
"mode": "mjml", // mjml | html
"subject": "Welcome to {{plan}}",
"previewText": "You're in.",
"themeId": "thm_…" // optional brand theme
}Returns 201 with { data: { id, key, name, version, … } }.
Compose from a brief
POST /v1/templates/compose · scope templates:write
Genie writes a new email from a natural-language brief and persists it as
a draft. Charges compose-template credits. Hero image generation is
off by default (includeHeroImage: true opts in — placeholder band
only on this path).
{
"prompt": "Welcome email for new Glow subscribers. Warm, short, one CTA.",
"key": "welcome-glow", // optional
"name": "Welcome · Glow", // optional
"category": "marketing",
"mode": "mjml",
"themeId": "thm_…",
"starterShellId": "editorial-hero", // optional layout seed
"includeHeroImage": false,
"model": "claude-sonnet-4-5" // optional override
}Returns 201 with { data: { id, key, name, subject, … } }. Open the
draft in the designer to refine, then send via
POST /v1/templates/{key}/send.
Sends (transactional)
Canonical send today:
POST /v1/templates/{key}/send · scope templates:send
{
"to": "ada@example.com",
"variables": { "first_name": "Ada", "plan_name": "Pro" },
"from": { "email": "team@yourbrand.com", "name": "Your Brand" },
"replyTo": { "email": "support@yourbrand.com" },
"metadata": { "user_id": "usr_123" },
// sandbox keys only — default "delivered"
"simulationScenario": "delivered"
}Pass Idempotency-Key. Response is a SendPublic object (id, status,
connectorProvider, events[], optional metadata / simulation).
GET /v1/sends/{id} — same SendPublic projection (not raw Firestore).
GET /v1/sends — list (limit 1–100 default 25, opaque cursor).
At most one equality filter: templateKey | status | to | apiKeyId,
plus optional since / until (ISO createdAt window).
/v1/transactional/* aliases, batch, and schedule/cancel are Phase 1C —
not live yet. Use the template send + sends routes above.
Organic social
Draft, schedule, and publish organic posts to connected company
networks (LinkedIn page, Instagram, X, TikTok, …). Distinct from
Transactional Socials under /v1/social/transactional/* (reserved
event keys + variables).
Scopes: social:posts:read · social:posts:write · social:posts:publish
(publish also needs Glow+). Personal native profiles are never exposed.
GET /v1/social/networks — connected company accounts
(channelId, accountRef, transport).
POST /v1/social/networks/refresh — re-sync from Ayrshare + native
(same as the SPA Networks refresh). Requires social:posts:publish
(Glow+). Returns the refreshed company-only list + lastSyncedAt.
GET /v1/social/posts — list (?status=&channelId=&groupId=&limit=).
GET /v1/social/posts/{postId} — one post.
POST /v1/social/posts — create (idempotent). Two modes:
mode: "copy" (default) — agent supplies the caption:
{
"mode": "copy",
"channels": ["linkedin", "x"],
"caption": "We're announcing our Series A…",
"channelCaptions": {
"x": "Series A: building the OS for brand marketing."
},
"hashtags": ["SeriesA"],
"media": [
{ "kind": "image", "assetId": "asset_…", "alt": "Founders on stage" }
],
"linkUrl": "https://pages.genieos.pro/acme/series-a/",
"scheduleAt": "2026-07-14T15:00:00.000Z", // optional — schedules via the live publish stack
"publish": false, // true = publish now (confirm first)
"targetAccountRefs": { "x": "ayrshare:x" } // optional — from GET /networks
}mode: "compose" — Genie writes captions from a brief (charges
social-post-compose credits per channel; same stack as the SPA
composer):
{
"mode": "compose",
"brief": "Announce our Series A — calm, founder voice, link the landing page",
"channels": ["linkedin", "x"],
"composer": "sonnet", // or "opus"
"media": [{ "kind": "image", "assetId": "asset_…" }],
"linkUrl": "https://pages.genieos.pro/acme/series-a/",
"scheduleAt": "2026-07-14T15:00:00.000Z"
}PATCH /v1/social/posts/{postId} — edit draft / ready posts.
POST /v1/social/posts/{postId}/schedule — body { "scheduledAt": "ISO-8601" }
(Ayrshare scheduleDate or native X / LinkedIn queues — same path as the SPA).
POST /v1/social/posts/{postId}/publish — publish now.
DELETE /v1/social/posts/{postId} — delete a draft; ?fromProvider=true
also removes a live post (needs publish scope).
GET /v1/social/posts/{postId}/analytics — cached engagement summary.
Pass ?refresh=true to poll the provider now (same path as the SPA
Refresh metrics button).
Instagram / TikTok / YouTube / Pinterest require media; YouTube is
video-only. Native X with a URL in the caption or linkUrl costs
4 credits (social-post-publish-url).
Pages
GET /v1/pages — list landing pages.
GET /v1/pages/{idOrSlug} — read one page (metadata + section summary).
POST /v1/pages/{idOrSlug}/compose — compose the block tree from a brief
(persists by default). Scope pages:write.
POST /v1/pages/{idOrSlug}/publish — publish live. Scope pages:publish.
POST /v1/pages/{idOrSlug}/unpublish — take offline. Scope pages:publish.
Transactional SMS
GET /v1/messaging/transactional/kit — reserved SMS keys.
GET /v1/messaging/transactional/catalog — installed templates.
POST /v1/messaging/transactional/preview — preview copy + segments.
POST /v1/messaging/transactional — send. Scope
messaging.transactional.send.
GET /v1/messaging/transactional/deliveries — recent deliveries.
Transactional socials
Reserved event keys under /v1/social/transactional/* (distinct from
organic posts above):
GET /v1/social/transactional/catalog
GET /v1/social/transactional/templates
POST /v1/social/transactional/preview
POST /v1/social/transactional/events — trigger (preview / draft / publish)
GET /v1/social/transactional/events — recent runs
Sequences
Author sequences in the SPA; the API exposes discover, enrol, and run control:
GET /v1/sequences — list published sequences.
GET /v1/sequences/{keyOrId} — read one (trigger, status, node/edge counts).
GET /v1/sequences/{keyOrId}/runs — runs for that sequence.
POST /v1/sequences/{keyOrId}/enroll — enrol a contact
(alias: POST /v1/flows/{flowKey}/enroll).
{
"contact": { "email": "ada@example.com", "external_id": "usr_123" },
"variables": { "plan": "pro" }
}GET /v1/sequence-runs/{runId} — read one run.
POST /v1/sequence-runs/{runId}/cancel — cancel an active run
(cannot resume).
Scopes: sequences:read, sequences:trigger / flows:enroll.
Events
POST /v1/events — emit a custom event (can wake sequence edges and
fan out to webhooks). There is no list/query endpoint yet — use the
audit log or webhooks to observe outcomes.
{
"type": "order.completed",
"contact": { "external_id": "usr_123" },
"metadata": { "order_id": "ord_8a72c0" }
}Marketing OS
GET /v1/marketing/strategy — live Marketing Strategy
(?detail=summary|full).
PATCH /v1/marketing/strategy — sparse-merge patch.
GET /v1/marketing/icps / GET /v1/marketing/icps/{icpId} — ICPs.
POST /v1/marketing/icps / PATCH /v1/marketing/icps/{icpId}.
GET /v1/marketing/creation-defaults /
PATCH /v1/marketing/creation-defaults.
Scopes: marketing:read, marketing:write.
Creations (campaigns)
GET /v1/creations — list campaigns.
GET /v1/creations/{creationId} — read one (?detail=summary|full).
POST /v1/creations — spawn from a brief.
POST /v1/creations/{creationId}/approve-strategy — approve strategy
and start building planned channels.
Scopes: campaigns:read, campaigns:write.
Lists
GET /v1/lists / GET /v1/lists/{listId}
POST /v1/lists — create.
PATCH /v1/lists/{listId} / DELETE /v1/lists/{listId}
POST /v1/lists/{listId}/members — add by contactIds.
POST /v1/lists/{listId}/members/remove
Scopes: lists:read, lists:write.
Approvals
GET /v1/approvals/policies
PUT /v1/approvals/policies/{surfaceKind} — upsert policy.
GET /v1/approvals/pending
POST /v1/approvals/pending/{requestId}/decide — approve / request
changes / reject.
Scopes: approvals:read, approvals:write.
Short links
GET /v1/links — list tracked short links (newest first).
Query: includeArchived=true, limit=1..500 (default 100).
Scope: links:read.
GET /v1/links/{linkId} — read one short link (full detail including
password / schedule / route-rule metadata; the password secret itself
is never returned). Scope: links:read.
GET /v1/links/utm-suggestions — frequency-ranked prior UTM values
from existing short links (same source as the Links designer
autocomplete). Query: field=source|medium|campaign|content|term,
includeCounts=false. Scope: links:read.
Prefer calling utm-suggestions before create so agents / scripts reuse
existing utm_source spellings.
GET /v1/links/analytics — click analytics cards (workspace-wide or
scoped with linkId). Query: cardKey, linkId, days,
forceRefresh. Scope: links:read. Tier gates match the SPA Analyze
surface (Glow+ for the rich cards).
POST /v1/links — create a tracked short link (1 credit).
Scope: links:write.
{
"destinationUrl": "https://acme.com/sale",
"slug": "summer", // optional — omit to auto-generate
"label": "Summer sale", // optional library nickname
"campaignId": "cmp_…", // optional
"tags": ["launch"], // optional, max 20
"domain": "gogen.ie", // optional — Spark+ custom hosts when active
"password": "gate-phrase", // optional — every tier
"expiresAt": "2026-12-31T23:59:00.000Z", // optional
"scheduledGoLiveAt": "2026-09-01T09:00:00.000Z", // optional
"routeRules": [ // optional — geo / device / platform
{
"match": { "kind": "city", "values": ["london"] },
"destinationUrl": "https://acme.com/sale-uk"
}
],
"utm": { // optional — stamped on redirect
"source": "newsletter", // → utm_source
"medium": "email", // → utm_medium
"campaign": "summer-2026", // → utm_campaign
"content": "hero-cta", // → utm_content
"term": "optional-keyword" // → utm_term
}
}PATCH /v1/links/{linkId} — update destination, label, tags, UTM,
password (password / clearPassword), schedule
(expiresAt / clearExpiresAt, scheduledGoLiveAt /
clearScheduledGoLiveAt), and routeRules / clearRouteRules.
Scope: links:write.
Response 201 (create) / 200 (get/patch) includes redirectUrl,
linkId, slug, domain, and click counters. MCP:
list_utm_suggestions,
list_short_links,
get_short_link,
create_short_link,
update_short_link,
read_link_analytics.
QR designs
POST /v1/qr — create a QR design (1 credit). Body encodes is one of
{ kind: "shortLink", linkId }, { kind: "static", payload },
{ kind: "wifi", … }, or { kind: "vcard", … }. Optional label,
tags, style, frame. Scope: links:write. Does not return
image bytes — call render next.
PATCH /v1/qr/{qrId} — update style / frame / label / tags / encodes.
Scope: links:write.
POST /v1/qr/{qrId}/render — render bytes. Scope: links:read
(saveToAssets: true also needs links:write).
{
"format": "pdf", // svg | png | webp | png-print | pdf
"saveToAssets": true // default false for preview; true on download
}Credit costs: SVG / PNG / WebP free; png-print 2 credits; pdf
(print with bleed) 5 credits — every tier. When saveToAssets is
true the export lands in the workspace DAM (Images, tags qr /
qr-code). MCP: create_qr_design,
update_qr_design,
render_qr_design.
Keys & connectors
GET /v1/keys / GET /v1/keys/{id} — API keys for this workspace
(secret never returned after create).
GET /v1/connectors/catalog — available connector providers.
GET /v1/connectors — installed connectors.
Scope: connectors:read for connector reads.
Webhooks
GET /v1/webhooks
GET /v1/webhooks/{id}
POST /v1/webhooks
{
"url": "https://yourapp.com/genieos",
"events": ["send.delivered", "send.bounced", "sequence_run.enrolled"],
"description": "Production"
}Scope: webhooks:manage. Returns the subscription and the signing
secret (shown once at create; later GETs mask it).
Headers on delivery: X-GenieOS-Event, X-GenieOS-Delivery,
X-GenieOS-Signature, X-GenieOS-Timestamp. See Webhooks.
PATCH /v1/webhooks/{id} — update url / events / description.
DELETE /v1/webhooks/{id} — soft-disable / remove the subscription.
POST /v1/webhooks/test — fire a test delivery.
Deliveries list, replay, and secret rotation are Phase 1C.
Audit
GET /v1/audit?since=...&until=...&actor=...&action=...&cursor=...
Last 90 days of writes. Each entry:
{
"id": "aud_01JABC...",
"ts": "2026-04-20T11:34:00.123Z",
"actor": { "kind": "api", "key_id": "key_01J...", "ip": "203.0.113.4" },
"action": "transactional.send",
"target": { "kind": "message", "id": "msg_01J..." },
"request_id": "req_01J...",
"metadata": { "template": "welcome", "to_domain": "example.com" }
}OpenAPI 3.1
The full machine-readable spec is at
/v1/openapi.json. Use it
to generate clients, validate fixtures, or scaffold tests:
curl https://api.genieos.pro/v1/openapi.json > openapi.json
npx openapi-typescript openapi.json -o ./src/api.types.tsIf you spot a discrepancy between this page and openapi.json, the
OpenAPI document wins; please open an issue at
github.com/GenieOS-0.
Want a richer playground?
We render an interactive playground inside the dashboard at app.genieos.pro → Developers → Playground — backed by the same OpenAPI document, with sandbox mode pre-wired so you can experiment without burning sends.