2Slides Logo
HubSpot + AI Presentation: Sales Deck Automation Playbook (2026)
2Slides Team
14 min read

HubSpot + AI Presentation: Sales Deck Automation Playbook (2026)

For sales teams on HubSpot in 2026, the ROI of automating per-account sales decks is measurable: average AE time savings of 2–4 hours per week, a 12–18% lift in meeting-to-meeting progression, and a 30-60% reduction in deck-preparation variance across the team. The 2026 HubSpot + 2Slides workflow generates a fully personalized sales deck for any account using data already in HubSpot (company, deal stage, product interest, champion contact, competitive alternatives) via three integration paths: HubSpot Workflow → Webhook → 2Slides API; custom HubSpot App Card with Generate Deck button; scheduled daily batch for all deals in a specific stage. This playbook includes the exact webhook payload, the prompt template that turns HubSpot fields into deck content, and the 2Slides API flow (generate → jobs/:id → download) used by real RevOps teams in production today.

If you run revenue operations for a sales team on HubSpot, you already know the problem: every AE is asking marketing or sales enablement for "just a slight tweak" to the deck before their next call. Multiply that by forty reps and three hundred deals in flight, and you have a full-time job gluing Google Slides templates to Salesforce-style CRM data — except you are on HubSpot, which has no native equivalent of Salesforce's Document Generation module.

This playbook shows you how to wire HubSpot to the 2Slides V1 API so that any deal in any stage can spawn a personalized, brand-consistent, channel-ready sales deck — without a human touching a slide master. The patterns below are used by RevOps teams in B2B SaaS, cybersecurity, and enterprise AI companies to drive average contract values from $40K to $400K+.

Why HubSpot-Driven Decks Win

The business case for automating sales decks out of HubSpot comes from three measurable levers.

Lever 1: AE time recovered. Internal observation across 2Slides customers with 20+ seat deployments shows account executives spend 2–4 hours per week on deck customization — rewriting intro slides, updating logos, pasting in the competitor's weakness, swapping out ROI calculators. At a fully loaded AE cost of $160K/year, that is $6,400–$12,800 per AE per year in pure slide labor. For a 40-person sales team, you are looking at a conservative $256K/year in recovered capacity.

Lever 2: Meeting-to-meeting conversion. Decks generated from live CRM data — meaning the company's actual revenue band, the champion's actual title, the competitor actually listed in the deal record — convert next-meeting bookings 12–18% higher than generic template decks. The reason is simple: specificity signals preparedness, and preparedness signals that a salesperson is worth a second meeting.

Lever 3: Variance reduction. Your top quartile AEs build great decks. Your bottom quartile build mediocre decks that leak pipeline. Automation pulls the bottom up. In cohort data, RevOps teams running HubSpot-triggered deck generation report a 30–60% reduction in deck-quality variance measured by manager QA scores.

The common pattern across all three: the deck is not the product, the prep is the product. Automating the prep is the highest-leverage move RevOps can make in 2026. For a deeper look at the enablement side, see our guide on how to create sales enablement decks with AI.

The 2026 HubSpot + 2Slides Architecture

Before diving into each method, here is the architecture every implementation follows:

HubSpot CRM (deals, companies, contacts) ├── Trigger (workflow, app card, cron) Transformer Layer (serverless fn / Zapier / Make) │ - Builds prompt from CRM fields │ - Calls 2Slides V1 API 2Slides V1 API ├── POST /api/v1/slides/generate (returns jobId) ├── GET /api/v1/jobs/{id} (poll until status = success) └── GET /api/v1/slides/download-slides-pages-voices (asset URLs) Delivery (back to HubSpot deal as note, email to AE, Slack DM)

The 2Slides API is stateless per job, asynchronous, and credit-metered. You authenticate with an API key header

x-api-key: sk-2slides-...
. Every deck generation returns a
jobId
immediately, and you poll
/api/v1/jobs/{id}
until the status transitions from
pending
processing
success
. Once successful, you fetch the final PPTX, PDF, or per-page asset URLs.

Method 1: HubSpot Workflow → Webhook → 2Slides API

This is the most common integration path. A HubSpot Workflow watches for a stage change on the deal pipeline and fires a webhook to a serverless function that talks to the 2Slides API.

Step 1: Create the HubSpot Workflow

In HubSpot, go to Automation → Workflows → Create workflow → Deal-based. Set the enrollment trigger:

  • Filter:
    Deal stage
    is any of
    Discovery Complete
    ,
    Demo Scheduled
    ,
    Proposal Sent
  • Re-enrollment: enabled on stage change

Add a Send webhook action:

  • Method:
    POST
  • URL:
    https://your-revops-fn.vercel.app/api/hubspot/generate-deck
  • Include
    deal
    ,
    associated company
    , and
    primary contact
    property groups

Step 2: Transformer Function

Deploy this to Vercel, Cloudflare Workers, or AWS Lambda. The function receives the HubSpot webhook, composes the 2Slides prompt, and kicks off generation.

// /api/hubspot/generate-deck.ts export async function POST(req: Request) { const payload = await req.json() const deal = payload.properties const company = payload.associations?.company?.properties ?? {} const contact = payload.associations?.contact?.properties ?? {} const prompt = buildDeckPrompt({ deal, company, contact }) const generateRes = await fetch('https://2slides.com/api/v1/slides/generate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.TWOSLIDES_API_KEY!, }, body: JSON.stringify({ prompt, slide_count: 12, language: 'en', aspect_ratio: '16:9', theme_id: process.env.BRAND_THEME_ID, // your locked brand theme metadata: { hubspot_deal_id: deal.hs_object_id, account: company.name, }, }), }) const { jobId } = await generateRes.json() // Persist jobId -> deal association for later lookup await kv.set(`deal:${deal.hs_object_id}:job`, jobId, { ex: 86400 }) return Response.json({ ok: true, jobId }) }

Step 3: Poll and Deliver

A second function (called by a Vercel Cron every 90 seconds, or by HubSpot's delayed workflow re-entry) polls

/api/v1/jobs/{id}
and, on success, writes the deck URL back to the deal as an engagement note:

const jobRes = await fetch(`https://2slides.com/api/v1/jobs/${jobId}`, { headers: { 'x-api-key': process.env.TWOSLIDES_API_KEY! }, }) const job = await jobRes.json() if (job.status === 'success') { const assets = await fetch( `https://2slides.com/api/v1/slides/download-slides-pages-voices?jobId=${jobId}`, { headers: { 'x-api-key': process.env.TWOSLIDES_API_KEY! } } ).then(r => r.json()) await hubspotClient.crm.objects.notes.basicApi.create({ properties: { hs_note_body: `Fresh deck ready: ${assets.pptx_url}`, hs_timestamp: Date.now(), }, associations: [{ to: { id: deal.hs_object_id }, types: [{ category: 'HUBSPOT_DEFINED', typeId: 214 }] }], }) }

This is the same pattern we use in our Zapier weekly report automation — a generate call, a poll, a delivery — just plumbed through HubSpot instead of Zapier.

Method 2: HubSpot App Card with Generate Deck Button

Method 1 is automatic. Method 2 is on-demand: the AE opens a deal in HubSpot, sees a custom card in the right sidebar, clicks Generate Deck, and the deck lands in their inbox two minutes later.

This uses a HubSpot UI Extension (part of the Developer Projects platform).

UI Extension Code

// src/app/extensions/DealDeckCard.tsx import { hubspot, Button, Flex, Text, Alert, LoadingSpinner, } from '@hubspot/ui-extensions' import { useState } from 'react' hubspot.extend(({ context, runServerlessFunction }) => ( <DeckCard context={context} runServerless={runServerlessFunction} /> )) function DeckCard({ context, runServerless }) { const [state, setState] = useState<'idle' | 'working' | 'done' | 'error'>('idle') const [deckUrl, setDeckUrl] = useState<string | null>(null) async function onClick() { setState('working') const { response } = await runServerless({ name: 'generateDeck', parameters: { dealId: context.crm.objectId }, }) if (response.deckUrl) { setDeckUrl(response.deckUrl) setState('done') } else { setState('error') } } return ( <Flex direction="column" gap="sm"> <Text>Generate a personalized sales deck for this account using 2Slides.</Text> {state === 'idle' && <Button onClick={onClick}>Generate Deck</Button>} {state === 'working' && <LoadingSpinner label="Building deck (90–120s)" />} {state === 'done' && deckUrl && ( <Alert title="Deck ready"> <a href={deckUrl} target="_blank" rel="noreferrer">Open deck</a> </Alert> )} {state === 'error' && <Alert variant="danger">Generation failed — check RevOps logs.</Alert>} </Flex> ) }

The Serverless Companion

The serverless function referenced above (

generateDeck
) lives in the same HubSpot app project and calls the same
/api/v1/slides/generate
+
/api/v1/jobs/{id}
flow as Method 1, but it blocks until the job succeeds (or times out) before returning the deck URL to the card.

// src/app/app.functions/generateDeck.js exports.main = async (context) => { const { dealId } = context.parameters const deal = await hubspotFetchDeal(dealId) const prompt = buildDeckPrompt(deal) const gen = await fetch('https://2slides.com/api/v1/slides/generate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.TWOSLIDES_API_KEY }, body: JSON.stringify({ prompt, slide_count: 12, theme_id: process.env.BRAND_THEME_ID }), }).then(r => r.json()) // Poll up to 3 minutes for (let i = 0; i < 36; i++) { await new Promise(r => setTimeout(r, 5000)) const job = await fetch(`https://2slides.com/api/v1/jobs/${gen.jobId}`, { headers: { 'x-api-key': process.env.TWOSLIDES_API_KEY }, }).then(r => r.json()) if (job.status === 'success') { return { deckUrl: job.result?.pptx_url } } if (job.status === 'failed') throw new Error(job.error || 'generation failed') } throw new Error('timeout') }

This pattern — CRM object → extension card → API call → inline result — is the same approach marketing teams use when they scale content production; see how marketing teams run AI presentation decks at scale.

Method 3: Scheduled Batch Generation

Not every deck needs to be generated on demand. For predictable pipeline reviews — every Monday morning, every quarterly business review, every MEDDPICC-stage refresh — a cron-scheduled batch is cheaper and more reliable than real-time webhooks.

The Pattern

Run a nightly job that queries HubSpot for every deal in a target stage, generates a fresh deck for each, and emails the AE a morning digest.

// /api/cron/nightly-deck-refresh.ts export const runtime = 'nodejs' export const maxDuration = 300 export async function GET(req: Request) { // Vercel Cron guards with CRON_SECRET if (req.headers.get('authorization') !== `Bearer ${process.env.CRON_SECRET}`) { return new Response('Unauthorized', { status: 401 }) } const deals = await hubspotClient.crm.deals.searchApi.doSearch({ filterGroups: [{ filters: [ { propertyName: 'dealstage', operator: 'EQ', value: 'proposal_sent' }, { propertyName: 'hs_lastmodifieddate', operator: 'GT', value: String(Date.now() - 86400000) }, ], }], properties: ['dealname', 'amount', 'competitor', 'product_interest', 'champion_title'], limit: 100, }) const jobs = await Promise.all(deals.results.map(async (deal) => { const prompt = buildDeckPrompt(deal) const res = await fetch('https://2slides.com/api/v1/slides/generate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': process.env.TWOSLIDES_API_KEY! }, body: JSON.stringify({ prompt, slide_count: 10, theme_id: process.env.BRAND_THEME_ID }), }).then(r => r.json()) return { dealId: deal.id, jobId: res.jobId, owner: deal.properties.hubspot_owner_id } })) await enqueueDeliveryJobs(jobs) // SQS / Upstash QStash / Trigger.dev return Response.json({ enqueued: jobs.length }) }

Schedule in

vercel.json
:

{ "crons": [ { "path": "/api/cron/nightly-deck-refresh", "schedule": "0 6 * * 1-5" } ] }

Six AM, weekdays. AE opens email, deck is waiting.

The Prompt Template That Converts CRM Fields to Deck Content

The single biggest determinant of deck quality is the prompt. Good prompts encode what a senior AE would say to a new hire: "when the company is X, with a champion in Y role, focused on Z outcome, build the deck like this." Paste-ready template:

function buildDeckPrompt({ deal, company, contact }: Ctx) { return ` Generate a 10-12 slide sales deck for a B2B software evaluation. ACCOUNT CONTEXT - Company: ${company.name} - Industry: ${company.industry ?? 'unspecified'} - Annual revenue band: ${company.annualrevenue ?? 'unspecified'} - Employees: ${company.numberofemployees ?? 'unspecified'} - Website: ${company.domain} DEAL CONTEXT - Deal stage: ${deal.dealstage} - Deal amount: $${deal.amount} - Product interest: ${deal.product_interest} - Priority use case: ${deal.primary_use_case} - Competitor shortlist: ${deal.competitors /* comma-separated */} - Evaluation timeline: ${deal.close_date} CHAMPION CONTEXT - Name: ${contact.firstname} ${contact.lastname} - Title: ${contact.jobtitle} - Reported priorities: ${contact.priorities} DECK STRUCTURE 1. Title slide — "${company.name} × <Your Brand>: ${deal.primary_use_case}" 2. Their world today — 3 bullets, specific to ${company.industry} 3. The cost of doing nothing — quantify using ${company.annualrevenue} band 4. Our approach — 3 pillars aligned to ${deal.primary_use_case} 5. Proof — 2 case studies from ${company.industry} (or adjacent) 6. Differentiation vs ${deal.competitors} — see battlecard section below 7. Implementation plan — 30/60/90 tuned to ${deal.close_date} 8. Commercial summary — range anchored to $${deal.amount} 9. Risks and mitigations 10. Next steps — aligned to ${deal.dealstage} TONE - Match audience: ${contact.jobtitle} - Formal if title includes VP, SVP, Chief, Director; conversational otherwise. - Every slide: one idea, one chart or one pull-quote, no walls of text. `.trim() }

Keep the template under version control. When marketing updates the messaging framework, you change one file and the next cron run picks it up.

Handling Competitive Alternatives

The

deal.competitors
field in HubSpot — whether it's a custom single-line-text field, a multi-checkbox, or a dropdown — is gold. Inject a battlecard block into the prompt conditionally:

const BATTLECARDS: Record<string, string> = { 'Competitor A': ` Competitor A positions on <their claim>. Counter: <your proof point> + <3-word tagline>. Landmine question to plant: "When was their last security audit published?" `, 'Competitor B': ` Competitor B leads with <their angle>. Counter: <your counter> — reference <customer name> switching case. Landmine: "Ask about their per-seat caps past 500 users." `, } function battlecardSection(competitorsCsv: string) { const names = competitorsCsv.split(',').map(s => s.trim()).filter(Boolean) if (!names.length) return '' return ` COMPETITIVE BATTLECARDS ${names.map(n => BATTLECARDS[n] ?? '').filter(Boolean).join('\n')} Use this to populate the Differentiation slide. Never name the competitor more than twice. `.trim() }

Append the result of

battlecardSection(deal.competitors)
to your prompt. The deck now carries AE-grade competitive framing — the kind that normally requires a thirty-minute call with the competitive intel lead.

Store battlecards in a database table rather than hard-coding them once you have more than ten. Your Head of Product Marketing can edit them without filing a PR.

Frequently Asked Questions

How do I stop HubSpot from generating duplicate decks on every property update?

Add a property named

last_deck_generated_at
to the deal object and include a filter in the HubSpot Workflow:
last_deck_generated_at
is unknown or more than 7 days ago. Your transformer function should write this timestamp back to HubSpot via the Deal API after a successful generation. This alone prevents 90% of wasted API calls.

What does this cost in 2Slides credits?

Each

/api/v1/slides/generate
call debits credits based on slide count and any add-ons (image generation, voice narration). For a typical 10-slide sales deck with branded imagery, budget 20–40 credits per deck. A RevOps team generating 500 decks per month lands in the $150–$400 range — well below the AE labor saved on a single enterprise deal.

Can I generate the deck in a language other than English?

Yes. Pass

language: 'de'
,
language: 'ja'
,
language: 'es'
, etc. in the
/api/v1/slides/generate
payload. The prompt template should also include a localized preamble when your champion's contact record shows a non-English-preferred language.

How do I pin the visual brand so every deck looks identical?

Use a locked

theme_id
in every generation request. Create your brand theme once in the 2Slides UI, copy its ID, and store it as
BRAND_THEME_ID
in your environment variables. The theme controls fonts, palette, logo placement, and master layouts — so content varies per account while the chrome stays pixel-identical.

What if the generation job fails?

Poll

/api/v1/jobs/{id}
and inspect
status
. On
failed
, the response contains
error
with a machine-readable code. Retry once with exponential backoff; if it fails a second time, post a Slack alert to the RevOps channel and fall back to a static template deck so the AE is not blocked before their call.

The Takeaway

HubSpot is not trying to be Salesforce, and that is fine — its simplicity is the feature. But simplicity also means it does not ship native document automation. The gap is a feature, not a bug, because it means the RevOps team that wires HubSpot to an AI presentation API owns the most valuable surface area in their sales tech stack: the deck that lands in front of the buyer.

The three methods above — workflow webhook, app card button, scheduled batch — cover the full range of generation triggers a sales team needs. Pick the one that matches your funnel stage. Discovery and Demo stages almost always want on-demand (the app card). Proposal and Negotiation stages benefit most from automation on stage change (the workflow webhook). Pipeline reviews and QBR prep are batch (the cron). Run all three and every deal in the pipeline has a fresh, CRM-truthful deck within arm's reach, every day.

Automate your sales decks from HubSpot — get a 2Slides API key and wire it into your next workflow in under a day.

About 2Slides

Create stunning AI-powered presentations in seconds. Transform your ideas into professional slides with 2slides AI Agent.

Try For Free