REST endpoints for customer insights in ServFlo: 12 operations with parameters, request and response fields, permissions and curl examples.
Base URL: https://app.servflo.ai/api. Every call needs Authorization: Bearer sfk_… (Authentication ); {organizationId} ($ORGANIZATION_ID in the examples) is your business's id .
GET /organizations/{organizationId}/customers/{customerId}/insights
Customer 360 for one customer, computed from the business's own records: lifetime value, revenue in the last 12 months, average ticket, jobs and the last / next visit, membership, open balance and days to pay; a 0–100 health score with its drivers; behaviours with their evidence (books online, prefers text, pays late, price-sensitive, seasonal, member, promoter …); each piece of equipment's age and replacement outlook; and predictions (next service due, equipment replacement, lapse risk, late payment, each open quote's acceptance), each with likelihood, confidence, a due date or window, reasons and the data used. Use it to answer "why is this customer at risk" or "when are they due". Recomputed first when stale; nothing else changes. Technicians see only customers on their jobs and no money figures.
Permission customers.read (Owner, Admin, Dispatcher, Technician, Member)Risk read, OAuth scope servflo.read. Read: reads only.MCP tool insights_customer (toolset customers)
Name In Type Required Description customerIdpath string Yes
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /customers/{customerId}/insights" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description customerIdstring computedAtstring When it was computed (ISO). asOfstring The business day it was computed for (YYYY-MM-DD). versioninteger Rules version. summaryobject summary.lifetimeValueCentsinteger or null Collected, net of refunds, tips excluded. Null without invoice access. summary.revenue12mCentsinteger or null summary.averageTicketCentsinteger or null summary.jobCountinteger summary.completedJobCountinteger summary.lastVisitDatestring or null summary.nextVisitDatestring or null summary.membershipobject or null summary.membership.idstring summary.membership.identifierstring summary.membership.planNamestring summary.membership.statusstring summary.membership.endDatestring summary.membership.autoRenewboolean summary.openBalanceCentsinteger or null summary.averageDaysToPayinteger or null summary.customerSincestring healthobject health.scoreinteger 0–100; 70 is a typical customer. health.bandenum One of GOOD, WATCH, AT_RISK. health.driversarray of object health.drivers[].keystring health.drivers[].impactinteger health.drivers[].detailstring behavioursarray of object behaviours[].keyenum One of BOOKS_ONLINE, BOOKS_BY_PHONE, PREFERS_TEXT, PREFERS_EMAIL, RESPONSIVE, UNRESPONSIVE, PAYS_ON_TIME, PAYS_LATE, AUTOPAY, QUICK_APPROVER, PRICE_SENSITIVE, SEASONAL, MEMBER, REVIEWER, PROMOTER, DETRACTOR, HIGH_VALUE. behaviours[].evidencearray of string equipmentarray of object equipment[].assetIdstring equipment[].namestring equipment[].propertyIdstring or null equipment[].equipmentTypestring equipment[].typeInferredboolean The type was read from the name: set it on the equipment to be sure. equipment[].installYearinteger or null equipment[].ageYearsnumber or null equipment[].lifespanYearsinteger equipment[].lifeUsednumber or null Age divided by the lifespan. equipment[].refrigerantTypestring or null equipment[].conditionenum or null One of GOOD, FAIR, POOR, FAILED. equipment[].repairs24minteger equipment[].repairCost24mCentsinteger equipment[].likelihoodnumber equipment[].levelenum One of HIGH, MEDIUM, LOW. equipment[].outlookenum One of REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. predictionsarray of object predictions[].kindenum One of SERVICE_DUE, EQUIPMENT_REPLACEMENT, LAPSE_RISK, LATE_PAYMENT, QUOTE_ACCEPTANCE. predictions[].subjectTypeenum or null What subjectId is: equipment or a quote. One of asset, quote. predictions[].subjectIdstring The asset or quote id; empty for the customer. predictions[].likelihoodnumber 0–1: how likely (replacement, lapse, late payment, acceptance). Always 1 for SERVICE_DUE. predictions[].confidencenumber 0–1: how much data stands behind it. predictions[].levelenum One of HIGH, MEDIUM, LOW. predictions[].basisstring or null SERVICE_DUE: BOOKED, MEMBERSHIP, CADENCE, INTERVAL, SEASONAL or ANNUAL. Replacement: REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. Quote: its Q-n. predictions[].dueDatestring or null Yyyy-mm-dd: service due, end of life, the next expected visit, the next invoice due or the quote's expiry.predictions[].windowStartstring or null predictions[].windowEndstring or null predictions[].reasonsarray of string Plain-language reasons, most important first. predictions[].dataUsedarray of object The records and figures it used. predictions[].dataUsed[].labelstring predictions[].dataUsed[].valuestring linesarray of string The same in a few sentences.
GET /organizations/{organizationId}/customers/{customerId}/timeline
Everything that happened with a customer, newest first by when it happened in the business (not when the record was entered): jobs and visits by their scheduled or service day, quotes when sent (else created) / approved / declined, invoices by issue day, payments by paid day, messages, replies, Google reviews and notes by their own time, requests and memberships. at is that moment; dateOnly entries (imported or entered after the fact) know only the day (occurredOn); recordedAt is when the record was written and breaks ties. Each kind appears only when your role may read its records. Page with cursor (the nextCursor of the previous page; stable, pages never overlap). Read-only.
Permission customers.read (Owner, Admin, Dispatcher, Technician, Member)Risk read, OAuth scope servflo.read. Read: reads only.MCP tool insights_timeline (toolset customers)
Name In Type Required Description customerIdpath string Yes kindsquery string No Comma-separated kinds to include: JOB, QUOTE, INVOICE, PAYMENT, MESSAGE, REVIEW, NOTE, REQUEST, MEMBERSHIP, CALL. Default: all you may see. Up to 200 characters. cursorquery string No The nextCursor of the previous page: the entries after it. Up to 500 characters. beforequery string (date-time) No ISO time: only entries that happened before it. limitquery integer No 1–200. Default 50.
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /customers/{customerId}/timeline" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description itemsarray of object items[].idstring Unique per entry items[].atstring When it happened in the business (ISO) items[].occurredOnstring The business day (YYYY-MM-DD) items[].dateOnlyboolean Only the day is known (imported or entered after the fact) items[].recordedAtstring When the record was written (ISO) items[].kindenum One of JOB, QUOTE, INVOICE, PAYMENT, MESSAGE, REVIEW, NOTE, REQUEST, MEMBERSHIP, CALL. items[].actionstring items[].recordIdstring items[].identifierstring or null items[].titlestring items[].detailstring or null items[].amountCentsinteger or null nextCursorstring or null Pass as cursor for the next page; null on the last page.
POST /organizations/{organizationId}/customers/{customerId}/insights/recompute
Recomputes one customer's figures, behaviours and predictions from their records now (they are normally kept fresh automatically on every change and daily). Changes only the stored insight; nothing is sent. Returns the insight.
Permission customers.write (Owner, Admin, Dispatcher)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool insights_recomputeCustomer (toolset customers)
Name In Type Required Description customerIdpath string Yes
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /customers/{customerId}/insights/recompute" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description customerIdstring computedAtstring When it was computed (ISO). asOfstring The business day it was computed for (YYYY-MM-DD). versioninteger Rules version. summaryobject summary.lifetimeValueCentsinteger or null Collected, net of refunds, tips excluded. Null without invoice access. summary.revenue12mCentsinteger or null summary.averageTicketCentsinteger or null summary.jobCountinteger summary.completedJobCountinteger summary.lastVisitDatestring or null summary.nextVisitDatestring or null summary.membershipobject or null summary.membership.idstring summary.membership.identifierstring summary.membership.planNamestring summary.membership.statusstring summary.membership.endDatestring summary.membership.autoRenewboolean summary.openBalanceCentsinteger or null summary.averageDaysToPayinteger or null summary.customerSincestring healthobject health.scoreinteger 0–100; 70 is a typical customer. health.bandenum One of GOOD, WATCH, AT_RISK. health.driversarray of object health.drivers[].keystring health.drivers[].impactinteger health.drivers[].detailstring behavioursarray of object behaviours[].keyenum One of BOOKS_ONLINE, BOOKS_BY_PHONE, PREFERS_TEXT, PREFERS_EMAIL, RESPONSIVE, UNRESPONSIVE, PAYS_ON_TIME, PAYS_LATE, AUTOPAY, QUICK_APPROVER, PRICE_SENSITIVE, SEASONAL, MEMBER, REVIEWER, PROMOTER, DETRACTOR, HIGH_VALUE. behaviours[].evidencearray of string equipmentarray of object equipment[].assetIdstring equipment[].namestring equipment[].propertyIdstring or null equipment[].equipmentTypestring equipment[].typeInferredboolean The type was read from the name: set it on the equipment to be sure. equipment[].installYearinteger or null equipment[].ageYearsnumber or null equipment[].lifespanYearsinteger equipment[].lifeUsednumber or null Age divided by the lifespan. equipment[].refrigerantTypestring or null equipment[].conditionenum or null One of GOOD, FAIR, POOR, FAILED. equipment[].repairs24minteger equipment[].repairCost24mCentsinteger equipment[].likelihoodnumber equipment[].levelenum One of HIGH, MEDIUM, LOW. equipment[].outlookenum One of REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. predictionsarray of object predictions[].kindenum One of SERVICE_DUE, EQUIPMENT_REPLACEMENT, LAPSE_RISK, LATE_PAYMENT, QUOTE_ACCEPTANCE. predictions[].subjectTypeenum or null What subjectId is: equipment or a quote. One of asset, quote. predictions[].subjectIdstring The asset or quote id; empty for the customer. predictions[].likelihoodnumber 0–1: how likely (replacement, lapse, late payment, acceptance). Always 1 for SERVICE_DUE. predictions[].confidencenumber 0–1: how much data stands behind it. predictions[].levelenum One of HIGH, MEDIUM, LOW. predictions[].basisstring or null SERVICE_DUE: BOOKED, MEMBERSHIP, CADENCE, INTERVAL, SEASONAL or ANNUAL. Replacement: REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. Quote: its Q-n. predictions[].dueDatestring or null Yyyy-mm-dd: service due, end of life, the next expected visit, the next invoice due or the quote's expiry.predictions[].windowStartstring or null predictions[].windowEndstring or null predictions[].reasonsarray of string Plain-language reasons, most important first. predictions[].dataUsedarray of object The records and figures it used. predictions[].dataUsed[].labelstring predictions[].dataUsed[].valuestring linesarray of string The same in a few sentences.
GET /organizations/{organizationId}/insights/service-due
Who needs service, and when: each customer's predicted next service date (from a membership visit, their own cadence, the service type's interval or the season) within from–to (default: 30 days overdue to 60 days ahead), soonest first, with reasons. Customers with a visit already booked are left out unless includeBooked. Answers "who needs service next month". Read-only. Then POST /insights/bulk/requests, /insights/bulk/tag.
Permission customers.read (Owner, Admin, Dispatcher, Technician, Member)Risk read, OAuth scope servflo.read. Read: reads only.MCP tool insights_serviceDue (toolset customers)
Name In Type Required Description fromquery string No A calendar date in the business's own calendar, YYYY-MM-DD. toquery string No A calendar date in the business's own calendar, YYYY-MM-DD. includeBookedquery enum No true: include customers with a visit already booked. One of true, false. memberOnlyquery enum No true: active members only. One of true, false. basisquery enum No One of MEMBERSHIP, CADENCE, INTERVAL, SEASONAL, ANNUAL, BOOKED. serviceTypeIdquery string No Customers who have had this service. minConfidencequery number No 0–1. Only predictions with at least this much data behind them. limitquery integer No Page size, 1–200. Default 50. offsetquery integer No Rows to skip; the answer's total counts every match. Default 0.
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/service-due" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description todaystring fromstring tostring totalinteger itemsarray of object items[].kindenum One of SERVICE_DUE, EQUIPMENT_REPLACEMENT, LAPSE_RISK, LATE_PAYMENT, QUOTE_ACCEPTANCE. items[].subjectTypeenum or null What subjectId is: equipment or a quote. One of asset, quote. items[].subjectIdstring The asset or quote id; empty for the customer. items[].likelihoodnumber 0–1: how likely (replacement, lapse, late payment, acceptance). Always 1 for SERVICE_DUE. items[].confidencenumber 0–1: how much data stands behind it. items[].levelenum One of HIGH, MEDIUM, LOW. items[].basisstring or null SERVICE_DUE: BOOKED, MEMBERSHIP, CADENCE, INTERVAL, SEASONAL or ANNUAL. Replacement: REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. Quote: its Q-n. items[].dueDatestring or null Yyyy-mm-dd: service due, end of life, the next expected visit, the next invoice due or the quote's expiry.items[].windowStartstring or null items[].windowEndstring or null items[].reasonsarray of string Plain-language reasons, most important first. items[].dataUsedarray of object The records and figures it used. items[].dataUsed[].labelstring items[].dataUsed[].valuestring items[].customerIdstring items[].customerNamestring items[].phonestring or null items[].emailstring or null items[].healthScoreinteger or null items[].lastVisitDatestring or null items[].memberboolean items[].overdueboolean Due before today.
GET /organizations/{organizationId}/insights/replacements
Equipment most likely to need replacing, most likely first: age against its expected lifespan (the organization's lifespan table), repairs and their cost in the last 24 months, R-22 refrigerant, condition and notes, with reasons and the data used. Filter by likelihood (default 0.35), level, equipment type, minimum age, members. Read-only. Then POST /insights/bulk/quotes or /insights/bulk/tag.
Permission customers.read (Owner, Admin, Dispatcher, Technician, Member)Risk read, OAuth scope servflo.read. Read: reads only.MCP tool insights_replacements (toolset customers)
Name In Type Required Description minLikelihoodquery number No 0–1. Only predictions at least this likely. minConfidencequery number No 0–1. Only predictions with at least this much data behind them. levelquery enum No One of HIGH, MEDIUM, LOW. equipmentTypequery enum No One of AC, HEAT_PUMP, FURNACE, BOILER, MINI_SPLIT, AIR_HANDLER, PACKAGE_UNIT, WATER_HEATER, TANKLESS_WATER_HEATER, THERMOSTAT, HUMIDIFIER, AIR_PURIFIER, WATER_SOFTENER, SUMP_PUMP, WELL_PUMP, SEPTIC_PUMP, BACKFLOW_PREVENTER, ELECTRICAL_PANEL, GENERATOR, EV_CHARGER, GARAGE_DOOR_OPENER, POOL_PUMP, POOL_HEATER, REFRIGERATION, ICE_MACHINE, OTHER. minAgeYearsquery number No 0–100. memberOnlyquery enum No true: active members only. One of true, false. limitquery integer No Page size, 1–200. Default 50. offsetquery integer No Rows to skip; the answer's total counts every match. Default 0.
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/replacements" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description totalinteger itemsarray of object items[].kindenum One of SERVICE_DUE, EQUIPMENT_REPLACEMENT, LAPSE_RISK, LATE_PAYMENT, QUOTE_ACCEPTANCE. items[].subjectTypeenum or null What subjectId is: equipment or a quote. One of asset, quote. items[].subjectIdstring The asset or quote id; empty for the customer. items[].likelihoodnumber 0–1: how likely (replacement, lapse, late payment, acceptance). Always 1 for SERVICE_DUE. items[].confidencenumber 0–1: how much data stands behind it. items[].levelenum One of HIGH, MEDIUM, LOW. items[].basisstring or null SERVICE_DUE: BOOKED, MEMBERSHIP, CADENCE, INTERVAL, SEASONAL or ANNUAL. Replacement: REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. Quote: its Q-n. items[].dueDatestring or null Yyyy-mm-dd: service due, end of life, the next expected visit, the next invoice due or the quote's expiry.items[].windowStartstring or null items[].windowEndstring or null items[].reasonsarray of string Plain-language reasons, most important first. items[].dataUsedarray of object The records and figures it used. items[].dataUsed[].labelstring items[].dataUsed[].valuestring items[].customerIdstring items[].customerNamestring items[].phonestring or null items[].emailstring or null items[].assetIdstring items[].assetNamestring items[].manufacturerstring or null items[].modelstring or null items[].equipmentTypestring or null items[].installYearinteger or null items[].ageYearsnumber or null items[].lifespanYearsinteger or null items[].refrigerantTypestring or null items[].conditionstring or null items[].repairs24minteger items[].repairCost24mCentsinteger items[].propertyobject or null items[].property.idstring items[].property.namestring or null items[].property.addressstring or null items[].property.citystring or null
GET /organizations/{organizationId}/insights/predictions
Customers ranked by one prediction, most likely first: LAPSE_RISK (churn: overdue against their own rhythm, lapsed membership, declined quotes, bad reviews), LATE_PAYMENT (payment history; needs full invoice access) or QUOTE_ACCEPTANCE (each open quote). Filter by likelihood (default 0.35) and level. Read-only.
Permission customers.read (Owner, Admin, Dispatcher, Technician, Member)Risk read, OAuth scope servflo.read. Read: reads only.MCP tool insights_predictions (toolset customers)
Name In Type Required Description kindquery enum Yes One of LAPSE_RISK, LATE_PAYMENT, QUOTE_ACCEPTANCE. minLikelihoodquery number No 0–1. Only predictions at least this likely. minConfidencequery number No 0–1. Only predictions with at least this much data behind them. levelquery enum No One of HIGH, MEDIUM, LOW. memberOnlyquery enum No One of true, false. limitquery integer No Page size, 1–200. Default 50. offsetquery integer No Rows to skip; the answer's total counts every match. Default 0.
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/predictions?kind=LAPSE_RISK" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description totalinteger itemsarray of object items[].kindenum One of SERVICE_DUE, EQUIPMENT_REPLACEMENT, LAPSE_RISK, LATE_PAYMENT, QUOTE_ACCEPTANCE. items[].subjectTypeenum or null What subjectId is: equipment or a quote. One of asset, quote. items[].subjectIdstring The asset or quote id; empty for the customer. items[].likelihoodnumber 0–1: how likely (replacement, lapse, late payment, acceptance). Always 1 for SERVICE_DUE. items[].confidencenumber 0–1: how much data stands behind it. items[].levelenum One of HIGH, MEDIUM, LOW. items[].basisstring or null SERVICE_DUE: BOOKED, MEMBERSHIP, CADENCE, INTERVAL, SEASONAL or ANNUAL. Replacement: REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. Quote: its Q-n. items[].dueDatestring or null Yyyy-mm-dd: service due, end of life, the next expected visit, the next invoice due or the quote's expiry.items[].windowStartstring or null items[].windowEndstring or null items[].reasonsarray of string Plain-language reasons, most important first. items[].dataUsedarray of object The records and figures it used. items[].dataUsed[].labelstring items[].dataUsed[].valuestring items[].customerIdstring items[].customerNamestring
POST /organizations/{organizationId}/insights/recompute
Recomputes stale customer insights now (normally done on every change and daily by the worker), up to limit customers; all marks every customer for recomputation first (after changing many records). Changes only stored insights; nothing is sent. Answers how many were recomputed and how many remain stale.
Permission customers.write (Owner, Admin, Dispatcher)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool insights_recompute (toolset customers)
Field Type Required Description allboolean No Recompute everyone, not only stale ones. limitinteger No Customers per call (default 500). 1–2000.
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/recompute" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"all":true,"limit":1}'
200: the answer's fields Field Type Description addedinteger recomputedinteger remaininginteger
GET /organizations/{organizationId}/insights/lifespans
Expected lifespan in years per equipment type: the typical figure and the organization's own where it set one. Used by the replacement predictions. Read-only.
Permission customers.read (Owner, Admin, Dispatcher, Technician, Member)Risk read, OAuth scope servflo.read. Read: reads only.MCP tool insights_lifespans (toolset customers)
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/lifespans" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields An array of objects:
Field Type Description equipmentTypestring defaultYearsinteger yearsinteger overriddenboolean
PATCH /organizations/{organizationId}/insights/lifespans
Sets the organization's expected lifespan (years) for equipment types; years: null goes back to the typical figure. Every customer's insights are recomputed afterwards (in the background, or POST /insights/recompute). Returns the table.
Permission settings.manage (Owner, Admin)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool insights_setLifespans (toolset customers)
Field Type Required Description entriesarray of object Yes Up to 26 items. entries[].equipmentTypeenum Yes One of AC, HEAT_PUMP, FURNACE, BOILER, MINI_SPLIT, AIR_HANDLER, PACKAGE_UNIT, WATER_HEATER, TANKLESS_WATER_HEATER, THERMOSTAT, HUMIDIFIER, AIR_PURIFIER, WATER_SOFTENER, SUMP_PUMP, WELL_PUMP, SEPTIC_PUMP, BACKFLOW_PREVENTER, ELECTRICAL_PANEL, GENERATOR, EV_CHARGER, GARAGE_DOOR_OPENER, POOL_PUMP, POOL_HEATER, REFRIGERATION, ICE_MACHINE, OTHER. entries[].yearsinteger or null Yes 1–100.
curl -X PATCH "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/lifespans" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"entries":[{"equipmentType":"AC","years":1}]}'
200: the answer's fields An array of objects:
Field Type Description equipmentTypestring defaultYearsinteger yearsinteger overriddenboolean
POST /organizations/{organizationId}/insights/bulk/requests
From "Service due": creates one NEW service request (REQ-n) per customer at their primary property, with the predicted date as the preferred date and the reasons in the internal notes, for the office to schedule. Nothing is sent to the customer. One row per customer: CREATED, SKIPPED or FAILED with the reason. Emits request.created per request.
Permission requests.write (Owner, Admin, Dispatcher)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool insights_bulkRequests (toolset customers)
Field Type Required Description customerIdsarray of string Yes Customer ids from GET /insights/service-due or /insights/replacements, at most 100. Up to 100 items. descriptionstring or null No Default: "Service due around <date>". Up to 2000 characters. serviceTypeIdstring or null No
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/bulk/requests" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"customerIds":["customer_id"]}'
200: the answer's fields Field Type Description requestedinteger succeededinteger skippedinteger failedinteger resultsarray of object results[].customerIdstring results[].outcomeenum One of CREATED, UPDATED, SKIPPED, FAILED. results[].recordIdstring or null results[].identifierstring or null results[].reasonstring or null
POST /organizations/{organizationId}/insights/bulk/quotes
From "Likely replacements": creates one DRAFT quote (Q-n) per customer titled for the unit most likely to need replacing, valid validDays (default 30), with the reasons in the internal notes and an empty "Replacement" option to price. Nothing is sent. One row per customer: CREATED, SKIPPED (no equipment) or FAILED. Emits quote.created per quote.
Permission: quotes.write (Owner, Admin, Dispatcher, Technician (quotes they sold)); risk write (OAuth scope servflo.write).
Permission quotes.writeRisk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool insights_bulkQuotes (toolset customers)
Field Type Required Description customerIdsarray of string Yes Customer ids from GET /insights/service-due or /insights/replacements, at most 100. Up to 100 items. validDaysinteger No 1–365.
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/bulk/quotes" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"customerIds":["customer_id"]}'
200: the answer's fields Field Type Description requestedinteger succeededinteger skippedinteger failedinteger resultsarray of object results[].customerIdstring results[].outcomeenum One of CREATED, UPDATED, SKIPPED, FAILED. results[].recordIdstring or null results[].identifierstring or null results[].reasonstring or null
POST /organizations/{organizationId}/insights/bulk/tag
Adds a tag to each customer (UPDATED, or SKIPPED when already tagged). An automation whose audience is "tags contains <tag>" then reaches exactly these customers. Nothing is sent by this call. Emits customer.updated per customer changed.
Permission customers.write (Owner, Admin, Dispatcher)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool insights_bulkTag (toolset customers)
Field Type Required Description customerIdsarray of string Yes Customer ids from GET /insights/service-due or /insights/replacements, at most 100. Up to 100 items. tagstring Yes Up to 100 characters.
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /insights/bulk/tag" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"customerIds":["customer_id"],"tag":"string"}'
200: the answer's fields Field Type Description requestedinteger succeededinteger skippedinteger failedinteger resultsarray of object results[].customerIdstring results[].outcomeenum One of CREATED, UPDATED, SKIPPED, FAILED. results[].recordIdstring or null results[].identifierstring or null results[].reasonstring or null