Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.propal.io/llms.txt

Use this file to discover all available pages before exploring further.

This guide shows you how to automatically create a Propal proposal when a deal reaches a specific stage in your CRM (HubSpot, Salesforce, Pipedrive, etc.).

Flow overview

  1. A deal reaches the “Proposal” stage in your CRM
  2. Your webhook/automation triggers a script
  3. The script creates (or finds) a lead in Propal
  4. Creates a proposal from a template
  5. Publishes it and sends the link back to the CRM

Prerequisites

  • An API key with scopes: leads:read, leads:write, proposals:read, proposals:write, templates:read
  • At least one proposal template in Propal

Step 1: Find or create the lead

First, check if the contact already exists in Propal. If not, create them.
async function findOrCreateLead(client, contactData) {
  // Search for existing lead by email
  const { data: existingLeads } = await client
    .request('/leads?search=' + encodeURIComponent(contactData.email))
    .then(r => r.json());

  if (existingLeads.length > 0) {
    return existingLeads[0];
  }

  // Create a new lead
  const response = await client.request('/leads', {
    method: 'POST',
    body: JSON.stringify({
      name: contactData.name,
      email: contactData.email,
      company: contactData.company,
      phone: contactData.phone,
    }),
  });

  return response.json();
}

Step 2: Pick a template

List your templates and select the right one:
async function getTemplate(client, category) {
  const response = await client.request(
    `/templates?category=${category}&limit=1`
  );
  const { data } = await response.json();

  if (data.length === 0) {
    throw new Error(`No template found for category: ${category}`);
  }

  return data[0];
}

Step 3: Create the proposal

async function createProposal(client, lead, template, dealName) {
  const slug = dealName
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-|-$/g, '');

  const response = await client.request('/proposals', {
    method: 'POST',
    body: JSON.stringify({
      template_id: template.id,
      title: dealName,
      slug: `${slug}-${Date.now()}`,
      lead_id: lead.id,
      language: 'en',
      settings: {
        allow_client_to_sign: true,
        allow_client_to_deny: true,
        allow_payment: true,
        allow_client_to_download_pdf: true,
      },
    }),
  });

  return response.json();
}

Step 4: Publish the proposal

async function publishProposal(client, proposalId) {
  await client.request(`/proposals/${proposalId}/publish`, {
    method: 'POST',
  });
}

Full example

import { PropalClient } from './propal-client.js';

const client = new PropalClient(process.env.PROPAL_API_KEY);

async function handleDealStageChange(deal) {
  // 1. Find or create the lead
  const lead = await findOrCreateLead(client, {
    name: deal.contact.name,
    email: deal.contact.email,
    company: deal.contact.company,
    phone: deal.contact.phone,
  });

  // 2. Get the right template
  const template = await getTemplate(client, 'custom');

  // 3. Create the proposal
  const proposal = await createProposal(
    client,
    lead,
    template,
    deal.name,
  );

  // 4. Publish it
  await publishProposal(client, proposal.id);

  // 5. Return the proposal URL to your CRM
  const proposalUrl = `https://app.propal.io/p/${proposal.id}`;

  console.log(`Proposal created and published: ${proposalUrl}`);
  return proposalUrl;
}

Connecting to your CRM

Use HubSpot Workflows to trigger a webhook when a deal enters the “Proposal” stage. Point the webhook to your server running the script above.Alternatively, use Propal’s native HubSpot integration for a no-code setup.
Create a Zap or Scenario that triggers when a deal stage changes in your CRM, then use the “Webhooks” action to call the Propal API directly.
Poll your CRM’s API for deal stage changes, or set up webhooks if your CRM supports them. Run the script above whenever a deal reaches the proposal stage.