# Proposal Generator

> Generate a branded sales proposal from a CRM deal record — packaged as a Gamma deck (or Google Doc fallback) with embedded Stripe payment link, emailed to the prospect via Gmail draft. On-demand per deal.

- Skill: `fivebucksventures/proposal-generator` (Agent Skill)
- Install (CLI): `npx skillmds@latest add fivebucksventures/proposal-generator`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fivebucksventures/proposal-generator/raw
- Safety review: PASS (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: fivebucksventures (https://skillmd.com/u/fivebucksventures)
- Updated: 2026-08-19
- Page: https://skillmd.com/skills/fivebucksventures/proposal-generator

---


## Maintenance

| Agent | Version | Last Changed |
|---|---|---|
| Link | v2.20.0 | July 12, 2026 |

**Description:** Generate a branded sales proposal from a CRM deal record — packaged as a Gamma deck (or Google Doc fallback) with embedded Stripe payment link, emailed to the prospect via Gmail draft. On-demand per deal.

### Change Log

**v2.20.0** — July 12, 2026
- **Declared the fb.ai dependency and its required scope.** fb.ai API keys are now scoped; this skill's `deps.gateway` now names `fivebucks` and the scope it needs (`social_posts`), so `brand-setup` can tell users which capability boxes to tick. No behaviour change — see `agents/link.md` for the scope/error/quota contract.

**v2.5.0** — May 20, 2026
- Brand color/font resolution is now a **3-tier lookup**: fb.ai brand kit (`fivebucks_get_brand_kit`) → local `brands/{brand}/design-system/` → `brand.md`, per the Brand kit field map in `agents/link.md`. The Gamma `additionalInstructions` payload no longer requests a `secondary {HEX}` the kit can't provide — it now uses primary/accent/background/text-dark mapped from the active source. Trimmed duplicated design-system reading boilerplate.

**v2.4.1** — May 12, 2026
- Step 1 (Read Brand Context) — restructured with a leading "Brand visual identity" block. design-system/ probed first (extract HEX color tokens + typography + component patterns), brand.md visual identity as fallback. Strategic + commercial context (product.md, audience.md, sales.md) now grouped under their own subhead. Abort behavior clarified: only `brand.md`/`product.md`/`audience.md`/`sales.md` missing aborts; design-system/ missing is a graceful fallback.
- Step 5 — Gamma `additionalInstructions` payload expanded to carry explicit HEX values + font-family extracted at Step 1. Was previously "Use brand colors {primary, secondary}" — vague enough that Gamma routinely defaulted to its own template colors. Now spells out primary/secondary/accent/background HEX + typography family, with the design-system→brand.md fallback order baked into the spec.
- Quality Checklist — two new entries enforce the design-system probe and the explicit HEX/font payload in Gamma's `additionalInstructions`.

**v2.4.0** — May 07, 2026
- Initial production release as part of the v2.4.0 business-operations expansion.

# SKILL.md — Proposal Generator

## Before Executing

Read `agents/link.md` before starting. It defines the active brand, personality, quality checklist, and available tools. Determine the active brand from `$DEFAULT_BRAND` env var — if not set, ask the user.

You are a B2B sales engineer for the active brand. Your job is to take a qualified deal from the CRM and produce a tailored, branded sales proposal — packaged as a sharable Gamma deck (or Google Doc fallback), priced from `product.md`, governed by the terms in `sales.md`, and dispatched as a Gmail draft with an embedded Stripe payment link. The proposal MUST be free of fabrication: every claim, feature, price, and term comes from brand context files.

Runs on-demand, one deal at a time.

---

## Role

Sales engineer + deal closer. You translate a CRM record into a proposal a prospect can sign and pay. You do NOT invent pricing, terms, case studies, or guarantees — those live in `brands/{brand}/product.md` and `brands/{brand}/sales.md`. You DO tailor the language and emphasis to the persona's pain points (from `audience.md`) and pick the right plan tier per the persona's default rule (from `sales.md`).

---

## When to use

Use this skill when:
- A CRM deal record reaches Stage = "Proposal Requested" and the rep wants a branded deliverable
- Manual invocation per deal with a Notion CRM page URL or page ID
- A re-quote is needed for an existing deal (custom terms or new tier)

Do NOT use this skill for:
- Cold outreach copy → use `outreach-sequencer`
- Customer onboarding after the deal closes → use `customer-onboarder`
- Generic sales decks not tied to a specific deal → use `campaign-presenter`

---

## Inputs required

Before starting, confirm these inputs with the user:

| Input | Required | Notes |
|-------|----------|-------|
| Deal record | Yes | Notion CRM page URL or page ID. Must live inside `${BRAND}_CRM_DB` |
| Persona slug | Optional | Auto-detect from CRM record's Persona property; ask only if missing |
| Plan tier | Optional | Defaults to `sales.md` → Default tier per persona; override only if user explicitly asks |
| Add-ons | Optional | List of add-on slugs from `product.md`; defaults per `sales.md` upsell rules |
| Custom terms | Optional | Override `sales.md` Proposal Defaults (payment, validity, cancellation) — flag in audit trail |
| Output format | Optional | `gamma` (default) or `gdoc` fallback; auto-fallback to gdoc if Gamma fails for the brand |

---

## Step-by-step workflow

### Step 1 — Read Brand Context

**Brand visual identity — read FIRST so the Gamma deck inherits the right palette. Resolve the source in this 3-tier order:**
1. **fb.ai brand kit** *(top tier — only when `FIVEBUCKS_API_KEY` is set)* — call gateway tool `fivebucks_get_brand_kit`. If it returns non-null, use its color tokens (HEX) + typography as the authoritative source for the Gamma `additionalInstructions` at Step 5 — resolve fields via the Brand kit field map in `agents/link.md` (secondary→`tokens.colors.accent`, text→`tokens.colors.dark`, fonts from `tokens.fonts.heading`/`body`; the kit has no separate `secondary` or font weight scale). Returns null when no kit is uploaded — fall through to tier 2.
2. **brands/{brand}/design-system/** *(local folder — when the fb.ai kit is null or `FIVEBUCKS_API_KEY` is unset; the free baseline)* — read per link.md tier 2 (HEX color tokens + typography); also note any component patterns you'll reference in the Gamma `additionalInstructions` at Step 5.
3. **brands/{brand}/brand.md** — visual identity (colors, fonts, logo URL), voice, locale, currency, approved phrases. The Colors + Google Font sections are the universal fallback when neither of the above is available. Never block on a missing fb.ai key or design-system; brand.md is always available.

Same Visual consistency rule as `agents/link.md` — derive from the fb.ai brand kit (`fivebucks_get_brand_kit`, when `FIVEBUCKS_API_KEY` set) → local `design-system/` → `brand.md` (fallback), never hardcode brand colors/fonts from memory.

**Strategic + commercial context:**
- `brands/{brand}/product.md` — pricing tiers, features per tier, differentiators, add-ons
- `brands/{brand}/audience.md` — persona pain points, objections, buying triggers (used to tailor the Solution section)
- `brands/{brand}/sales.md` — Proposal Defaults (terms, payment, validity, cancellation), Default tier per persona, Upsell rules, Sender Persona (signature block, photo URL, email)

If `brand.md`, `product.md`, `audience.md`, or `sales.md` is missing, abort with a `failed` log and tell the user which file to populate. `design-system/` missing is NOT a hard fail — fall back to brand.md and continue.

### Step 2 — Fetch Deal Record from CRM

Resolve the deal from Notion. The deal page MUST live inside the brand's CRM database (`${BRAND}_CRM_DB` env var) — never accept a deal page from another brand's DB.

```
Use mcp__claude_ai_Notion__notion-fetch:
- id: <deal page URL or page ID from user input>
```

Validate:
- The page's parent database ID matches `${BRAND}_CRM_DB`. If not, abort with `failed` — wrong brand context.
- Required CRM properties exist: `Client Name`, `Client Email`, `Persona`, `Stage`, `Plan Tier` (optional), `Custom Notes` (optional).
- If `Persona` is unset and user did not supply one in inputs, ask before continuing.

Extract: `client_name`, `client_email`, `persona`, `current_stage`, `crm_plan_tier` (if any), `custom_notes`.

### Step 3 — Resolve Plan Tier and Add-ons

Decide which plan to propose:
1. If user supplied a plan tier explicitly → use it.
2. Else if `crm_plan_tier` is set on the deal → use it.
3. Else read `sales.md` → Default tier per persona, look up the row matching `persona`, use that tier.

Decide add-ons:
1. If user supplied add-ons → use them.
2. Else read `sales.md` → Upsell rules. Apply rules whose trigger matches this deal (e.g., "if persona = enterprise-buyer AND plan = Pro, suggest Managed Onboarding").

Pull the resolved tier's price, billed period, and feature list from `product.md`. Pull each add-on's price and description from `product.md`.

### Step 4 — Build Proposal Content Sections

Assemble the proposal content (text only — no formatting yet). Six sections:

| Section | Source | Content |
|---|---|---|
| Cover | `brand.md` + deal | Brand logo, client name, proposal date, validity expiry from `sales.md` |
| About Us | `brand.md` | Mission, positioning statement, approved phrases — 1 paragraph max |
| The Challenge | `audience.md` → Pain points for this persona | Restate the persona's top 2-3 pain points in their language. Reference `custom_notes` from CRM if present |
| Our Solution | `product.md` features tailored to persona pain points | Map each pain point to a specific feature (claim → proof). Use approved phrases from `brand.md`. Lead with the persona's strongest buying trigger from `audience.md` |
| Pricing | `product.md` resolved tier + add-ons | Tier name, price, billed period, included features, optional add-ons with line-item pricing. Use brand currency from `brand.md` Locale section |
| Terms & Next Steps | `sales.md` Proposal Defaults + payment link | Payment terms, validity period, cancellation policy, signature block from Sender Persona, payment link from Step 6 |

Keep total content under 1200 words for a deck (Gamma renders best in this range). For a Google Doc fallback, expand to 2-3 pages.

### Step 5 — Generate the Proposal Deliverable

**Preferred path — Gamma deck** (when the brand has a Gamma template configured):

```
Use mcp__claude_ai_Gamma__generate_from_template:
- inputText: <full proposal content from Step 4, structured as section headings>
- additionalInstructions: "Brand: {brand}. Voice: {voice from brand.md}. Use brand colors: primary {HEX}, accent {HEX}, background {HEX}, text/dark {HEX} (map each from the active source per the Brand kit field map in agents/link.md — fb.ai kit provides primary/accent/dark/background; local design-system/ or brand.md when the kit is null — never invent). Typography: heading {font-family} + body {font-family} (family names from fb.ai kit / local design-system/ when present, brand.md Google Font when fallback; font weight scale comes only from the local design-system, not the kit). Logo: {logo URL from brand.md}. Persona: {persona}."
- format: "presentation"
- numCards: 8
- exportAs: "pdf"
```

Build the `additionalInstructions` string from the values extracted at Step 1 — fb.ai brand kit tokens win when present, then local design-system/, brand.md fallback otherwise. The explicit HEX + font-family line is what makes Gamma generate a deck that matches the brand instead of defaulting to its own templates.

Capture the returned `gammaUrl` and `pdfUrl`.

**Fallback path — Google Doc** (when Gamma is unavailable for the brand, or `output format = gdoc`):

```
Use mcp__claude_ai_Google_Drive__create_file:
- name: "Proposal_{client_name}_{DDMonYYYY}"
- mimeType: "application/vnd.google-apps.document"
- content: <full proposal content from Step 4, formatted as markdown>
- parents: [<brand's proposal folder ID from settings.local.json, or root>]
```

Capture the returned `webViewLink`. Set link sharing to "anyone with the link can view" only if `sales.md` Proposal Defaults declares external sharing is allowed; otherwise leave restricted.

If both paths fail, abort with `failed` log — do not silently produce a worse deliverable.

### Step 6 — Create Stripe Payment Link

Compute the payment amount:
- Base: resolved tier price × validity period (e.g., monthly tier × 12 months for an annual proposal). Period rule lives in `sales.md` Proposal Defaults.
- Add-ons: sum of resolved add-on prices.
- Currency: from `brand.md` Locale section.
- Expires at: today + `sales.md` Proposal Defaults validity period (e.g., 14 days).

If the Stripe MCP is not yet authenticated, kick off the OAuth flow first:

```
Use mcp__claude_ai_Stripe__authenticate
```

Once authenticated, create a payment link via the Stripe MCP's payment-link tool. The tool name surfaces only after authentication — load it via ToolSearch before calling.

```
Use Stripe MCP payment_link / checkout creation tool (loaded via ToolSearch after authentication):
- amount: <total in smallest currency unit, e.g., cents>
- currency: <ISO from brand.md, e.g., "sgd">
- description: "Proposal for {client_name} — {tier} plan"
- metadata: {
    "brand": "<active-brand>",
    "deal_id": "<Notion page ID>",
    "persona": "<persona slug>",
    "tier": "<tier name>",
    "addons": "<comma-separated slugs>"
  }
- expires_at: <ISO timestamp = today + validity from sales.md>
```

Capture the returned `payment_link_url`. Inject it into the Terms & Next Steps section of the proposal — if the deck is already generated, regenerate the final card with the link, or note the link in the cover email so the prospect can pay independently.

**Stripe MCP deferred-tool note:** before calling any Stripe tool other than `authenticate` / `complete_authentication`, call `ToolSearch` with query `"select:<tool_name>"` to load the schema. The Stripe MCP exposes its real tools only after the user completes OAuth.

### Step 7 — Compose Cover Email (Gmail Draft)

Build a Gmail draft with the proposal link, payment link, and persona-tailored opening.

**Gmail is a deferred MCP tool — load its schema before calling:**

```
Use ToolSearch:
- query: "select:mcp__claude_ai_Gmail__create_draft"
```

```
Use mcp__claude_ai_Gmail__create_draft:
- to: <client_email from CRM>
- from: <sender email from sales.md Sender Persona>
- subject: "{Brand} proposal — {client_name} — {tier} plan"
- body: <persona-tailored email — see structure below>
```

Email body structure (all content from context files — never invent):
1. **Opening** — 1 sentence referencing the persona's top buying trigger from `audience.md`. Use voice from `brand.md`.
2. **What's inside** — 2-3 bullet points summarizing the proposal (tier, key features, total price).
3. **Proposal link** — Gamma URL or Google Doc URL from Step 5.
4. **Payment link** — Stripe URL from Step 6 with validity expiry note.
5. **Next steps** — book-a-call link if `sales.md` Sender Persona declares one, else a reply-to-this-email CTA.
6. **Signature block** — exact block from `sales.md` Sender Persona (name, title, photo URL, contact info).

Save as draft (do NOT auto-send). The rep reviews and sends manually unless the user explicitly says "send it".

### Step 8 — Update Notion Deal Record

Update the deal page properties to reflect proposal-sent state:

```
Use mcp__claude_ai_Notion__notion-update-page:
- page_id: <deal page ID>
- properties: {
    "Stage": "Proposal Sent",
    "Proposal URL": "<gamma URL or gdoc URL>",
    "Payment Link": "<stripe URL>",
    "Proposal Sent Date": "<today ISO>",
    "Proposal Tier": "<resolved tier name>",
    "Proposal Amount": <total amount>,
    "Proposal Currency": "<currency from brand.md>",
    "Proposal Expires": "<expiry date from Stripe>"
  }
```

Append a content block to the deal page's body summarizing: tier, add-ons, total, expiry, links — so the deal history is auditable inside Notion.

### Step 9 — Notify Slack

DM the rep via Slack so they know the draft is ready to review and send.

**Before calling `slack_send_message`, you MUST first call `ToolSearch` with query `"select:mcp__claude_ai_Slack__slack_send_message"` to load the tool schema.** The Slack MCP tool is deferred — calling it without loading the schema first will cause the task to hang.

```
Use mcp__claude_ai_Slack__slack_send_message:
- channel_id: "$SLACK_NOTIFY_USER"
- text: "Proposal ready — {client_name} — {tier} — {amount} {currency} — Gmail draft pending review.
         Deck: <gamma/gdoc URL>
         Payment link: <stripe URL>
         Notion deal: <notion deal URL>"
```

### Step 10 — Save Local Audit Backup

Save a metadata-only audit record to local outputs:

```
outputs/{brand}/proposals/Proposal_{client_name}_{DDMonYYYY}.md
```

The local file is metadata only — the actual deliverable lives in Gamma or Google Drive. Local file purpose is the audit trail.

---

## Output format

**Save location — local audit:**
```
outputs/{brand}/proposals/
```

**Naming convention:**
```
Proposal_{client_name}_{DDMonYYYY}.md
```

Examples:
- `Proposal_AcmeCorp_07May2026.md`
- `Proposal_PineAccounting_07May2026.md`

**Local audit metadata:**
```yaml
---
Date: YYYY-MM-DD
Skill Used: proposal-generator
Brand: {brand}
Client: {client_name}
Persona: {persona slug}
Tier: {resolved tier}
Add-ons: [list]
Amount: {total}
Currency: {ISO}
Validity: {days}
Expires: YYYY-MM-DD
Deal Notion URL: <url>
Proposal URL (Gamma/GDoc): <url>
Payment Link (Stripe): <url>
Gmail Draft ID: <id>
Status: Draft Sent | Sent | Failed
---
```

**Deliverables produced:**
- Gamma deck or Google Doc (proposal itself)
- Stripe payment link (signature/payment)
- Gmail draft (cover email — pending rep review)
- Notion deal record updated (Stage=Proposal Sent, links, amount, expiry)
- Slack notification to `$SLACK_NOTIFY_USER`
- Local audit file at `outputs/{brand}/proposals/`

---

## Quality checklist

Before finalizing:

- [ ] Deal page parent DB matches `${BRAND}_CRM_DB` — no cross-brand contamination
- [ ] Brand visual source resolved in 3-tier order at Step 1: fb.ai brand kit (`fivebucks_get_brand_kit`, checked first when `FIVEBUCKS_API_KEY` set) → local `brands/{brand}/design-system/` (when present) → `brand.md` Colors + Google Font; never blocked on a missing key or design-system
- [ ] Gamma `additionalInstructions` carries explicit HEX values + font-family extracted at Step 1 (or the Google Doc fallback styles the cover/section headings to match) — never invented from memory
- [ ] All pricing comes from `brands/{brand}/product.md` — no invented prices or tiers
- [ ] Plan tier matches `sales.md` Default tier per persona unless user explicitly overrode
- [ ] Add-ons resolved per `sales.md` Upsell rules — no random add-ons
- [ ] Persona pain points in The Challenge section come verbatim from `audience.md` — no fabrication
- [ ] Solution section maps each pain point to a real feature in `product.md`
- [ ] Terms (payment, validity, cancellation) come from `sales.md` Proposal Defaults
- [ ] Currency in proposal matches `brand.md` Locale section
- [ ] Stripe payment link metadata includes `brand`, `deal_id`, `persona`, `tier`
- [ ] Stripe payment link expiry matches `sales.md` validity period
- [ ] Gmail signature block is exactly the Sender Persona block from `sales.md` — not invented
- [ ] Gmail draft saved (not auto-sent) unless user explicitly approved sending
- [ ] Notion deal updated with all 7 properties (Stage, URLs, dates, amount)
- [ ] Slack notification sent to `$SLACK_NOTIFY_USER`
- [ ] Local audit file written to `outputs/{brand}/proposals/`
- [ ] Agent run logged to dashboard

---

## Final Step — Log to Dashboard

See `docs/new_agent_onboarding/metrics-spec.md` for the full JSONB contract.

```
Use gateway MCP tool `fiveagents_log_run`:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
- skill: "proposal-generator"
- brand: "<active-brand>"
- status: "<success|failed>"
- summary: "<1 line, <200 chars>"
- started_at: "<ISO timestamp>"
- completed_at: "<ISO timestamp>"
- metrics: {
    "date": "YYYY-MM-DD",
    "client": "<client name>",
    "deal_id": "<notion page id>",
    "persona": "<persona slug>",
    "tier": "<resolved tier>",
    "addons": ["<addon slugs>"],
    "amount": 0,
    "currency": "<ISO>",
    "validity_days": 0,
    "expires_at": "YYYY-MM-DD",
    "format": "<gamma|gdoc>",
    "proposal_url": "<gamma or gdoc URL>",
    "payment_link_url": "<stripe URL>",
    "gmail_draft_id": "<id>",
    "notion_deal_url": "<notion URL>",
    "stage_after": "Proposal Sent",
    "output_path": "outputs/{brand}/proposals/",
    "deliverable": "Proposal_{client}_{DDMonYYYY}.md"
  }
```

