Skip to main content
Designing eye-catching social media creatives using an AI agent requires it to have a lot of context: your product features, voice, target audience, and design tokens. Your agent needs to crawl your marketing site, distill its positioning and design system, then generate on-brand campaign ideas, copy, and social creatives for every platform. In this guide, we build a pipeline that does this at scale using Context.dev’s APIs and some popular agent skills. Try it: drop a domain in and render three example artboards — square post, landscape, and hero banner — using the brand’s website styleguide palette.

Architecture

The pipeline runs in four phases. The leverage is grounding every phase in the brand’s real site instead of a blank prompt.
  1. Gather crawls the whole marketing site to Markdown (Web Scraping API) and pulls the brand kit (Brand API) and design tokens (Styleguide API).
  2. Distill runs the product-marketing-context skill over the crawl to capture positioning, audience, and voice, turns the brand kit into a design guide, and generates net-new campaign ideas grounded in that positioning.
  3. Write runs the copywriting skill on the chosen idea for an on-brand headline, supporting line, and CTA.
  4. Render lays out the creative from the design guide and copy, then rasterizes it to each platform size.
Phases 2 and 3 run in your coding agent through the two skills; the rest is code. Gather once per brand and reuse the corpus, positioning, and design guide across every idea, creative, and size.

Prerequisites

  • A Context.dev API key. Grab one from the dashboard and export it as CONTEXT_DEV_API_KEY.
  • An Anthropic API key for the model calls in Steps 2 through 4. Export it as ANTHROPIC_API_KEY.
  • The Context.dev SDK for your backend:
npm install context.dev

Step 1. Gather the brand’s site

The first step is always to understand the product deeply. To do this, your AI agent needs context. It needs to know what your product does and who it’s for. You will also need your brand’s logos and styleguides. For this we’ll use:
  1. The Brand API to get brand name, description, slogans, backdrops, and all logo variants.
  2. The Web Scraping API to crawl reachable marketing-site pages as clean Markdown.
  3. The Styleguide API to get exact colors, component CSS, spacing, fonts, shadows, etc.
import ContextDev from "context.dev";

const client = new ContextDev({ apiKey: process.env.CONTEXT_DEV_API_KEY! });

export async function gatherBrand(domain: string) {
  // The voice (whole-site crawl), the identity (brand), and the type (styleguide).
  const [{ results }, { brand }, { styleguide }] = await Promise.all([
    client.web.webCrawlMd({ url: `https://${domain}`, maxPages: 100 }),
    client.brand.retrieve({ domain }),
    client.web.extractStyleguide({ domain }),
  ]);
  if (!brand || !styleguide) throw new Error(`No brand or styleguide data for ${domain}`);

  const logos = brand.logos ?? [];
  const lightLogo =
    logos.find((l) => l.type === "logo" && l.mode === "light")?.url ?? logos[0]?.url;

  return {
    // The design guide: identity plus the look, everything the render step needs.
    designGuide: {
      name: brand.title,
      description: brand.description,
      slogan: brand.slogan,
      palette: [
        styleguide.colors?.accent,
        styleguide.colors?.background,
        styleguide.colors?.text,
      ].filter(Boolean),
      logos: logos.map((l) => ({ url: l.url, mode: l.mode, type: l.type })), // every variant
      lightLogo,
      headingFont: styleguide.typography.headings.h1?.fontFamily,
      bodyFont: styleguide.typography.p?.fontFamily,
      spacing: styleguide.elementSpacing,
      shadows: styleguide.shadows,
      components: styleguide.components,
    },
    // The marketing corpus: one entry per page.
    pages: results.map((r) => ({ url: r.metadata.url, markdown: r.markdown })),
  };
}
Brand · 10 credits Styleguide · 10 credits Crawl · 1 credit per page The crawl bills 1 credit per page, so cost depends on how big the marketing site is. Use maxPages (default 100, max 500) and urlRegex to crawl fewer, more specific pages.

Step 2. Distill positioning and generate ideas

Next, we need the agent to come up with campaign ideas. Instead of trying to engineer the perfect prompt, we recommend using a skill. Run Corey Haines’ product-marketing-context on your coding agent with access to all the context we gathered in the last step. Make sure to specifically prompt it to generate 10 ideas for social media campaigns.
// ideate.ts
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic(); // reads ANTHROPIC_API_KEY
const MODEL = "claude-opus-4-8";   // Opus 4.8 for quality; Sonnet 4.6 for cheaper drafts

// Corey Haines' product-marketing-context skill, loaded straight from its repo.
const SKILL_URL =
  "https://raw.githubusercontent.com/coreyhaines31/marketingskills/refs/heads/main/skills/product-marketing/SKILL.md";

type Gathered = {
  designGuide: { name: string; slogan?: string; description?: string };
  pages: { url: string; markdown: string }[];
};

// Pass in everything gathered in Step 1; no extra Context.dev calls here.
export async function generateIdeas({ designGuide, pages }: Gathered): Promise<string> {
  const skill = await fetch(SKILL_URL).then((r) => r.text());

  const context = [
    `Brand: ${designGuide.name} (${designGuide.slogan})`,
    designGuide.description,
    ...pages.map((p) => `## ${p.url}\n${p.markdown}`),
  ].join("\n\n");

  const message = await anthropic.messages.create({
    model: MODEL,
    max_tokens: 2000,
    system: skill, // run the skill: it tells Claude how to build product-marketing context
    messages: [
      {
        role: "user",
        content: `${context}\n\nUsing the product-marketing context above, generate 10 distinct ideas for social media campaigns. One per line: the hook, then the angle.`,
      },
    ],
  });

  return message.content.find((b) => b.type === "text")?.text ?? "";
}

Step 3. Write the copy

Now you need to pick the ideas you like best. You can also fix or add new ideas. Then run Corey Haines’ copywriting skill on it, with a prompt specifying how you want the output.
// copy.ts
import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic(); // reads ANTHROPIC_API_KEY
const MODEL = "claude-opus-4-8";

// Corey Haines' copywriting skill, loaded straight from its repo.
const SKILL_URL =
  "https://raw.githubusercontent.com/coreyhaines31/marketingskills/refs/heads/main/skills/copywriting/SKILL.md";

export type Copy = { headline: string; subhead: string; cta: string };

type Gathered = {
  designGuide: { name: string; slogan?: string; description?: string };
  pages: { url: string; markdown: string }[];
};

export async function writeCopy(idea: string, { designGuide, pages }: Gathered): Promise<Copy> {
  const skill = await fetch(SKILL_URL).then((r) => r.text());

  const context = [
    `Brand: ${designGuide.name} (${designGuide.slogan})`,
    designGuide.description,
    ...pages.map((p) => `## ${p.url}\n${p.markdown}`),
  ].join("\n\n");

  const message = await anthropic.messages.create({
    model: MODEL,
    max_tokens: 1000,
    system: skill, // run the skill: it writes on-brand copy from the context
    messages: [
      {
        role: "user",
        content: `${context}\n\nWrite a social ad for this campaign idea: "${idea}". Return only a JSON object with keys "headline", "subhead", and "cta".`,
      },
    ],
  });

  const text = message.content.find((b) => b.type === "text")?.text ?? "{}";
  return JSON.parse(text) as Copy;
}

Step 4. Render the creative across sizes

Before we get an LLM to render the images, we need to specify the sizes. This script contains 6 of the most popular social media post sizes:
  • Instagram Story
  • Instagram Square Post
  • Instagram Portrait Post
  • X image card
  • LinkedIn Banner
  • YouTube video thumbnail
Edit this to include all the variants you want.
// variants.ts
export type Variant = {
  id: string;
  platform: string;
  width: number;
  height: number;
  layout: "square" | "portrait" | "wide";
};

export const VARIANTS: Variant[] = [
  { id: "ig-square", platform: "Instagram feed", width: 1080, height: 1080, layout: "square" },
  { id: "ig-portrait", platform: "Instagram portrait", width: 1080, height: 1350, layout: "portrait" },
  { id: "ig-story", platform: "Instagram story", width: 1080, height: 1920, layout: "portrait" },
  { id: "x-card", platform: "X card", width: 1200, height: 675, layout: "wide" },
  { id: "li-banner", platform: "LinkedIn banner", width: 1584, height: 396, layout: "wide" },
  { id: "yt-thumbnail", platform: "YouTube thumbnail", width: 1280, height: 720, layout: "wide" },
];
This pipeline designs the creatives in HTML/CSS/JS instead of using an image model because this way is cheaper, leads to fewer hallucinations, can be customized easily, and the text stays crisp and editable. So we will fan out one agent per idea, in parallel, handing each the design guide, the full set of sizes, and its idea. Each returns a single self-contained mockup that lays out every platform size, with a Download PNG button under each artboard that exports it to an image in the browser.

System prompt for the creative designer

// render.ts
import Anthropic from "@anthropic-ai/sdk";
import { VARIANTS } from "./variants";

const anthropic = new Anthropic(); // reads ANTHROPIC_API_KEY
const MODEL = "claude-opus-4-8";

type Gathered = {
  designGuide: {
    name: string;
    slogan?: string;
    description?: string;
    palette: string[];
    lightLogo?: string;
    headingFont: string;
    bodyFont: string;
  };
};

const SYSTEM_PROMPT = `You are a senior brand designer. Build ONE self-contained HTML document that mocks up a social campaign across every artboard size provided.
- Render one artboard per size at its exact width and height, labeled with the platform name.
- Use ONLY the styleguide palette, fonts, and Brand API logo from the design guide. Inline all CSS and load nothing external except the logo URL and the html-to-image library.
- Write short, on-brand copy for the idea: a headline, a supporting line, and a CTA. Lead with the styleguide accent color and heading font.
- Give each artboard a "Download PNG" button that exports it at its exact size with html-to-image (https://cdn.jsdelivr.net/npm/html-to-image) and triggers a download.
Return only the HTML document, with no commentary or code fences.`;

// One agent per idea: lays out every size as an HTML/CSS/JS mockup.
export async function designMockup(idea: string, { designGuide }: Gathered): Promise<string> {
  const message = await anthropic.messages.create({
    model: MODEL,
    max_tokens: 16000,
    system: SYSTEM_PROMPT,
    messages: [
      {
        role: "user",
        content: [
          `Brand: ${designGuide.name} (${designGuide.slogan}). ${designGuide.description}`,
          `Palette (primary first): ${designGuide.palette.join(", ")}`,
          `Heading font: ${designGuide.headingFont}. Body font: ${designGuide.bodyFont}. Logo: ${designGuide.lightLogo}`,
          `Campaign idea: ${idea}`,
          `Artboards (build one of each, at its exact size): ${JSON.stringify(VARIANTS)}`,
        ].join("\n"),
      },
    ],
  });

  return message.content.find((b) => b.type === "text")?.text ?? "";
}

// Fan out across every idea from Step 2 in parallel.
export async function renderCampaign(ideas: string[], gathered: Gathered) {
  return Promise.all(
    ideas.map(async (idea) => ({ idea, html: await designMockup(idea, gathered) })),
  );
}

Next steps

Web Scraping API

Crawl a whole site to clean Markdown, the corpus the positioning skill reads.

Styleguide API

Fonts, colors, and component CSS for the design guide.

product-marketing-context skill

Turns the crawl into positioning, audience, and brand voice.

copywriting skill

Writes on-brand copy from that positioning.