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:
| Endpoint | What it returns |
|---|
/metrics/overview | Global KPIs — total proposals, total value, conversion rate |
/metrics/pipeline | Proposals by deal status (pending, approved, denied) |
/metrics/conversion | Conversion rate and funnel data |
/metrics/sales | Sales data by period (daily, weekly, hourly) |
/metrics/team-performance | Performance per team member |
/metrics/proposal-timing | Average time to close, first view |
/metrics/rejection-reasons | Why 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.