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 how to use the Propal Metrics API to build automated reports and custom dashboards.

Overview

The Metrics API provides read-only access to your organization’s analytics:
EndpointWhat it returns
/metrics/overviewGlobal KPIs — total proposals, total value, conversion rate
/metrics/pipelineProposals by deal status (pending, approved, denied)
/metrics/conversionConversion rate and funnel data
/metrics/salesSales data by period (daily, weekly, hourly)
/metrics/team-performancePerformance per team member
/metrics/proposal-timingAverage time to close, first view
/metrics/rejection-reasonsWhy proposals get rejected

Quick example: weekly report

async function generateWeeklyReport(client) {
  const [overview, pipeline, conversion, sales, timing] = await Promise.all([
    client.request('/metrics/overview').then(r => r.json()),
    client.request('/metrics/pipeline').then(r => r.json()),
    client.request('/metrics/conversion').then(r => r.json()),
    client.request('/metrics/sales?period=weekly').then(r => r.json()),
    client.request('/metrics/proposal-timing').then(r => r.json()),
  ]);

  return {
    generatedAt: new Date().toISOString(),
    kpis: overview.data,
    pipeline: pipeline.data,
    conversionRate: conversion.data,
    weeklySales: sales.data,
    averageClosingTime: timing.data,
  };
}

Building a Slack report bot

Send a weekly summary to Slack every Monday:
import cron from 'node-cron';

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

// Every Monday at 9 AM
cron.schedule('0 9 * * 1', async () => {
  const report = await generateWeeklyReport(client);

  const message = formatSlackMessage(report);
  await sendToSlack(process.env.SLACK_WEBHOOK_URL, message);
});

function formatSlackMessage(report) {
  const kpis = report.kpis;
  return {
    blocks: [
      {
        type: 'header',
        text: { type: 'plain_text', text: 'Weekly Propal Report' },
      },
      {
        type: 'section',
        fields: [
          { type: 'mrkdwn', text: `*Total Proposals*\n${kpis?.total_proposals ?? 0}` },
          { type: 'mrkdwn', text: `*Conversion Rate*\n${report.conversionRate?.conversion_rate ?? 'N/A'}%` },
          { type: 'mrkdwn', text: `*Avg. Close Time*\n${report.averageClosingTime?.avg_closing_time ?? 'N/A'} days` },
        ],
      },
    ],
  };
}

Building a custom dashboard

Fetch all metrics and display them in your own dashboard:
// API client for your dashboard backend
async function getDashboardData(client) {
  const endpoints = [
    { key: 'overview', path: '/metrics/overview' },
    { key: 'pipeline', path: '/metrics/pipeline' },
    { key: 'conversion', path: '/metrics/conversion' },
    { key: 'dailySales', path: '/metrics/sales?period=daily' },
    { key: 'weeklySales', path: '/metrics/sales?period=weekly' },
    { key: 'teamPerformance', path: '/metrics/team-performance' },
    { key: 'timing', path: '/metrics/proposal-timing' },
    { key: 'rejections', path: '/metrics/rejection-reasons' },
  ];

  const results = await Promise.all(
    endpoints.map(async ({ key, path }) => {
      const response = await client.request(path);
      const { data } = await response.json();
      return [key, data];
    })
  );

  return Object.fromEntries(results);
}

Combining metrics with proposals

For richer reports, combine metrics with actual proposal data:
async function getTopPerformingProposals(client) {
  const { data: proposals } = await client
    .request('/proposals?status=approved&limit=10')
    .then(r => r.json());

  return proposals.map(p => ({
    title: p.title,
    lead: p.lead_name,
    value: p.opportunity,
    currency: p.currency,
    acceptedAt: p.accepted_at,
    daysToClose: p.accepted_at && p.proposal_created_at
      ? Math.round(
          (new Date(p.accepted_at) - new Date(p.proposal_created_at)) /
          (1000 * 60 * 60 * 24)
        )
      : null,
  }));
}

Exporting to Google Sheets

Use the Google Sheets API to push your Propal data into a spreadsheet:
async function exportToSheets(client, spreadsheetId) {
  // Fetch all proposals
  const proposals = await fetchAllProposals(client);

  // Format as rows
  const rows = proposals.map(p => [
    p.title,
    p.lead_name,
    p.deal_status,
    p.opportunity,
    p.currency,
    p.proposal_created_at,
    p.accepted_at || '',
  ]);

  // Push to Google Sheets using their API
  await sheets.spreadsheets.values.update({
    spreadsheetId,
    range: 'Proposals!A2',
    valueInputOption: 'RAW',
    resource: { values: rows },
  });
}

Best practices

  • Cache metrics. Most metrics don’t change frequently. Cache them for 5-15 minutes to reduce API calls.
  • Use parallel requests. Fetch multiple metric endpoints simultaneously with Promise.all().
  • Required scope. All metrics endpoints require the metrics:read scope. You don’t need write access.
  • Time ranges. Some endpoints support period parameters (daily, weekly, hourly) to control the granularity.