Handle Service Compare To
CRM-9667 phase 2 — Δ and Impact for one page of the Services table.
The service-grain counterpart to /compare and
/companies/compare. Same shape and same timeframe vocabulary, and
the same reason for being a separate endpoint rather than an
extension of POST /filter: these values are computed per request
from two points in time, and that router sorts and filters over
real columns on the queried model.
Eight metrics — ARR, CARR, MRR, CMRR and their _local twins. No
ACV or TCV: both are contract-span figures with no per-service
reading that sums back to the contract's.
Unlike the other two grains, the current side is computed rather than read: per-service ARR and CARR are stored nowhere, so they are derived through the same code path the snapshot writer uses. That is what keeps a service's Δ and the figure beside it on the same row from disagreeing.
A service deleted between the page load and this call is reported
in notFoundRevenueItemIds rather than omitted, following the
company grain — silently dropping them makes a page of deleted
services indistinguishable from a page where nothing moved.
Body·
A Compare to request for one page of the Services table.
Row ids come from the caller for the reason the other two grains' requests
do: the comparison is a companion to the table's own paginated read, not a
replacement for it. The client already holds the page and asks only for the
movement on those rows, which keeps this out of the shared /filter router
— that one filters and sorts over real columns, and these values are
computed per request from two points in time.
- Type: stringenumperiodrequired
Comparison timeframe. MTD, QTD and YTD follow the account's fiscal calendar; last30Days and last12Months are counted back from today.
valueslast30Daysmtdqtdytdlast12Monthscustom - Type: array of string 1…200revenue
Item Ids requiredRevenue item IDs to compare, normally the current page. Capped so one request cannot fan out across a whole tenant.
- Type: stringFormat: datenullablecustom
Start Start date, required when period is 'custom' and ignored otherwise
Responses
- application/json
- application/json
curl https://crm.dreamhub.ai/api/v1/contracts/services/compare \
--request POST \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_SECRET_TOKEN' \
--data '{
"revenueItemIds": [
""
],
"period": "last30Days",
"customStart": ""
}'
{
"period": "string",
"periodStart": "2026-10-07T22:43:40.394Z",
"services": [
{
"revenueItemId": "string",
"existed": true,
"previousCapturedAt": "2026-10-07T22:43:40.394Z",
"previousSource": "string",
"currency": "string",
"previousCurrency": "string",
"contractHasFinancialOverride": false,
"metrics": {
"additionalProperty": {
"previous": 1,
"current": 1,
"delta": 1,
"impact": "new"
}
}
}
],
"notFoundRevenueItemIds": [
"string"
]
}
