← Docs

Concepts · The closed loop

4 calls. 1 approval. 1 launch.

Most "AI marketing" tools stop at the brief. ChiefLab closes the loop: plan → approve → publish/send → measure → recommend the next iteration. Every step is approval-gated, persisted, and reproducible. Audit log proves every step fires live in production.

The shape

1. agent calls chiefmo_launch_product       → launchPack + signed reviewUrl + approval-gated publishActions
2. user clicks reviewUrl, approves          → action.status: "awaiting_approval" → "approved"
3. agent calls chiefmo_publish_approved_post  → real Zernio postId
   agent calls chiefmo_send_approved_email   → real Resend messageId
4. 24h later, Vercel cron auto-fires        → chiefmo_post_launch_review pulls metrics + recommends next iteration
                                              → POSTs to webhookUrl if you set one

Step 1 — chiefmo_launch_product

The wedge tool. Call it the moment the user has shipped something and asks for help with launch / users / marketing / Product Hunt / launch graphics / launch email / "what to do after launch."

chiefmo_launch_product({
  productUrl:  "https://yoursite.com",
  goal:        "Get our first 100 users",
  channels:    ["linkedin", "x", "product_hunt", "email", "landing_hero"],
  tenantId:    "acme-co",          // optional: load stored brand context
  brand:       { name, audience, voice, pillars },  // OR pass inline
  imagesNeeded: 1,                 // 0 to skip; default 1
  scheduleFor: "2026-06-01T09:00Z", // optional ISO 8601
  webhookUrl:  "https://your-app.com/hooks/chieflab",  // optional
  idempotencyKey: "launch-yoursite-2026-06"  // CRITICAL on retries
})

Returns:

{
  "launchId":      "<UUID>",
  "intent":        "launch_product",
  "run":           { ... },
  "launchPack": {
    "positioning": { assetId, body },
    "launchAngle": { assetId, body },
    "channels": {
      "linkedin":  { assetId, body, publishViaConnector: "zernio" },
      "x":         { assetId, body, publishViaConnector: "zernio" },
      "email":     { assetId, body, publishViaConnector: "resend" },
      "landing_hero": { assetId, body, publishViaConnector: "site_builder" }
    }
  },
  "generatedImages": [{ id, usedFor, status, generator: "gemini-2.5-flash-image" }],
  "publishActions": [
    {
      "id":            "<UUID>",          // PERSISTED to actionStore — pass to publish
      "channel":       "linkedin",
      "connector":     "zernio",
      "executorTool":  "chiefmo_publish_approved_post",  // ← what to call after approval
      "status":        "awaiting_approval",
      "preflight": {
        "severity":    "medium",
        "warnings":    ["Publishing to linkedin via zernio — visible to your audience..."],
        "recommendations": [...],
        "gates":       ["Requires explicit human approval before execution."]
      }
    }
  ],
  "reviewUrl":     "https://chieflab.io/runs/<id>?token=...",  // HMAC, 7-day TTL
  "trackingPlan": { metricsTrackedAfter: "24h", nextRunTool: "chiefmo_measure_launch_results" }
}

Step 2 — user approves on the reviewUrl

The reviewUrl is HMAC-signed (7-day TTL). Open it in a browser. No login. The page renders the briefs + graphics + per-action preflight. User clicks Approve / Reject per action. Rejected actions can store feedback that goes into per-tenant memory for the next launch.

Programmatic alternative (when the user is the agent's own builder, not an end-user): chiefmo_approve_action({ id }). This is the same approval transition the reviewUrl page does.

Step 3a — chiefmo_publish_approved_post (Zernio)

Strict approval gate. Refuses with { reason: "requires_approval", reviewUrl } if status is anything but "approved".

chiefmo_publish_approved_post({
  actionId:    "<UUID from launchPack.publishActions>",
  content:     "<final post text — your LLM rendered this from the brief>",
  platforms:   [{ platform: "linkedin", accountId: "zer_acc_..." }],
  mediaUrls:   ["data:image/png;base64,..."],   // optional — ChiefLab uploads to Zernio CDN
  scheduleAt:  "2026-06-01T09:00:00Z"           // optional — schedule instead of fire-now
})

// → { executed: true, zernioPostId, platforms, scheduled, results }
// → action.status flips "approved" → "executed", metadata.zernioPostId persisted

Get the Zernio accountIds with chieflab_list_publish_accounts({}). Connect new accounts with chieflab_connect_publish_account({ platform }) — Zernio handles the per-platform OAuth dance.

Step 3b — chiefmo_send_approved_email (Resend)

chiefmo_send_approved_email({
  actionId:    "<UUID — required for the gate>",
  from:        "Brand <[email protected]>",  // must be a verified Resend domain
  to:          "[email protected]",
  subject:     "Launch day",
  html:        "<h1>...</h1>",
  text:        "Plain-text fallback for inboxing"
})

// → { sent: true, messageId, to, sentAt }
// → action.status flips "approved" → "executed", metadata.resendMessageId persisted

Verify your sender domains with chieflab_list_email_senders({}). Set the workspace-scoped Resend key with chieflab_set_resend_key({ apiKey }) if not already configured.

Step 4 — chiefmo_measure_launch_results (24h+)

chiefmo_measure_launch_results({
  runId:        "<launchId from step 1>",
  lookbackDays: 7,                // 1-90, default 7
  outputMode:   "context"          // "context" returns metrics + brief; "full" renders the recommendation
})

// → {
//     totals:        { posts, likes, comments, views, shares },
//     perAccount:    [{ accountId, profile, analytics, topPosts }],
//     ga4:           { connected, metrics, period, rows },
//     searchConsole: { connected, metrics, topQueries },
//     brief:         "<recommendation brief — your LLM renders into customer-facing summary>"
//   }

The cron auto-fire

You don't have to remember to call measure_launch_results manually. When step 3 fires, ChiefLab records a launch_followup row scheduled for publishedAt + 24h (configurable via CHIEFLAB_REVIEW_DELAY_HOURS). The Vercel cron at /api/cron/tick (daily, midnight UTC) scans for due rows, fires chiefmo_post_launch_review for each, and POSTs the result to your webhookUrl if you set one.

The webhook payload:

POST <your-webhookUrl>
X-ChiefLab-Signature: sha256=<hex>
Content-Type: application/json

{
  "event":        "launch.review_ready",
  "operator":     "chiefmo",
  "tool":         "chiefmo_post_launch_review",
  "launchId":     "<UUID>",
  "actionId":     "<UUID>",
  "workspaceId":  "<your-workspace>",
  "result":       { ...same shape as the tool returns... },
  "deliveredAt":  "2026-05-05T09:00:00.000Z"
}

Verify the signature using your workspace's webhook secret (set via CHIEFLAB_WEBHOOK_SECRET).

Hard guarantees

  • No publish or send tool ever fires without status: "approved" on the action. The gate checks the actionStore record, not just the input. Calling without an approved actionId returns { reason: "requires_approval", reviewUrl } — never silently fires.
  • The reviewUrl is HMAC-signed. 7-day TTL. Opens with no login. Tampering rejects. Use buildReviewUrl() and verifyRunToken() if you want to re-issue.
  • Idempotency keys cache results for 24h. Retrying a launch with the same idempotencyKey returns the original launchPack — won't double-create actions, won't double-fire publishes.
  • Runs persist across cold starts. Supabase-backed actionStore + assetStore + runStore. Cold-start on Vercel doesn't lose state.
  • Per-tenant isolation. Brand context, voice samples, memory, runs all scoped by tenantId. Multi-tenant agent products can serve thousands of end-users from one workspace.
  • Webhook delivery retries with exponential backoff. 1m → 5m → 30m → 2h → 12h. Signature on every attempt.

What's verified live

Every step above has been observed firing end-to-end against production with real run IDs, action IDs, and Zernio postIds. The wedge claim isn't aspirational.

What's next

  • MCP reference — JSON-RPC contract, all production tools, error codes
  • Quickstart — install + first launch in 60 seconds
  • For AI agents — instruction text, hard safety rules
  • Examples — real launch pack JSON envelopes from production