GenieOSdocs

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

This 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/json with 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.

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

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

On this page