Generate a Cold-Email Pitch
Generate ready-to-send cold-email content (intro hook, competitive intel, fun facts, findings, full short email) for any URL — category-aware, voice-tunable, sync, built for batch outbound at scale.
Authentication: Required
Generate cold-email content for an outbound sales motion in a single sync request. Returns a ready-to-send email plus structured fields a rep can mix and match for follow-ups, and a scanLandingUrl deep link that triggers a free Visibility Scan Preview when the recipient clicks.
This endpoint is one half of Pendium's outbound stack — the cheap, batch top-of-funnel. The other half is Trigger a Visibility Scan Preview, the deeper public report you escalate to once a recipient engages. Both are built for GTM partners, in-house GTM teams, and consultants/agencies running outbound on their own Pendium account. Neither endpoint is the ongoing-monitoring product — paying customers' managed agents run scheduled scans through POST /api/visibility/scan and read results from GET /api/visibility/report. Use those for in-app dashboards, recurring measurement, and score history; use this endpoint and the Scan Preview for outbound prospecting.
Category-aware. The endpoint supports five base prompt framings (default, shopify, ecommerce, yelp, gbp) so the same API works for DTC stores, broader online retail, local services, and any business that lives in Google Business Profile. The default category auto-detects Shopify and gracefully falls back to a generic-store prompt for non-Shopify URLs.
Voice-tunable. Pass a named voice preset or your own freeform voice text and the email register adapts.
Override-aware. Pass notes with any open-ended instructions and they're injected at the top of the prompt with override priority over the voice and anti-slop rules.
Request body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | — | URL to generate the pitch for. Doesn't need to be *.myshopify.com — custom domains and non-Shopify URLs are both supported. |
category | string | No | "default" | Base prompt framing. One of default, shopify, ecommerce, yelp, gbp. See Categories below. |
voice | string | No | — | Named preset (founder, peer, analyst, witty, direct) OR freeform custom voice text. Preset names are matched case-insensitively; anything else is treated as your own voice instructions. See Voice presets below. |
notes | string | No | — | Open-ended notes / instructions injected with override priority. Use this when the default behavior is close but not quite right for a specific batch — e.g. campaign context, a tone constraint, a specific angle to lean into, an existing offer to mention. ≤4000 characters. |
recipientFirstName | string | No | — | Recipient's first name from your CRM, when available. Drives the personalised greeting line ("hey Dan,") so the rep never has to edit the email. ≤80 chars. Omit when the name isn't available — the model uses a no-name greeting ("hey,"). |
model | string | No | "gemini-flash" | Which model family to use. Three options: "gemini-flash" (Gemini 3 Flash — default; high throughput and the most cost-effective option for batch outbound at scale), "gemini-pro" (Gemini 3.1 Pro — frontier-quality middle tier from Google), "sonnet" (Claude Sonnet 4.6 — premium option with the strongest anti-slop and voice-preset adherence; lower throughput because Anthropic's organization-wide capacity is shared with other Pendium pipelines). Opt in to "sonnet" only when output quality matters more than throughput. |
Categories
| Value | Shopify probe | Catalog injected? | Best for |
|---|---|---|---|
default (omitted) | yes (auto-detect) | if probe hits | mixed lists where you don't know up front whether each row is Shopify |
shopify | trusts the caller (no probe) | yes (or falls back to e-commerce framing if catalog fetch fails) | confirmed Shopify storefronts |
ecommerce | yes (try) | if probe hits | broader online retail (Magento, BigCommerce, custom DTC) where Shopify is uncertain |
yelp | no | no | local services with strong review-aggregator signal — restaurants, salons, contractors, clinics |
gbp | no | no | businesses where Map Pack and Google Business Profile completeness is the visibility lever |
is_shopify in the response reflects whether a catalog was actually fetched, independent of category — a shopify category can still produce isShopify: false if the storefront fetch failed.
Voice presets
Each preset bundles character + tone + vibe into a single named choice:
| Preset | Vibe |
|---|---|
founder | First-person, plainspoken, skin-in-the-game energy. Built-this-because-I-needed-it. |
consultant | Third-person, measured, observational. Reads like an analyst note. |
witty | Wry, observation-driven. One sharp line per email; specificity over wordplay. |
warm | Friendly, generous, curious. Notices the small thing on the homepage. |
direct | Short sentences. Plain words. Get to the observation, ask the question, sign off. |
Pass any other string and the endpoint treats it as your own custom voice instructions. The voiceLabel field in the response tells you whether your input matched a preset (preset name), was treated as custom ("custom"), or wasn't supplied ("default").
Notes — when to use it
Notes outrank the voice block, the category instructions, and the anti-slop guards if anything conflicts. Reach for notes when:
- The default category framing is close but you want to nudge it (e.g. "this brand sells refurbished electronics — frame the pitch around AI assistants steering shoppers away from refurbs and how to win that consideration").
- You're running a campaign with shared context (e.g. "all of these emails are going out the week before Black Friday — tilt the offer toward urgency without using the word 'urgency'").
- A specific batch needs a tone constraint (e.g. "do not mention competitors by name in the email body — keep them in competitiveIntel only").
Don't use notes to fight the anti-slop guards — those exist because cold-email tells like "I hope this finds you well" tank reply rates. The override is for nuance, not to disable quality controls.
Example
Response
The email object is split into five fields so the rep never has to edit anything before sending. Concatenate them with blank lines between (greeting → opening → body → ps?); the outbound tool appends the rep's signature.
Bracket-free guarantee. Every email field — subject, greeting, opening, body, ps — is fully ready to send as text. No [Your name] placeholders, no {{firstName}} merge tokens, no brackets of any kind. Pass recipientFirstName to get a personalised greeting; otherwise the greeting uses a no-name fallback ("hey,"). Sender signature is appended by the outbound tool.
Key fields
| Field | Description |
|---|---|
uuid | Stable identifier for this pitch. Store it alongside your CRM row so you can refetch the same content later via GET. |
model | Which model family generated this pitch — "gemini-flash" (Gemini 3 Flash, default), "gemini-pro" (Gemini 3.1 Pro), or "sonnet" (Claude Sonnet 4.6). |
category | The base prompt block actually used. Echoes back the resolved value (default if the caller didn't pass one). |
isShopify | true when the URL produced a usable Shopify catalog and it was injected into the prompt. Independent of category. |
voice | Echoes the caller's voice input (or null if none). |
voiceLabel | Resolved voice — preset name, the literal "custom" for freeform input, or "default" if no voice was supplied. |
notesApplied | true if the caller passed notes and they were injected as a high-priority override. |
recipientFirstName | Echoes the caller's recipientFirstName input (or null if none). |
introHook | One-sentence opener (≤140 chars) — repurpose for follow-ups, subject-line A/B tests, or CRM merge fields. |
competitiveIntel | { competitor, angle }. A recognizable competitor + the scan finding stated as a finding (the model IS the AI agent that ran the scan; its judgment is the data). |
funFacts | 1–2 short, recipient-specific observations. |
findings | 1–3 actionable AI-visibility improvements, each { title, detail }. |
email.subject | Email subject line (≤60 chars), sentence case, fully ready to send. |
email.greeting | Salutation line ("hey Dan," / "Hi,"). Uses recipientFirstName when provided. No brackets. |
email.opening | Single hook sentence. Stands alone after the greeting. |
email.body | 2–3 middle sentences (the scan finding + offer). Does NOT include the greeting, opening, or any signoff. |
email.ps | Optional one-line PS — populated only for the founder voice preset (cold-email-acknowledgment pattern). null for every other preset. |
scanLandingUrl | Always present. The deep-link CTA the rep can drop into the email when they want to include it. When the recipient clicks, Pendium runs the deeper followup Visibility Scan Preview on their URL and lands them on the live results page. The model does NOT auto-embed this in the email body — the rep decides per-recipient whether to include it (links sometimes hurt deliverability). |
grounded | Reports whether the always-on brand-identity grounding step actually fired for this pitch. true in the normal case; false only when the search backend was unavailable (in which case groundingFallbackReason is also present). Not a request parameter — the endpoint always attempts grounding. |
groundingFallbackReason | Only present when grounded: false because the always-on grounding step failed. Currently only "serper_unavailable". |
Refetch a previous pitch
Returns the same JSON shape as the POST response, including the same scanLandingUrl. Useful for CRM idempotency, support, and replaying a pitch into a different outbound tool without re-paying for LLM cost. Authorization: caller must own the pitch (or be a platform admin).
What to do with this data
The email is fully ready to send — every field is plain text with no brackets, no placeholders, no merge tokens. The simplest integration is:
Pass that into your outbound tool's body field; the tool appends the rep's signature. Use scanLandingUrl as the call-to-action link when you want to include it (often left out of first emails for deliverability and saved for follow-ups).
The structured fields (introHook, competitiveIntel, funFacts, findings) exist so the rep can mix and match for follow-ups in the same thread, A/B different openers, or pull individual hooks into a CRM merge field. When a recipient engages, escalate to the full Visibility Scan Preview for the same URL.