# BrandBeacon > AI marketing assistant for small businesses: extract your brand voice, generate on-brand social content, and schedule across platforms — without a marketing team. BrandBeacon helps small businesses and solo founders run consistent social media marketing with AI. It learns your brand voice from your website and existing content, generates platform-optimized posts that sound like you, and automates scheduling and publishing — enterprise-grade marketing without the budget or headcount. ## Who it's for Small business owners, solo founders, and small marketing teams that need a consistent social media presence but don't have time to write and schedule posts every day. ## What it does - Brand voice extraction from your website (personality, tone, language patterns) into a reusable brand profile - AI content generation optimized per platform - Multi-platform scheduling and automated publishing from a content queue - On-brand AI image generation - Engagement analytics Publishes to Facebook, Instagram, X/Twitter, LinkedIn, and Google Business. ## Pricing - **Free** — get started at no cost - **Pro — $99/month** (or $89.10/mo billed quarterly, $79.20/mo billed annually): unlimited posts and the full feature set Start free at https://brandbeacon.dev ## Developer API BrandBeacon exposes a REST API at `/api/v1/` for programmatic access. Authenticate with a Bearer token (API key prefixed `bb_live_`). - [API Documentation](/llms-full.txt): Full API reference with endpoints, request/response schemas, and examples - [OpenAPI Specification](/openapi.json): Machine-readable API specification ### Authentication API keys are created in the BrandBeacon dashboard under Settings > API Keys. Keys use the format `bb_live_<40 characters>`. Pass the key as a Bearer token: ``` Authorization: Bearer bb_live_your_key_here ``` ### Rate Limits Rate limits are set per API key — the default is 60 requests/minute and 1,000 requests/day, and can be raised on request. ### Scopes API keys can be scoped to limit access: - `read` - Read brands, brand voice, posts - `write` - Create and update posts - `generate` - Generate AI content - `post` - Create posts via API - `schedule` - Schedule posts for publishing - `ad_creatives` - Generate, render, list, and export ad creative sets - `admin` - Full access (all scopes) ## v1 Ad Creatives API Generate on-brand ad copy and images, trigger async video renders, and export creative sets for Meta, Google, and other platforms. ### Authentication & Scopes All requests require `Authorization: Bearer `. Write endpoints (generate, render) require the `ad_creatives` scope. Read endpoints (list, get, export, job status) accept either `read` or `ad_creatives`. The `admin` scope satisfies any requirement. Keys may be brand-restricted (`brand_ids` allow-list). A request targeting a brand outside that list returns `403 FORBIDDEN`. ### Idempotency POST endpoints accept an optional `Idempotency-Key: ` header. Supplying the same key twice returns the cached first response and an HTTP `409 CONFLICT` instead of executing the operation again. ### Endpoints #### POST /api/v1/ad-creatives/generate Generate an ad creative set (copy + images) for a brand. Video generation is asynchronous — use the render endpoint instead for `kinds: ['video']`. **Required scope:** `ad_creatives` **Request fields:** - `brand_id` (string, required) — Brand UUID - `platforms` (string[], required) — e.g. `["meta", "instagram", "google"]` - `formats` (string[], required) — e.g. `["1:1", "9:16", "16:9"]` - `kinds` (string[], optional) — `["copy", "image"]`; defaults to both. `"video"` is rejected here — use the render endpoint. - `offer` (string, optional) — Offer or product description for copy generation - `goal` (string, optional) — Campaign goal (e.g. "increase trial signups") - `name` (string, optional) — Human-readable set name - `variants_per_platform` (number, optional) — A/B variants per platform (default 1, clamped to plan cap) **Response:** `{ set: AdCreativeSet, variants: AdCreativeVariant[] }` **Credit costs:** copy = 1 credit per variant, image = 1 credit per variant #### GET /api/v1/ad-creatives List ad creative sets for the authenticated identity. **Required scope:** `read` or `ad_creatives` **Query params:** - `limit` (number, default 20, max 100) - `offset` (number, default 0) - `status` (string, optional) — `pending` | `generating` | `completed` | `partial` | `failed` - `brand_id` (string, optional) — Filter to one brand (must be in key's allow-list) **Response:** `{ sets: AdCreativeSet[], pagination: { limit, offset, total } }` #### GET /api/v1/ad-creatives/{setId} Fetch a single ad creative set and all its variants. **Required scope:** `read` or `ad_creatives` **Response:** `{ set: AdCreativeSet, variants: AdCreativeVariant[] }` A set not owned by the caller or outside the key's brand allow-list returns `404 NOT_FOUND` (no existence leak). #### GET /api/v1/ad-creatives/{setId}/export #### POST /api/v1/ad-creatives/{setId}/export Build and download an export bundle. Returns a short-lived signed URL (15 min TTL) to the artifact rather than streaming bytes inline. **Required scope:** `read` or `ad_creatives` **Query params:** - `format` (string, default `zip`) — `zip` | `meta` | `google` - `only_selected` (boolean, default `false`) — Export only variants flagged `selected: true` **Response:** ``` { download_url, expires_at, format, variant_count, only_selected, content_type, filename, byte_size } ``` **Error:** `EXPORT_FAILED` (500) if storage upload or URL signing fails. #### POST /api/v1/ad-creatives/{setId}/render Enqueue an async final-render job (video concat + music + captions). Returns `202` immediately with a `job_id`; the caller polls the jobs endpoint. **Required scope:** `ad_creatives` **Request fields:** - `music_mood` (string, optional) — Music mood key for the render (e.g. `"upbeat"`, `"calm"`) - `only_selected` (boolean, optional) — Render only variants flagged `selected: true` - `callback_url` (string, optional) — Public HTTPS URL to receive a webhook POST when the render settles. BrandBeacon sends an HMAC-signed `POST` with `X-BrandBeacon-Signature: sha256=` on `render.completed` (includes `asset_url`) or `render.failed` (includes `error`). Webhooks are live — every settled job with a `callback_url` fires one. **Response (202):** `{ job_id, status: "queued", set_id }` **Credit cost:** `final_render` = 10 credits, reserved at enqueue; refunded in full if the render fails. **Video tier costs (charged at render):** - minimax — 5 credits - kling / runway — 10 credits - veo3 — 15 credits #### GET /api/v1/ad-creatives/jobs/{jobId} Poll the status of an async render job. **Required scope:** `read` or `ad_creatives` **Response:** ``` { job_id, status: "queued"|"processing"|"completed"|"failed", set_id, asset_url?, error? } ``` - `queued` / `processing` — still running; poll again. - `completed` — `asset_url` contains the final mp4 public URL. - `failed` — `error` describes the failure; reserved credits were fully refunded. A job not owned by the caller or an unknown/expired job ID returns `404 NOT_FOUND`. ### Async Job Lifecycle ``` POST /render → 202 { job_id, status: "queued" } │ ▼ (Vercel Cron, ~1 min) status: "processing" │ ┌────────┴────────┐ ▼ ▼ status: "completed" status: "failed" asset_url: error: credits refunded ``` Poll `GET /jobs/{jobId}` every 15–30 seconds until terminal state. ### Error Codes | Code | HTTP | Description | |------|------|-------------| | `UNAUTHORIZED` | 401 | Missing or invalid API key | | `FORBIDDEN` | 403 | Missing required scope, or brand not in key's allow-list | | `RATE_LIMITED` | 429 | Per-key rate limit exceeded (check `Retry-After` header) | | `AGENT_LIMIT_EXCEEDED` | 429 | Daily plan generation limit reached | | `PAYMENT_REQUIRED` | 402 | Insufficient ad creative credits | | `VALIDATION_ERROR` | 400 | Invalid request body (missing required field, disallowed value, video in generate) | | `NOT_FOUND` | 404 | Set, variant, or job not found (or not owned by caller) | | `CONFLICT` | 409 | Idempotent request already executed (cached response returned) | | `GENERATION_FAILED` | 500 | AI copy/image generation failed | | `EXPORT_FAILED` | 500 | Export artifact build or storage upload failed | | `RENDER_ENQUEUE_FAILED` | 500 | Failed to enqueue render job | ### Related Documentation - [OpenAPI Specification](/api/v1/ad-creatives.openapi.json) — Machine-readable schema for all 6 endpoints - [Ad Creatives API Reference](/settings/api-docs/ad-creatives) — Developer guide with curl examples and integration walkthrough ## v1 Creative Scripts API Generate a structured, scene-by-scene creative script from a hook concept. Supports 6 format styles (UGC authentic, problem-solver, expert-led, visual story, stat-backed, interactive challenge) and 3 durations (15s / 30s / 60s). Scripts are persisted and can be used as creative briefs for copy, image, and video generation. ### POST /api/v1/creative-scripts/generate Convert a hook concept into a full scene-by-scene creative script. **Required scope:** `ad_creatives` **Request fields:** - `brand_id` (string, required) — Brand UUID - `hook_concept` (string, required, max 500 chars) — The core creative idea / angle - `format_style` (string, required) — One of: `ugc_authentic` | `problem_solver` | `expert_led` | `visual_story` | `stat_backed` | `interactive_challenge` - `platform` (string, required) — `instagram` | `facebook` | `tiktok` - `duration_seconds` (number, required) — `15` | `30` | `60` - `target_audience` (string, optional, max 200 chars) — Who this ad is for - `guardrails` (string, optional, max 500 chars) — Hard constraints, e.g. `"NO medical claims"` **Response:** `{ script: CreativeScript }` **CreativeScript fields:** - `id` — UUID, persisted to `ad_creative_scripts` - `brand_id` — Brand UUID - `format_style` — The requested format style - `hook_line` — Opening line / hook (max 100 chars) - `scenes` — Array of `{ second: string, visual: string, audio: string }` covering the full duration - `cta` — Call to action - `tone_notes` — Emotional arc and tonal guidance - `created_at` — ISO 8601 timestamp **Format style descriptions:** - `ugc_authentic` — Handheld camera energy, first-person POV, casual speech, problem → relief arc - `problem_solver` — Opens by agitating a pain point, then demonstrates the solution clearly - `expert_led` — Authority figure delivers confident insight with data and proof-points - `visual_story` — Emotion-first storytelling; visuals carry the narrative - `stat_backed` — Leads with a surprising statistic; builds credibility fast - `interactive_challenge` — Hooks with a direct challenge or question to the viewer **Error codes (in addition to common v1 errors):** - `GENERATION_FAILED` (500) — Claude output was unparseable or DB persist failed