REST endpoints for sales presenter in ServFlo: 9 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}/presentation-templates
The business's in-home presentation templates (Settings → Sales presenter): sections in order with their copy and images, company story, trust badges, reviews on/off and the membership add-on; one may be the default and others belong to business units. With none, the built-in template is used. Read-only.
Permission: quotes.read (Owner, Admin, Dispatcher, Technician (quotes they sold), Member); risk read (OAuth scope servflo.read).
Permission quotes.readRisk read, OAuth scope servflo.read. Read: reads only.MCP tool presentationTemplates_list (toolset sales)
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /presentation-templates" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description itemsarray of object items[].idstring items[].namestring items[].businessUnitIdstring or null items[].businessUnitobject or null items[].businessUnit.idstring items[].businessUnit.namestring items[].isDefaultboolean items[].contentobject items[].content.sectionsarray of object items[].content.companyStorystring or null items[].content.trustBadgesarray of object items[].content.showReviewsboolean items[].content.membershipPlanIdstring or null items[].createdAtstring (date-time) items[].updatedAtstring (date-time)
POST /organizations/{organizationId}/presentation-templates
A template for the in-home presentation. The first one for the whole business becomes the default. 409 on a duplicate name. Customer-visible on the next presentation.
Permission presentations.manage (Owner, Admin)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool presentationTemplates_create (toolset sales)
Field Type Required Description namestring Yes Up to 120 characters. businessUnitIdstring or null No A business unit whose quotes use this template; null = the whole business. isDefaultboolean No The business's default template (one at most). contentobject No Sections in order (INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL; APPROVAL stays on) with title, copy and image; the company story; trust badges (licensed, insured…); whether to show reviews; the membership plan offered as an add-on. content.sectionsarray of object Yes Up to 5 items. content.sections[].stepenum Yes One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. content.sections[].enabledboolean No Default true. content.sections[].titlestring or null No Up to 120 characters. content.sections[].bodystring or null No Up to 4000 characters. content.sections[].imageUrlstring or null No Up to 2000 characters. content.companyStorystring or null No Up to 4000 characters. content.trustBadgesarray of object No Up to 8 items. Default []. content.trustBadges[].labelstring Yes Up to 80 characters. content.trustBadges[].detailstring or null No Up to 200 characters. content.showReviewsboolean No Default true. content.membershipPlanIdstring or null No
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /presentation-templates" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"name":"Dana Whitfield"}'
201: the answer's fields Field Type Description idstring namestring businessUnitIdstring or null businessUnitobject or null businessUnit.idstring businessUnit.namestring isDefaultboolean contentobject content.sectionsarray of object content.sections[].stepenum One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. content.sections[].enabledboolean content.sections[].titlestring or null content.sections[].bodystring or null content.sections[].imageUrlstring or null content.companyStorystring or null content.trustBadgesarray of object content.trustBadges[].labelstring content.trustBadges[].detailstring or null content.showReviewsboolean content.membershipPlanIdstring or null createdAtstring (date-time) updatedAtstring (date-time)
PATCH /organizations/{organizationId}/presentation-templates/{templateId}
Change a template's sections, copy, images, badges, reviews switch, membership add-on, unit or default flag. Customer-visible on the next presentation.
Permission presentations.manage (Owner, Admin)Risk write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.MCP tool presentationTemplates_update (toolset sales)
Name In Type Required Description templateIdpath string Yes
Field Type Required Description namestring No Up to 120 characters. businessUnitIdstring or null No A business unit whose quotes use this template; null = the whole business. isDefaultboolean No The business's default template (one at most). contentobject No Sections in order (INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL; APPROVAL stays on) with title, copy and image; the company story; trust badges (licensed, insured…); whether to show reviews; the membership plan offered as an add-on. content.sectionsarray of object Yes Up to 5 items. content.sections[].stepenum Yes One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. content.sections[].enabledboolean No Default true. content.sections[].titlestring or null No Up to 120 characters. content.sections[].bodystring or null No Up to 4000 characters. content.sections[].imageUrlstring or null No Up to 2000 characters. content.companyStorystring or null No Up to 4000 characters. content.trustBadgesarray of object No Up to 8 items. Default []. content.trustBadges[].labelstring Yes Up to 80 characters. content.trustBadges[].detailstring or null No Up to 200 characters. content.showReviewsboolean No Default true. content.membershipPlanIdstring or null No
curl -X PATCH "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /presentation-templates/{templateId}" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"name":"Dana Whitfield","businessUnitId":"businessUnit_id","isDefault":true}'
200: the answer's fields Field Type Description idstring namestring businessUnitIdstring or null businessUnitobject or null businessUnit.idstring businessUnit.namestring isDefaultboolean contentobject content.sectionsarray of object content.sections[].stepenum One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. content.sections[].enabledboolean content.sections[].titlestring or null content.sections[].bodystring or null content.sections[].imageUrlstring or null content.companyStorystring or null content.trustBadgesarray of object content.trustBadges[].labelstring content.trustBadges[].detailstring or null content.showReviewsboolean content.membershipPlanIdstring or null createdAtstring (date-time) updatedAtstring (date-time)
DELETE /organizations/{organizationId}/presentation-templates/{templateId}
Destructive: removes the template; its quotes fall back to the default (or the built-in one). Past presentations keep their analytics.
Permission presentations.manage (Owner, Admin)Risk sensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.MCP tool presentationTemplates_delete (toolset sales)Approval Over MCP, this tool always waits for your approval in ServFlo. See Approvals .
Name In Type Required Description templateIdpath string Yes
curl -X DELETE "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /presentation-templates/{templateId}" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description oktrue
GET /organizations/{organizationId}/jobs/{jobId}/presentable-quotes
The job's customer's quotes that can still be presented (draft, sent, changes requested, expired), newest first; a technician sees the ones they sold. Pick one, then GET /quotes/{quoteId}/presentation?jobId=… Read-only.
Permission: quotes.read (Owner, Admin, Dispatcher, Technician (quotes they sold), Member); risk read (OAuth scope servflo.read).
Permission quotes.readRisk read, OAuth scope servflo.read. Read: reads only.MCP tool presentations_forJob (toolset sales)
Name In Type Required Description jobIdpath string Yes
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /jobs/{jobId}/presentable-quotes" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description jobIdstring customerIdstring itemsarray of object items[].idstring items[].identifierstring items[].titlestring or null items[].statusstring items[].createdAtstring (date-time) items[].optionCountinteger
GET /organizations/{organizationId}/quotes/{quoteId}/presentation
Everything the tablet presentation shows for one quote: branding (the unit's), the template's steps and copy, reviews and trust badges, the job's photos and notes with the equipment's age and replacement outlook (pass jobId), the good / better / best options with images, features, warranty and prices (as a member with the add-on, and "as low as" per month when financing is on, with the disclosure), and the approval terms. A technician sees only quotes they sold. Read-only.
Permission: quotes.read (Owner, Admin, Dispatcher, Technician (quotes they sold), Member); risk read (OAuth scope servflo.read).
Permission quotes.readRisk read, OAuth scope servflo.read. Read: reads only.MCP tool presentations_get (toolset sales)
Name In Type Required Description quoteIdpath string Yes jobIdquery string No
curl "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /quotes/{quoteId}/presentation" \
-H "Authorization: Bearer $SERVFLO_API_KEY "
200: the answer's fields Field Type Description quoteobject quote.idstring quote.identifierstring quote.titlestring or null quote.messagestring or null quote.statusstring quote.canApproveboolean quote.publicTokenstring or null For the card deposit: /q/{token}. quote.validUntilstring or null quote.taxRatenumber quote.depositPercentnumber or null quote.depositCentsinteger or null quote.businessUnitIdstring or null quote.customerobject quote.customer.idstring quote.customer.namestring quote.propertyobject or null quote.property.idstring quote.property.namestring or null quote.property.addressstring or null quote.property.citystring or null brandingobject branding.businessNamestring branding.unitNamestring or null branding.phonestring or null branding.emailstring or null branding.logostring or null branding.footerstring or null templateobject template.idstring or null template.namestring template.sectionsarray of object template.sections[].stepenum One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. template.sections[].enabledboolean template.sections[].titlestring or null template.sections[].bodystring or null template.sections[].imageUrlstring or null stepsarray of enum The steps shown, in order (empty findings / financing are skipped). introobject intro.companyStorystring or null intro.trustBadgesarray of object intro.trustBadges[].labelstring intro.trustBadges[].detailstring or null intro.licenseTextstring or null intro.reviewsarray of object intro.reviews[].quotestring intro.reviews[].authorstring intro.reviews[].ratingnumber intro.reviews[].sourceenum One of GOOGLE, WEBSITE. intro.ratingobject or null intro.rating.averagenumber intro.rating.countinteger findingsobject findings.jobobject or null findings.job.idstring findings.job.identifierstring findings.job.servicestring or null findings.job.scheduledDatestring findings.job.notesstring or null findings.job.completionNotesstring or null findings.notesarray of object findings.notes[].contentstring findings.notes[].createdAtstring (date-time) findings.photosarray of object findings.photos[].idstring findings.photos[].urlstring findings.photos[].tagstring or null findings.photos[].fileNamestring findings.equipmentarray of object findings.equipment[].assetIdstring findings.equipment[].namestring findings.equipment[].equipmentTypestring findings.equipment[].installYearinteger or null findings.equipment[].ageYearsnumber or null findings.equipment[].lifespanYearsinteger findings.equipment[].lifeUsednumber or null findings.equipment[].conditionstring or null findings.equipment[].outlookenum One of REPLACE_NOW, PLAN, HEALTHY, UNKNOWN_AGE. findings.equipment[].repairs24minteger findings.equipment[].replaceBystring or null optionsarray of object options[].idstring options[].namestring options[].descriptionstring or null options[].tierenum or null One of GOOD, BETTER, BEST. options[].imageUrlstring or null options[].highlightsarray of string options[].warrantystring or null options[].recommendedboolean options[].linesarray of object options[].lines[].idstring options[].lines[].descriptionstring options[].lines[].quantitynumber options[].lines[].unitPriceCentsinteger options[].lines[].optionalboolean options[].lines[].defaultSelectedboolean options[].lines[].kindstring or null options[].lines[].memberPriceAppliedboolean Priced at the item's member price: the member discount skips it. options[].pricingobject options[].pricing.totalCentsinteger options[].pricing.depositCentsinteger options[].pricing.memberobject or null With the membership add-on: the total as a member, the saving and the plan's price. options[].pricing.monthlyCentsinteger or null "As low as" per month over months (an illustration, subject to approval); null when it cannot be financed. options[].pricing.monthsinteger featuresarray of object features[].featurestring features[].includedarray of boolean membershipobject membership.alreadyMemberboolean membership.currentPlanNamestring or null membership.planobject or null membership.plan.idstring membership.plan.namestring membership.plan.descriptionstring or null membership.plan.priceCentsinteger membership.plan.billingFrequencystring membership.plan.termMonthsinteger membership.plan.serviceDiscountPercentnumber membership.plan.materialDiscountPercentnumber financingobject financing.availableboolean financing.methodsarray of string financing.monthsinteger financing.disclosurestring approvalobject approval.termsstring approval.depositRequiredboolean
POST /organizations/{organizationId}/quotes/{quoteId}/presentations
Records that the signed-in person started presenting the quote (for the close-rate and time-per-step analytics). Returns the presentation to report steps to.
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 presentations_start (toolset sales)
Name In Type Required Description quoteIdpath string Yes
Field Type Required Description jobIdstring or null No
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /quotes/{quoteId}/presentations" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"jobId":"job_id"}'
201: the answer's fields Field Type Description idstring quoteIdstring jobIdstring or null templateIdstring or null presenterUserIdstring technicianIdstring or null outcomeenum One of OPEN, WON, LOST. stepSecondsobject currentStepenum or null One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. optionsViewedarray of string chosenOptionIdstring or null chosenPositioninteger or null optionCountinteger membershipAddedboolean membershipIdstring or null financingViewedboolean lostReasonstring or null startedAtstring (date-time) lastActivityAtstring (date-time) completedAtstring (date-time) or null
PATCH /organizations/{organizationId}/quotes/{quoteId}/presentations/{presentationId}
Adds seconds spent per step since the last report (capped per step), the current step, options viewed and whether financing was shown; lost ends it without approval (with a reason). Ignored once it ended.
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 presentations_track (toolset sales)
Name In Type Required Description quoteIdpath string Yes presentationIdpath string Yes
Field Type Required Description addSecondsobject No Seconds spent on each step, e.g. { INTRO: 40, OPTIONS: 180 }. currentStepenum or null No One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. viewedOptionIdsarray of string No Up to 10 items. financingViewedboolean No lostobject or null No lost.reasonstring or null No Up to 500 characters.
curl -X PATCH "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /quotes/{quoteId}/presentations/{presentationId}" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"addSeconds":{},"currentStep":"INTRO","viewedOptionIds":["viewedOption_id"]}'
200: the answer's fields Field Type Description idstring quoteIdstring jobIdstring or null templateIdstring or null presenterUserIdstring technicianIdstring or null outcomeenum One of OPEN, WON, LOST. stepSecondsobject currentStepenum or null One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. optionsViewedarray of string chosenOptionIdstring or null chosenPositioninteger or null optionCountinteger membershipAddedboolean membershipIdstring or null financingViewedboolean lostReasonstring or null startedAtstring (date-time) lastActivityAtstring (date-time) completedAtstring (date-time) or null
POST /organizations/{organizationId}/quotes/{quoteId}/presentations/{presentationId}/complete
Acts for the customer: with membership, signs them up for the template's membership plan (its agreement signed with the same signature) and applies member pricing; then approves the chosen option with the signature through the existing quote approval (terms, snapshot, device) and records the presentation as won. Take the deposit afterwards with POST /quotes/{quoteId}/deposits (cash, check) or the customer's card on /q/{token}.
Permission: quotes.approve (Owner, Admin, Dispatcher, Technician (quotes they sold)); risk sensitive (OAuth scope servflo.sensitive).
Permission quotes.approveRisk sensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.MCP tool presentations_complete (toolset sales)Approval Over MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals .
Name In Type Required Description quoteIdpath string Yes presentationIdpath string Yes
Field Type Required Description optionIdstring Yes selectedLineIdsarray of string No Up to 200 items. signatureNamestring Yes Up to 200 characters. signatureDataUrlstring or null No The signature drawn on the tablet: a data:image/png;base64 URL. termsstring or null No The quote terms shown. Up to 5000 characters. membershipobject or null No membership.planIdstring Yes The template's membership plan. membership.termsstring Yes The membership agreement shown.
curl -X POST "https://app.servflo.ai/api/organizations/ $ORGANIZATION_ID /quotes/{quoteId}/presentations/{presentationId}/complete" \
-H "Authorization: Bearer $SERVFLO_API_KEY " \
-H "Content-Type: application/json" \
-d '{"optionId":"option_id","signatureName":"string"}'
200: the answer's fields Field Type Description presentationobject presentation.idstring presentation.quoteIdstring presentation.jobIdstring or null presentation.templateIdstring or null presentation.presenterUserIdstring presentation.technicianIdstring or null presentation.outcomeenum One of OPEN, WON, LOST. presentation.stepSecondsobject presentation.currentStepenum or null One of INTRO, FINDINGS, OPTIONS, FINANCING, APPROVAL. presentation.optionsViewedarray of string presentation.chosenOptionIdstring or null presentation.chosenPositioninteger or null presentation.optionCountinteger presentation.membershipAddedboolean presentation.membershipIdstring or null presentation.financingViewedboolean presentation.lostReasonstring or null presentation.startedAtstring (date-time) presentation.lastActivityAtstring (date-time) presentation.completedAtstring (date-time) or null quoteobject quote.idstring quote.identifierstring quote.statusstring quote.approvedTotalCentsinteger or null quote.approvedDepositCentsinteger or null quote.publicTokenstring or null membershipIdstring or null