Reporting API

Account Metrics

Six metrics for one account over a date range, in total and by lead source and by channel. leads and appointments are the account's own OnePath Dashboard counts.

curl -H "Authorization: Bearer YOUR_KEY" \
  "https://app.onepath.ai/api/reporting/v1/accounts/42/metrics?from=2026-08-01&to=2026-08-31"
{
  "account": { "id": 42, "name": "Wahoo Plumbing", "time_zone": "America/Chicago" },
  "period":  { "from": "2026-08-01", "to": "2026-08-31" },
  "metrics": {
    "leads": 120,
    "appointments": 48,
    "booking_rate": 40.0,
    "won_revenue": 96000.0,
    "average_ticket": 2000.0,
    "pipeline_revenue": 150000.0
  },
  "by_source": [
    {
      "source": { "id": 913, "handle": "yelp", "name": "Yelp", "kind": "platform", "category": "marketplace", "platform": "yelp", "archived": false },
      "metrics": { "leads": 40, "appointments": 18, "booking_rate": 45.0, "won_revenue": 36000.0, "average_ticket": 2000.0, "pipeline_revenue": 52000.0 }
    }
  ],
  "by_channel": [
    {
      "channel": { "type": "web_scheduler", "name": "Web Scheduler" },
      "metrics": { "leads": 75, "appointments": 30, "booking_rate": 40.0, "won_revenue": 60000.0, "average_ticket": 2000.0, "pipeline_revenue": 94000.0 }
    }
  ],
  "generated_at": "2026-09-03T10:00:00Z"
}

Take the id from /accounts. Numbers are numbers: rates are percentages as floats, money is US dollars as decimals. Nothing comes back as a formatted string. by_source and by_channel carry the same six metrics per row; see Breakdowns.

Dates

from and to are both required, in YYYY-MM-DD, and inclusive. A range may cover at most 366 days. Anything else is a 422 invalid_date_range.

Days start and end in the account's own time zone (the time_zone in the response), not yours and not UTC. A booking at 23:30 local time on to is included; one at 00:30 the next local morning is not. For a monthly report, ask for the first to the last day of the month and you get exactly that month as the contractor experienced it.

What each number means

KeyCountsNotes
leadsjob requestseach service a customer asked for, so a conversation asking for two services counts twice; the Dashboard's Job requests
appointmentsjob requests that were wonwon means the job was booked; the Dashboard's Booked jobs
booking_rateappointments ÷ leads × 100one decimal; 0.0 when there are no leads
won_revenuethe value of won opportunitiesUS dollars
average_ticketwon_revenue ÷ appointmentstwo decimals; 0.0 when there are no appointments
pipeline_revenuethe value of opportunities that are open, being followed up, handed to a person, or wonan estimate; the Dashboard doesn't show it

leads, appointments and booking_rate match what the account owner sees on their Dashboard for the same days. The three revenue metrics are opportunity estimates the Dashboard doesn't show. Its Realized Revenue comes from the account's field service software, so it won't match them.

Reporting on several accounts

There is no rollup endpoint. Call /accounts once, then this endpoint once per account. Add the totals yourself, and recompute the two rates from your sums rather than averaging them:

const key   = "YOUR_KEY";
const base  = "https://app.onepath.ai/api/reporting/v1";
const range = "from=2026-08-01&to=2026-08-31";
const headers = { Authorization: `Bearer ${key}` };

const { accounts } = await fetch(`${base}/accounts`, { headers }).then((r) => r.json());

const rows = await Promise.all(
  accounts.map((a) => fetch(`${base}/accounts/${a.id}/metrics?${range}`, { headers }).then((r) => r.json()))
);

const leads        = rows.reduce((sum, r) => sum + r.metrics.leads, 0);
const appointments = rows.reduce((sum, r) => sum + r.metrics.appointments, 0);
const bookingRate  = leads ? Math.round((appointments / leads) * 1000) / 10 : 0;

Results are cached for six hours per account and range, so a nightly job that walks every account is well within the rate limits.

Previous
Get started