Developer docs
Marketing API
Products, strategy, metrics, calendar, contents, approvals, reports and the AI marketer — every screen of the app as REST endpoints.
The marketing API follows the same flow as the app: product → strategy → calendar → contents → approvals → reports. All routes are under /v1, accept an API key or an OAuth token, and are scoped by workspaceId (id or slug) in the body / query or by the resource id. Lists return either an array or { items, nextCursor } (noted as paged).
Products
| Endpoint | Body / query → result |
|---|---|
GET / | ProductDto[] |
POST / | { workspaceId, name, kind?, url?, storeUrls?, summary?, description?, goals?, monthlyBudgetKrw?, strategyTemplateId?, channelKinds? } → ProductDetailDto. strategyTemplateId applies the template right away |
GET / | ProductDetailDto — brand kit, audiences, latest strategy doc per section, funnel, channels |
PATCH / | Partial update → ProductDetailDto |
DELETE / | Archive → { ok } |
POST / | { url } → ProductScrapeResult — Open Graph, App Store / Play listing, GitHub repo + README; null fields on failure |
PUT / | { tone?, forbidden?, colors?, logoAssetId?, examples?, fonts? } → BrandKitDto |
POST / · DELETE / | Product gallery (ProductDetailDto.images, max 24) — { assetId } of an image you uploaded via POST /. Set the icon with PATCH … { iconAssetId }, the logo with brand-kit logoAssetId |
GET / POST / · PATCH / DELETE / | Target audiences |
GET / POST / · PATCH / DELETE / | Notes (decisions, findings) |
kind is one of APP WEB_SERVICE COMMERCE LOCAL_STORE CREATOR B2B EVENT OTHER; goals[] of INSTALLS SIGNUPS PURCHASES VISITS LEADS FOLLOWERS AWARENESS.
curl https://api.poremu.ai/v1/products \
-H "Authorization: Bearer $POREMU_API_KEY" -H "Content-Type: application/json" \
-d '{"workspaceId":"my-workspace","name":"Lumo","kind":"APP","url":"https://lumo.app","goals":["INSTALLS"]}'Strategy
| Endpoint | Body / query → result |
|---|---|
GET / | { recommended: StrategyTemplate[], all: StrategyTemplate[] } — 100 templates, scored for the given product shape |
GET / | StrategyTemplate with stages, triggers, automations |
POST / | { templateId, from?, replaceCalendar? } → ProductDetailDto. Creates the funnel, strategy-doc drafts, manual channels, calendar drafts and content drafts |
POST / | { urls?, notes?, overwriteHuman?, applyTemplate?, engine?, model? } → AgentRunDto (QUEUED, runs in the background). Writes the seven sections, audiences and brand tone; sections a person edited are kept unless overwriteHuman |
GET / | ProductAnalysisDto — sources read, facts, summary, when it last ran |
GET / | StrategyDocDto[] (latest per section by default) |
POST / | { section, title, body, data? } → new version. data is validated per section (positioning messaging audience channel_mix kpi budget plan) |
The analyser needs an LLM: the workspace engine must be CREDITS or BYO_KEY. A workspace in BYO_MCP mode gets 400 VALIDATION_ERROR — there the client does the thinking with analysis_brief → save_strategy over MCP.
Channels, connections & metrics
| Endpoint | Body / query → result |
|---|---|
GET / POST / | ChannelDto[] / { kind, name?, role?, connectionId?, externalIds?, budgetCapKrwDaily?, note? }. With connectionId + externalIds the channel is CONNECTED and the last 30 days sync in the background |
PATCH / DELETE / · POST / | Update, remove, re-sync the last 7 days (CONNECTOR_SYNC 1 credit; manual channels → 400) |
GET / | ConnectorDto[] — Meta Ads, Google Ads, GA4, Google Sheets, Naver Search Ads… with available |
GET / | Browser session + admin: redirects to Meta / Google consent |
GET / POST / · DELETE / · POST / · GET / | Form connectors (API keys), test, list ad accounts / properties |
GET / | MetricsSummaryDto — totals for the period and the previous period of the same length; ctr cpc cpa cpi roas recomputed from sums (ctr is a ratio: 0.018 = 1.8 %) |
POST / · POST / | { productId, channelId, date, metrics, scopeType?, scopeId?, scopeName? } upsert / { productId, rows[] } → { count } |
GET / | FunnelDto — stages, rates, bottleneck |
GET / POST / · PATCH / DELETE / | Campaigns |
Connected channels sync daily at 04:00 KST (1 credit per channel to whoever created the connection). Token problems set the connection to NEEDS_REAUTH and fire the connection.needs_reauth webhook.
Calendar
| Endpoint | Body / query → result |
|---|---|
GET / | CalendarEventDto[]. Recurring events are expanded server-side; an expanded instance's id is {id}:{date} |
POST / | { workspaceId, title, startsAt, productId?, kind?, note?, endsAt?, allDay?, channelId?, campaignId?, contentItemId?, assigneeIds?, status?, draft?, color?, rrule? } |
PATCH / DELETE / · POST / | Update, delete, turn a draft into a confirmed event |
POST / | { workspaceId, productId, from?, to?, templateId? } → CalendarEventDto[] drafts — one week per template stage, routines as rrule; from defaults to next Monday 00:00 KST |
kind is CAMPAIGN PUBLISH LAUNCH REPORT TASK MILESTONE REMINDER; status is planned doing done canceled.
Contents
| Endpoint | Body / query → result |
|---|---|
GET / | paged ContentItemDto |
POST / | { title, format?, spec?, body?, status?, campaignId?, channelId?, assetIds?, generationIds?, scheduledAt?, parentId? }. Text passes the safety check; warnings[] flags superlatives, guarantees and medical claims |
GET / PATCH / DELETE / | |
POST / | { scheduledAt, channelId? } → content + a PUBLISH calendar event |
format: COPY IMAGE VIDEO CAROUSEL POST ARTICLE EMAIL SCRIPT LANDING. status moves IDEA → DRAFT → REVIEW → APPROVED → SCHEDULED → PUBLISHED (or REJECTED / ENDED); publishing to a channel is an approval (PUBLISH_POST). To generate the media itself use Images / Videos and pass generationIds.
Approvals, proposals & rules
| Endpoint | Body / query → result |
|---|---|
GET / | paged ApprovalDto (status accepts a comma list, e.g. APPROVED,REJECTED) |
POST / | { workspaceId, action, summary, productId?, channelId?, payload?, evidence?, expiresInHours? } → ApprovalDto (PENDING; expires after 72 h by default) |
GET / · POST / | { decision: "approve" | "reject", note? } (admin). Approve → the executor runs (EXECUTED) or fails with a reason |
GET / POST / · PATCH / DELETE / | { workspaceId, action, productId?, channelKind?, limits?, enabled? } — auto-approve small changes within limits |
GET / · GET / | paged ProposalDto — suggestions the AI left for review |
POST / | { decision: "accept" | "dismiss" }. Accepting converts suggested actions into approvals, calendar fills or content drafts |
GET / | Badge counts |
action: BUDGET_CHANGE BID_CHANGE PAUSE_RESUME PUBLISH_POST SEND_EMAIL SEND_MESSAGE CREATE_CAMPAIGN UPDATE_CREATIVE WEBHOOK_OUT CUSTOM. Executors exist for BUDGET_CHANGE ({ level: "campaign" | "adset", externalId, dailyBudgetKrw }) and PAUSE_RESUME ({ level, externalId, status: "PAUSED" | "ACTIVE" }) on Meta Ads, Google Ads and Naver Search Ads (2 credits per execution), and for WEBHOOK_OUT. evidence is { dataSnapshot?, rule?, expectedEffect?, risks?, reversible?, notes? } — fill it so the approver can decide at a glance.
curl https://api.poremu.ai/v1/approvals \
-H "Authorization: Bearer $POREMU_API_KEY" -H "Content-Type: application/json" \
-d '{
"workspaceId": "my-workspace",
"productId": "PRODUCT_ID",
"channelId": "CHANNEL_ID",
"action": "BUDGET_CHANGE",
"summary": "Cut Meta daily budget ₩120,000 → ₩96,000 (CPA ₩11,400 > target ₩9,000)",
"payload": { "level": "campaign", "externalId": "2384…", "dailyBudgetKrw": 96000 },
"evidence": { "expectedEffect": { "metric": "cpa", "from": 11400, "to": 9500 }, "reversible": true }
}'Approval lifecycle events (approval.requested, approval.decided, approval.executed) are delivered as webhooks.
Reports
| Endpoint | Body / query → result |
|---|---|
GET / · GET / · POST / | paged ReportDto |
POST / | { workspaceId, periodStart, periodEnd, productId?, title? } → ReportDto — numbers, channels and trend sections, an LLM summary (gpt-5.4-mini), a share link. 5 platform credits |
GET / | Public share view |
GET / POST / · PATCH / DELETE / | { workspaceId, name, cron, productId?, kind?, timezone?, deliver?, template?, enabled? }. cron is 5 fields or a preset such as weekly_mon_09; delivery in-app, by webhook (report.created) or by email to workspace members |
For anything richer than a scheduled report — conditions, AI runs, Slack, email with templated text — build an automation.
AI marketer (agent)
| Endpoint | Body / query → result |
|---|---|
POST / | { workspaceId, message, productId?, sessionId?, engine?, model? } → SSE when Accept: text/event-stream: session {id} · delta {text} · tool {step} · approval {approval} · done {run, message, balance} · error {code,message} |
POST / | { workspaceId, goal, productId?, engine?, model?, playbook? } → AgentRunDto (background; up to 12 tool steps / 4 minutes) |
GET / · GET / · POST / | Runs with their steps[], result and settled credits |
GET / | The tool catalog the agent (and MCP) can call: { name, description, kind, action?, platformCredits }[] |
GET / PATCH / | AiEngineSettingDto — CREDITS (Poremu models), BYO_KEY (your OpenAI / Gemini / Anthropic / xAI key, encrypted) or BYO_MCP (an assistant connected over MCP does the reasoning) |
GET / · GET / | What the AI spent, per tool |
GET / | HomeOverviewDto — the dashboard in one call |
The agent uses the same tools listed on the MCP page. In BYO_MCP mode /agent/chat answers 400 VALIDATION_ERROR — use the assistant you connected instead.
const res = await fetch('https://api.poremu.ai/v1/agent/chat', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.POREMU_API_KEY}`, 'Content-Type': 'application/json', Accept: 'text/event-stream' },
body: JSON.stringify({ workspaceId: 'my-workspace', productId, message: 'Which channel should get more budget next week, and why?' }),
});
for await (const chunk of res.body) process.stdout.write(new TextDecoder().decode(chunk)); // event: delta / tool / approval / doneInbound hooks
To push your own events — orders, sign-ups, metric rows — into a product, create an inbound hook (POST /) and POST to its token URL. Details in Webhooks → Inbound hooks.
Errors & limits
The usual error envelope applies. Marketing-specific cases:
| Code | When |
|---|---|
403 FORBIDDEN | Not a member of the workspace, or the action needs OWNER / ADMIN (deciding approvals, connections, rules, hooks) |
400 VALIDATION_ERROR with details.issues | Invalid strategy data, an automation / calendar body that fails validation, or an engine mode that cannot run this route |
402 INSUFFICIENT_CREDITS | The run, report or generation could not be reserved |
409 CONFLICT | A run is already in flight for this resource |
429 RATE_LIMITED | 60 requests / minute per key; daily run limits on automations |