Skip to main content
A booking rate is only useful if you know what it divides by. This page defines every figure GET /api/dashboard/analytics returns, so a number you show a client is a number you can explain.

The counts

inboundCalls and outboundCalls can sum to less than totalCalls, because a call recorded without a direction counts in neither, and calls from a sub-account that was archived before direction was recorded count only in totalCalls. Read each against totalCalls rather than assuming the two add up.

The rates

Two families, and the difference matters. Lead rates divide by totalLeads. Conversion rates divide by conversations, where a conversation is an answered call that lasted longer than 30 seconds. A conversion rate tells you how the agent does once it is actually talking to someone. A lead rate tells you how the whole campaign does per person dialled. On the dashboard, bookingRate is Lead to Booked and appointmentConversionRate is Appts / Conv (%). The same 10 bookings give two rates. Lead to Booked divides them by 100 leads, which is 10%. Appts / Conv divides them by 40 conversations, answered calls over 30 seconds, which is 25%. The same 10 bookings give two rates. Lead to Booked divides them by 100 leads, which is 10%. Appts / Conv divides them by 40 conversations, answered calls over 30 seconds, which is 25%. All rates are percentages, to two decimal places.

The window

dateFromUTC and dateToUTC set the reporting window. Send a UTC timestamp with Z or an offset, or a plain YYYY-MM-DD. A plain date covers that whole day. A timestamp without a zone is rejected rather than read as server time. The response carries window, the range actually used, as inclusive calendar days. Days, not instants: the underlying rows record the local calendar day the call happened, so a bound carrying a time is widened to cover its whole day.

Scoping the figures

Contacts you have marked as your own test leads are excluded from every figure here.

Per-project comparisons

Two endpoints return one row per project you can see, so an agency can compare clients:
  • GET /api/dashboard/booking-rate-by-project: project, leads, appointments, bookingRate.
  • GET /api/dashboard/transfer-rate-by-project: project, leads, transfers, transferRate.
Both take the same window and scope parameters, plus agency, projects and excludeProjects for agency-level filtering.

By lead age

GET /api/dashboard/analytics/lead-age returns the same figures split by how old each lead was when it reached the platform: the gap between the moment the sub-account’s CRM created the lead and the moment it was sent here. Each segment carries leads, calls, pickups, conversations, appointments, transfers, failedTransfers and attemptedTransfers, with pickupRate, conversationRate, bookingRate, transferRate and appointmentConversionRate on the same bases as above. totals adds the four segments up, and matches the summary for the same window and scope. A lead whose times cannot place it in exactly one segment goes to unknown, never to the nearest one. unknownReasons counts why: noLeadRecord, noCreationDate, noReceiptDate, createdAfterReceipt, or straddlesBoundary, when the CRM sent only a date and that date could fall on either side of a boundary. It takes the same parameters as the summary. A disconnected project’s archived totals are not included, because they carry no lead ages.

Boundaries

  • cached, queryTime and generatedAt describe the server, not your data. Do not build on them.
  • Not every call carries campaign attribution, so a campaignId filter cannot reach calls placed before that.
  • These endpoints need the dashboard:read scope.

In the API