Billing & BYOC rendering — pay per operation, bring your own content

Render modes

Set renderMode at campaign create (PLATFORM | BYOC). The mode locks as soon as any recipient passes SCREENED. BYOC is incompatible with canvas multi-step flows (v1). In BYOC the platform never spends its AI on your emails: the render cron skips the campaign, prepare only audits content you already submitted, and recipients/rerender just resets rows to SCREENED awaiting your resubmission.

BYOC intake

Submission itself is free. Limits: ≤100 items per call, subject ≤998 chars, bodyHtml ≤256 KB, bodyText ≤64 KB. Content must be FINISHED text — unresolved {{placeholders}} are rejected per-item at intake. Rows must be in stage SCREENED.

curl -s -X POST "https://api.live-direct-marketing.online/api/campaigns/$CAMPAIGN/recipients/content" \
  -H "Authorization: Bearer $LDM_KEY" -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "recipientId": "cmx...", "subject": "Hi Anna", "bodyHtml": "<p>Finished personalised email</p>" }
    ]
  }'
# → { "accepted": 1, "rejected": [], "awaitingAudit": 1 }

Mandatory paid audit (no bypass, any mode)

  • Every email — platform-rendered or BYOC — passes the audit gate before it can be sent. The audit is the ONLY writer of the READY stage; the send point additionally verifies a content hash fixed at audit time.
  • Audit iterations are billed per email: passed = audit, failed = audit_failed (50% of the audit price).
  • Rejections return COARSE categories only (auditCategories: spam_signals, quality_low, broken_personalization, link_policy, language_mix, formatting, policy_violation, other). No detailed rationale by design — the auditor is not an oracle to probe.
  • Limits: ≤5 paid audit iterations per recipient (then CREATIVE_FAILED); a campaign that burns more than 5× its recipient count in audit iterations is set to AUDIT_BLOCKED — manual moderation only.

Pricing & balance

One account balance (USD). Default prices per email-operation: platform_render $0.002, audit $0.001, audit_failed $0.0005, send $0.0005 (per-tenant overrides possible). GET /api/billing/balance returns balance, the pricing table and topUpUrl; GET /api/billing/ledger returns the itemised charge journal, including background cron charges.

// Every paid response (MCP/A2A clients) carries:
"_billing": { "operation": "audit", "cost": 0.05, "currency": "USD", "balance_after": 12.35 }

// 402 on insufficient funds — nothing is partially executed:
{ "status": 402, "code": "insufficient_balance",
  "required": 0.098, "balance": 0.01, "currency": "USD",
  "topUpUrl": "https://app.live-direct-marketing.online/crm/settings?tab=ai" }

Background conveyor (cron render/audit/send) pauses the campaign with circuitBreaker.reason = insufficient_balance when the balance runs out — top up in the dashboard, then POST /api/campaigns/:id/resume. There is NO top-up via API or MCP.