DevelopersAPI reference

Invoices API

REST endpoints for invoices in ServFlo: 22 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.

OperationEndpointRisk
List invoicesGET /organizations/{organizationId}/invoicesread
Create a draft invoicePOST /organizations/{organizationId}/invoiceswrite
Get an invoice with its lines and paymentsGET /organizations/{organizationId}/invoices/{invoiceId}read
Update an invoice's status, notes or datesPATCH /organizations/{organizationId}/invoices/{invoiceId}sensitive
Delete an invoiceDELETE /organizations/{organizationId}/invoices/{invoiceId}sensitive
Remove or re-apply member pricing on a draft invoicePOST /organizations/{organizationId}/invoices/{invoiceId}/member-pricingwrite
Jobs ready to invoiceGET /organizations/{organizationId}/invoice-batches/jobsread
Preview a batch of invoicesGET /organizations/{organizationId}/invoice-batches/previewread
Create invoices for many jobsPOST /organizations/{organizationId}/invoice-batcheswrite
Send many invoicesPOST /organizations/{organizationId}/invoice-batches/sendsensitive
Send payment reminders for many invoicesPOST /organizations/{organizationId}/invoice-batches/remindsensitive
Mark many invoices as sentPOST /organizations/{organizationId}/invoice-batches/mark-sentwrite
List progress billingsGET /organizations/{organizationId}/progress-billingsread
Bill a quote or job in progress invoicesPOST /organizations/{organizationId}/progress-billingswrite
Get a progress billingGET /organizations/{organizationId}/progress-billings/{progressBillingId}read
Change a progress billingPATCH /organizations/{organizationId}/progress-billings/{progressBillingId}write
Stop billing in progress invoicesDELETE /organizations/{organizationId}/progress-billings/{progressBillingId}sensitive
Create a progress invoicePOST /organizations/{organizationId}/progress-billings/{progressBillingId}/invoiceswrite
Create the final invoicePOST /organizations/{organizationId}/progress-billings/{progressBillingId}/final-invoicewrite
Invoice the retainage heldPOST /organizations/{organizationId}/progress-billings/{progressBillingId}/retainage-releasewrite
Get or create the invoice's customer linkPOST /organizations/{organizationId}/invoices/{invoiceId}/linksensitive
Replace the invoice's customer linkPOST /organizations/{organizationId}/invoices/{invoiceId}/link/regeneratesensitive

List invoices

GET /organizations/{organizationId}/invoices

Invoices newest first, filtered by text (INV-n, customer), status or customer, paged with a total. Each item carries amountPaidCents, balanceDueCents and partiallyPaid. Read-only.

Permission: invoices.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices), Member); risk read (OAuth scope servflo.read).

Permissioninvoices.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolinvoices_list (toolset invoices, in the default set)

Parameters

NameInTypeRequiredDescription
queryquerystringNoFree-text search over the list's names, numbers and contact details. Up to 200 characters.
statusqueryenumNoOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
customerIdquerystringNo
businessUnitIdquerystringNoOnly this business unit (GET /business-units), or "none" for records without one. Up to 100 characters.
limitqueryintegerNoPage size, 1–200. Default 50.
offsetqueryintegerNoRows to skip; the answer's total counts every match. Default 0.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
itemsarray of object
items[].idstringInvoice id.
items[].organizationIdstring
items[].identifierstringINV-n, unique per organization.
items[].customerIdstring
items[].jobIdstring or null
items[].statusenumDRAFT (not issued), SENT (issued, money due), PAID, OVERDUE (past dueDate) or CANCELLED. One of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
items[].subtotalCentsintegerLines total before tax, in integer cents.
items[].taxRatenumberTax rate in percent.
items[].taxCentsintegerTax, in integer cents.
items[].totalCentsintegerTotal with tax, in integer cents.
items[].notesstring or null
items[].dueDatestring or nullYYYY-MM-DD
items[].paidDatestring or nullYYYY-MM-DD
items[].amountPaidCentsintegerPaid so far, net of refunds, tips excluded, in integer cents.
items[].membershipIdstring or nullSet on a membership's billing invoice.
items[].memberPricingRemovedboolean
items[].financingDisabledboolean
items[].progressBillingIdstring or null
items[].progressRoleenum or nullIts place in a progress billing. One of PROGRESS, FINAL, RETAINAGE_RELEASE.
items[].progressMilestoneIdstring or null
items[].progressBilledCentsintegerBilled towards the contract on a progress invoice, in integer cents.
items[].retainageCentsintegerRetainage held on a progress invoice, in integer cents.
items[].businessUnitIdstring or null
items[].customFieldsanyCustom field values by key (definitions: GET /custom-fields?entity=INVOICE).
items[].createdAtstring (date-time)
items[].updatedAtstring (date-time)
items[].customerobject
items[].customer.idstring
items[].customer.namestring
items[].businessUnitobject or nullThe business unit.
items[].businessUnit.idstring
items[].businessUnit.namestring
items[].jobobject or null
items[].job.idstring
items[].job.identifierstringJOB-n
items[].job.signedBystring or nullWork accepted by (the job's sign-off).
items[].job.signedAtstring (date-time) or null
items[].balanceDueCentsintegerStill due, in integer cents.
items[].partiallyPaidboolean
totalintegerHow many match the filters, across all pages.

Create a draft invoice

POST /organizations/{organizationId}/invoices

Creates a DRAFT invoice (INV-n) for a customer with the given lines; amounts are fixed once created. Tax is 0 for tax-exempt customers, else taxRate. MEMBER PRICING: when the customer has an active membership covering the job's property, the plan's discount is added as separate negative lines (services and materials apart) unless memberPricing: false. Nothing is sent: share it with POST /invoices/{invoiceId}/link and mark it SENT with PATCH. To bill a job's work use POST /jobs/{jobId}/invoice instead.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolinvoices_create (toolset invoices, in the default set)

Request body

FieldTypeRequiredDescription
customerIdstringYesFrom GET /customers.
jobIdstring or nullNoThe job this bills, if any.
taxRatenumberNoPercent, e.g. 8.25. Ignored for tax-exempt customers. -1000–1000.
notesstring or nullNoPrinted on the invoice; the customer sees it.
dueDateempty string or string or nullNoDefaults from the customer's payment terms.
linesarray of objectYesUp to 500 items.
lines[].descriptionstringYes
lines[].quantitynumberYes-1000000–1000000.
lines[].unitPriceCentsintegerYesMoney in integer cents; negative for a discount or credit line. -100000000–100000000.
lines[].kindenum or nullNoFor member pricing: SERVICE (labour, the default) or MATERIAL get the member discount for their kind; MEMBERSHIP (a plan fee) is never discounted. Discount lines are added by the server. One of SERVICE, MATERIAL, MEMBERSHIP.
lines[].pricebookItemIdstring or nullNoThe pricebook item the line was picked from (GET /pricebook/items): the server snapshots its cost; pricebook edits never change the invoice.
lines[].memberPriceAppliedboolean or nullNoPriced at the item's member price: the plan's member discount skips this line. Kept only for items with a member price.
memberPricingbooleanNoDefault true: a member customer's discount lines are added. false leaves them off (staff removed member pricing).
businessUnitIdstring or nullNoBusiness unit (GET /business-units): its branding shows on the invoice and its accounting class or tracking applies. Omitted: the job's unit; null = none. Up to 100 characters.
customFieldsobjectNoInvoice custom field values (GET /custom-fields?entity=INVOICE); those marked visibleToCustomer print on the invoice, its emails and pages. Required ones are checked here.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"customerId":"customer_id","lines":[{"description":"string","quantity":1,"unitPriceCents":12500}]}'

Response

201: the answer's fields
FieldTypeDescription
idstringInvoice id.
organizationIdstring
identifierstringINV-n, unique per organization.
customerIdstring
jobIdstring or null
statusenumDRAFT (not issued), SENT (issued, money due), PAID, OVERDUE (past dueDate) or CANCELLED. One of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
subtotalCentsintegerLines total before tax, in integer cents.
taxRatenumberTax rate in percent.
taxCentsintegerTax, in integer cents.
totalCentsintegerTotal with tax, in integer cents.
notesstring or null
dueDatestring or nullYYYY-MM-DD
paidDatestring or nullYYYY-MM-DD
amountPaidCentsintegerPaid so far, net of refunds, tips excluded, in integer cents.
membershipIdstring or nullSet on a membership's billing invoice.
memberPricingRemovedboolean
financingDisabledboolean
progressBillingIdstring or null
progressRoleenum or nullIts place in a progress billing. One of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressMilestoneIdstring or null
progressBilledCentsintegerBilled towards the contract on a progress invoice, in integer cents.
retainageCentsintegerRetainage held on a progress invoice, in integer cents.
businessUnitIdstring or null
customFieldsanyCustom field values by key (definitions: GET /custom-fields?entity=INVOICE).
createdAtstring (date-time)
updatedAtstring (date-time)
customerobject
customer.idstring
customer.namestring
businessUnitobject or nullThe business unit.
businessUnit.idstring
businessUnit.namestring
jobobject or null
job.idstring
job.identifierstringJOB-n
job.signedBystring or nullWork accepted by (the job's sign-off).
job.signedAtstring (date-time) or null
balanceDueCentsintegerStill due, in integer cents.
partiallyPaidboolean

Get an invoice with its lines and payments

GET /organizations/{organizationId}/invoices/{invoiceId}

Includes payments (with refunds), amountPaidCents (net of refunds, tips excluded), balanceDueCents, tipsCents, partiallyPaid and publicToken (the /i/{token} link, null until made). Read-only.

Permission: invoices.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices), Member); risk read (OAuth scope servflo.read).

Permissioninvoices.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolinvoices_get (toolset invoices, in the default set)

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstringInvoice id.
organizationIdstring
identifierstringINV-n, unique per organization.
customerIdstring
jobIdstring or null
statusenumDRAFT (not issued), SENT (issued, money due), PAID, OVERDUE (past dueDate) or CANCELLED. One of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
subtotalCentsintegerLines total before tax, in integer cents.
taxRatenumberTax rate in percent.
taxCentsintegerTax, in integer cents.
totalCentsintegerTotal with tax, in integer cents.
notesstring or null
dueDatestring or nullYYYY-MM-DD
paidDatestring or nullYYYY-MM-DD
amountPaidCentsintegerPaid so far, net of refunds, tips excluded, in integer cents.
membershipIdstring or nullSet on a membership's billing invoice.
memberPricingRemovedboolean
financingDisabledboolean
progressBillingIdstring or null
progressRoleenum or nullIts place in a progress billing. One of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressMilestoneIdstring or null
progressBilledCentsintegerBilled towards the contract on a progress invoice, in integer cents.
retainageCentsintegerRetainage held on a progress invoice, in integer cents.
businessUnitIdstring or null
customFieldsanyCustom field values by key (definitions: GET /custom-fields?entity=INVOICE).
createdAtstring (date-time)
updatedAtstring (date-time)
customerobject
customer.idstring
customer.namestring
customer.emailstring or null
customer.phonestring or null
customer.addressstring or null
customer.citystring or null
customer.statestring or null
customer.zipstring or null
businessUnitobject or nullThe business unit.
businessUnit.idstring
businessUnit.namestring
jobobject or null
job.idstring
job.identifierstringJOB-n
job.signedBystring or nullWork accepted by (the job's sign-off).
job.signedAtstring (date-time) or null
balanceDueCentsintegerStill due, in integer cents.
partiallyPaidboolean
linesarray of object
lines[].idstring
lines[].invoiceIdstring
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsintegerUnit price, in integer cents.
lines[].totalCentsintegerLine total, in integer cents.
lines[].sortOrderinteger
lines[].kindenum or nullSERVICE or MATERIAL (member discount by kind), MEMBER_DISCOUNT (the automatic member pricing line) or MEMBERSHIP (a plan fee). One of SERVICE, MATERIAL, MEMBER_DISCOUNT, MEMBERSHIP.
lines[].pricebookItemIdstring or null
lines[].costCentsinteger or nullOur unit cost in cents (costing.read only).
lines[].memberPriceAppliedboolean
quotesarray of objectQuotes invoiced here (Q-n).
quotes[].idstring
quotes[].identifierstringQ-n
quotes[].statusenumDRAFT → SENT (customer can answer at /q/{publicToken}) → APPROVED / CHANGES_REQUESTED / DECLINED; APPROVED → CONVERTED (job or invoice made). SENT past validUntil becomes EXPIRED. One of DRAFT, SENT, APPROVED, CHANGES_REQUESTED, DECLINED, CONVERTED, EXPIRED, ARCHIVED.
paymentsarray of objectPayments on the invoice, with their refunds.
payments[].idstring
payments[].identifierstringPAY-n
payments[].amountCentsinteger
payments[].tipCentsinteger
payments[].refundedCentsinteger
payments[].methodenumOne of CASH, CHECK, CARD, BANK_TRANSFER, OTHER.
payments[].paidOnstringYYYY-MM-DD
payments[].statusenumOne of SUCCEEDED, PENDING, FAILED, REFUNDED, PARTIALLY_REFUNDED.
tipsCentsintegerTips collected, net of refunds, in integer cents.
memberPricingobject
memberPricing.appliedbooleanMember discount lines are on the document.
memberPricing.removedbooleanStaff removed the member pricing from this document.
memberPricing.membershipobject or nullThe customer's active membership that would apply; null when not a member.
memberPricing.membership.membershipIdstring
memberPricing.membership.identifierstringMEM-n
memberPricing.membership.planNamestring
memberPricing.membership.serviceDiscountPercentnumber
memberPricing.membership.materialDiscountPercentnumber
memberPricing.membership.priorityboolean
publicTokenstring or nullThe customer link token (/i/{token}); null until one is made.

Update an invoice's status, notes or dates

PATCH /organizations/{organizationId}/invoices/{invoiceId}

Amounts are fixed once created. MONEY: setting status: PAID while a balance is due records that balance as a payment (method OTHER, dated paidDate or today) so payments and status agree; prefer POST /invoices/{invoiceId}/payments to record the real method. An invoice paid in full cannot be set back to DRAFT/SENT/OVERDUE (refund a payment instead, 409), and one holding payments cannot be CANCELLED (409). paidDate is only for paid invoices. Marking SENT does not email anything.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissioninvoices.write
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolinvoices_update (toolset invoices, in the default set)
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals.

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Request body

FieldTypeRequiredDescription
statusenumNoOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
notesstring or nullNo
dueDateempty string or string or nullNo
paidDateempty string or string or nullNo
businessUnitIdstring or nullNoMove the invoice to another business unit (GET /business-units), or null for none. Up to 100 characters.
financingDisabledbooleanNoExposes to customers: true hides Affirm and Klarna on this invoice's pay page even when the business offers financing.
todaystringNoThe business's today (YYYY-MM-DD), for the paid date and reopened-overdue checks.
customFieldsobjectNoCustom field values by key (definitions: GET /custom-fields?entity=INVOICE); merged into the saved values on an update, null clears one. Checked against each field's type and options; unknown keys are refused.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"DRAFT","notes":"string","dueDate":"2026-11-03"}'

Response

200: the answer's fields
FieldTypeDescription
idstringInvoice id.
organizationIdstring
identifierstringINV-n, unique per organization.
customerIdstring
jobIdstring or null
statusenumDRAFT (not issued), SENT (issued, money due), PAID, OVERDUE (past dueDate) or CANCELLED. One of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
subtotalCentsintegerLines total before tax, in integer cents.
taxRatenumberTax rate in percent.
taxCentsintegerTax, in integer cents.
totalCentsintegerTotal with tax, in integer cents.
notesstring or null
dueDatestring or nullYYYY-MM-DD
paidDatestring or nullYYYY-MM-DD
amountPaidCentsintegerPaid so far, net of refunds, tips excluded, in integer cents.
membershipIdstring or nullSet on a membership's billing invoice.
memberPricingRemovedboolean
financingDisabledboolean
progressBillingIdstring or null
progressRoleenum or nullIts place in a progress billing. One of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressMilestoneIdstring or null
progressBilledCentsintegerBilled towards the contract on a progress invoice, in integer cents.
retainageCentsintegerRetainage held on a progress invoice, in integer cents.
businessUnitIdstring or null
customFieldsanyCustom field values by key (definitions: GET /custom-fields?entity=INVOICE).
createdAtstring (date-time)
updatedAtstring (date-time)
customerobject
customer.idstring
customer.namestring
businessUnitobject or nullThe business unit.
businessUnit.idstring
businessUnit.namestring
jobobject or null
job.idstring
job.identifierstringJOB-n
job.signedBystring or nullWork accepted by (the job's sign-off).
job.signedAtstring (date-time) or null
balanceDueCentsintegerStill due, in integer cents.
partiallyPaidboolean

Delete an invoice

DELETE /organizations/{organizationId}/invoices/{invoiceId}

Destructive: deletes the invoice and its lines. Refused (409) while the invoice holds any payment; cancel it instead.

Permissioninvoices.delete (Owner, Admin)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolinvoices_delete (toolset invoices)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Example

curl -X DELETE "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
oktrue

Remove or re-apply member pricing on a draft invoice

POST /organizations/{organizationId}/invoices/{invoiceId}/member-pricing

Member pricing: applied: false removes the automatic member discount lines and recomputes the totals; applied: true adds them back from the customer's active membership (409 when there is none). Only for DRAFT invoices without payments (amounts are fixed once issued; 409 otherwise). Read the state from GET /invoices/{invoiceId} (memberPricing).

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolinvoices_setMemberPricing (toolset invoices)

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Request body

FieldTypeRequiredDescription
appliedbooleanYestrue: apply the member discount; false: remove it.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}/member-pricing" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"applied":true}'

Response

200: the answer's fields
FieldTypeDescription
idstringInvoice id.
organizationIdstring
identifierstringINV-n, unique per organization.
customerIdstring
jobIdstring or null
statusenumDRAFT (not issued), SENT (issued, money due), PAID, OVERDUE (past dueDate) or CANCELLED. One of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
subtotalCentsintegerLines total before tax, in integer cents.
taxRatenumberTax rate in percent.
taxCentsintegerTax, in integer cents.
totalCentsintegerTotal with tax, in integer cents.
notesstring or null
dueDatestring or nullYYYY-MM-DD
paidDatestring or nullYYYY-MM-DD
amountPaidCentsintegerPaid so far, net of refunds, tips excluded, in integer cents.
membershipIdstring or nullSet on a membership's billing invoice.
memberPricingRemovedboolean
financingDisabledboolean
progressBillingIdstring or null
progressRoleenum or nullIts place in a progress billing. One of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressMilestoneIdstring or null
progressBilledCentsintegerBilled towards the contract on a progress invoice, in integer cents.
retainageCentsintegerRetainage held on a progress invoice, in integer cents.
businessUnitIdstring or null
customFieldsanyCustom field values by key (definitions: GET /custom-fields?entity=INVOICE).
createdAtstring (date-time)
updatedAtstring (date-time)
customerobject
customer.idstring
customer.namestring
customer.emailstring or null
customer.phonestring or null
customer.addressstring or null
customer.citystring or null
customer.statestring or null
customer.zipstring or null
businessUnitobject or nullThe business unit.
businessUnit.idstring
businessUnit.namestring
jobobject or null
job.idstring
job.identifierstringJOB-n
job.signedBystring or nullWork accepted by (the job's sign-off).
job.signedAtstring (date-time) or null
balanceDueCentsintegerStill due, in integer cents.
partiallyPaidboolean
linesarray of object
lines[].idstring
lines[].invoiceIdstring
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsintegerUnit price, in integer cents.
lines[].totalCentsintegerLine total, in integer cents.
lines[].sortOrderinteger
lines[].kindenum or nullSERVICE or MATERIAL (member discount by kind), MEMBER_DISCOUNT (the automatic member pricing line) or MEMBERSHIP (a plan fee). One of SERVICE, MATERIAL, MEMBER_DISCOUNT, MEMBERSHIP.
lines[].pricebookItemIdstring or null
lines[].costCentsinteger or nullOur unit cost in cents (costing.read only).
lines[].memberPriceAppliedboolean
quotesarray of objectQuotes invoiced here (Q-n).
quotes[].idstring
quotes[].identifierstringQ-n
quotes[].statusenumDRAFT → SENT (customer can answer at /q/{publicToken}) → APPROVED / CHANGES_REQUESTED / DECLINED; APPROVED → CONVERTED (job or invoice made). SENT past validUntil becomes EXPIRED. One of DRAFT, SENT, APPROVED, CHANGES_REQUESTED, DECLINED, CONVERTED, EXPIRED, ARCHIVED.
paymentsarray of objectPayments on the invoice, with their refunds.
payments[].idstring
payments[].identifierstringPAY-n
payments[].amountCentsinteger
payments[].tipCentsinteger
payments[].refundedCentsinteger
payments[].methodenumOne of CASH, CHECK, CARD, BANK_TRANSFER, OTHER.
payments[].paidOnstringYYYY-MM-DD
payments[].statusenumOne of SUCCEEDED, PENDING, FAILED, REFUNDED, PARTIALLY_REFUNDED.
tipsCentsintegerTips collected, net of refunds, in integer cents.
memberPricingobject
memberPricing.appliedbooleanMember discount lines are on the document.
memberPricing.removedbooleanStaff removed the member pricing from this document.
memberPricing.membershipobject or nullThe customer's active membership that would apply; null when not a member.
memberPricing.membership.membershipIdstring
memberPricing.membership.identifierstringMEM-n
memberPricing.membership.planNamestring
memberPricing.membership.serviceDiscountPercentnumber
memberPricing.membership.materialDiscountPercentnumber
memberPricing.membership.priorityboolean
publicTokenstring or nullThe customer link token (/i/{token}); null until one is made.

Jobs ready to invoice

GET /organizations/{organizationId}/invoice-batches/jobs

Batch invoicing, step 1: jobs not on any live invoice and not billed in progress invoices, oldest first. By default only COMPLETED jobs (ready to invoice); readyOnly: false adds scheduled and in-progress ones. Filter by the job's date (from/to, YYYY-MM-DD), customer, service type, technician (lead or crew) or text. Each row carries its subtotal before member discount and tax, and warnings. Read-only. Next: GET /invoice-batches/preview, then POST /invoice-batches.

Permission: invoices.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices), Member); risk read (OAuth scope servflo.read).

Permissioninvoices.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolinvoices_billableJobs (toolset invoices)

Parameters

NameInTypeRequiredDescription
fromquerystringNoA calendar date in the business's own calendar, YYYY-MM-DD.
toquerystringNoA calendar date in the business's own calendar, YYYY-MM-DD.
customerIdquerystringNo
serviceTypeIdquerystringNo
technicianIdquerystringNo
readyOnlyqueryenumNoDefault true: completed jobs only. One of true, false.
queryquerystringNoFree-text search over the list's names, numbers and contact details. Up to 200 characters.
limitqueryintegerNoPage size, 1–200. Default 50.
offsetqueryintegerNoRows to skip; the answer's total counts every match. Default 0.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoice-batches/jobs" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
itemsarray of object
items[].idstring
items[].identifierstringJOB-n
items[].statusstring
items[].scheduledDatestring
items[].customerIdstring
items[].customerNamestring
items[].serviceTypeNamestring or null
items[].technicianIdstring or null
items[].technicianNamestring or null
items[].subtotalCentsintegerService price plus materials, before member discount and tax.
items[].taxRatenumber
items[].readybooleanCompleted: ready to invoice.
items[].warningsarray of string
totalinteger

Preview a batch of invoices

GET /organizations/{organizationId}/invoice-batches/preview

Batch invoicing, step 2: the invoices POST /invoice-batches would create for these jobs (one per job, or with perCustomer one per customer and tax rate combining their jobs): lines, member discount, tax, total and deposits that would move onto each; plus the jobs it would skip and why. Creates nothing.

Permission: invoices.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices), Member); risk read (OAuth scope servflo.read).

Permissioninvoices.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolinvoices_batchPreview (toolset invoices)

Parameters

NameInTypeRequiredDescription
jobIdsquerystringYesJob ids, comma separated (from GET /invoice-batches/jobs). Up to 8000 characters.
perCustomerqueryenumNotrue: one invoice per customer combining their jobs. One of true, false.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoice-batches/preview?jobIds=string" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
invoicesarray of object
invoices[].customerIdstring
invoices[].customerNamestring
invoices[].jobIdsarray of string
invoices[].jobIdentifiersarray of string
invoices[].lineCountinteger
invoices[].taxRatenumber
invoices[].subtotalCentsinteger
invoices[].taxCentsinteger
invoices[].totalCentsinteger
invoices[].depositsCentsintegerDeposits on the jobs' quotes that move onto the invoice.
skippedarray of object
skipped[].jobIdstring
skipped[].identifierstring or null
skipped[].reasonstring
totalCentsinteger

Create invoices for many jobs

POST /organizations/{organizationId}/invoice-batches

Batch invoicing, step 3: creates DRAFT invoices (INV-n) for the given jobs, one per job or (perCustomer) one per customer and tax rate, priced as POST /jobs/{jobId}/invoice would (member pricing, the job property's tax, deposits on the job's quote moved on). Answers one row per job: CREATED with the invoice, SKIPPED (already invoiced, progress-billed, cancelled, not found) or FAILED with the reason; one failing never stops the others. Safe to re-run: an invoiced job is skipped, under a lock per job. Nothing is sent: then POST /invoice-batches/send. Emits invoice.created per invoice and invoice_batch.completed.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolinvoices_batchCreate (toolset invoices)

Request body

FieldTypeRequiredDescription
jobIdsarray of stringYesFrom GET /invoice-batches/jobs. Up to 200 items.
perCustomerbooleanNoOne invoice per customer combining their jobs.
dueDateempty string or string or nullNoDefaults from each customer's payment terms.
batchKeystringNoYour key for this batch (e.g. a UUID). Running a batch again with the same key never invoices a job twice nor sends a message twice; omit it and one is made. Up to 100 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoice-batches" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jobIds":["job_id"]}'

Response

200: the answer's fields
FieldTypeDescription
batchKeystring
requestedinteger
succeededinteger
skippedinteger
failedinteger
invoiceIdsarray of string
resultsarray of object
results[].jobIdstring
results[].identifierstring or null
results[].outcomeenumOne of CREATED, SENT, QUEUED, MARKED, SKIPPED, FAILED.
results[].invoiceIdstring or null
results[].invoiceIdentifierstring or null
results[].reasonstring or null

Send many invoices

POST /organizations/{organizationId}/invoice-batches/send

Customer-facing: emails or texts each invoice to its billing contact with the business's invoice template and link, exactly as the Send dialog would (POST /messages/send); a draft is issued (SENT). channel EMAIL, SMS, or EMAIL_OR_SMS (email, else text). Consent applies: no text without OK-to-text or after STOP, opted-out emails are skipped, quiet hours hold texts. One row per invoice: SENT, QUEUED (held for quiet hours) or SKIPPED/FAILED with the reason (paid, cancelled, no address, no consent). Re-running with the same batchKey never sends twice. In test mode messages are logged, not sent.

Permissionmessages.send (Owner, Admin, Dispatcher)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolinvoices_batchSend (toolset invoices)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Request body

FieldTypeRequiredDescription
invoiceIdsarray of stringYesInvoice ids from GET /invoices, at most 200. Up to 200 items.
channelenumNoOne of EMAIL, SMS, EMAIL_OR_SMS. Default EMAIL.
batchKeystringNoYour key for this batch (e.g. a UUID). Running a batch again with the same key never invoices a job twice nor sends a message twice; omit it and one is made. Up to 100 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoice-batches/send" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"invoiceIds":["invoice_id"]}'

Response

200: the answer's fields
FieldTypeDescription
batchKeystring
requestedinteger
succeededinteger
skippedinteger
failedinteger
resultsarray of object
results[].invoiceIdstring
results[].identifierstring or null
results[].outcomeenumOne of CREATED, SENT, QUEUED, MARKED, SKIPPED, FAILED.
results[].reasonstring or null
results[].channelenum or nullOne of EMAIL, SMS.
results[].messageIdstring or null

Send payment reminders for many invoices

POST /organizations/{organizationId}/invoice-batches/remind

Customer-facing: a payment reminder (the business's overdue template) for each SENT or OVERDUE invoice with a balance due; drafts, paid and cancelled invoices are skipped with the reason. Same channels, consent rules, per-row answer and batchKey replay safety as POST /invoice-batches/send.

Permissionmessages.send (Owner, Admin, Dispatcher)
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolinvoices_batchRemind (toolset invoices)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Request body

FieldTypeRequiredDescription
invoiceIdsarray of stringYesInvoice ids from GET /invoices, at most 200. Up to 200 items.
channelenumNoOne of EMAIL, SMS, EMAIL_OR_SMS. Default EMAIL.
batchKeystringNoYour key for this batch (e.g. a UUID). Running a batch again with the same key never invoices a job twice nor sends a message twice; omit it and one is made. Up to 100 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoice-batches/remind" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"invoiceIds":["invoice_id"]}'

Response

200: the answer's fields
FieldTypeDescription
batchKeystring
requestedinteger
succeededinteger
skippedinteger
failedinteger
resultsarray of object
results[].invoiceIdstring
results[].identifierstring or null
results[].outcomeenumOne of CREATED, SENT, QUEUED, MARKED, SKIPPED, FAILED.
results[].reasonstring or null
results[].channelenum or nullOne of EMAIL, SMS.
results[].messageIdstring or null

Mark many invoices as sent

POST /organizations/{organizationId}/invoice-batches/mark-sent

Issues each DRAFT invoice (status SENT, invoice.sent emitted) without contacting the customer, e.g. after mailing or handing them over. Invoices already issued are skipped. One row per invoice.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolinvoices_batchMarkSent (toolset invoices)

Request body

FieldTypeRequiredDescription
invoiceIdsarray of stringYesInvoice ids from GET /invoices, at most 200. Up to 200 items.
batchKeystringNoYour key for this batch (e.g. a UUID). Running a batch again with the same key never invoices a job twice nor sends a message twice; omit it and one is made. Up to 100 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoice-batches/mark-sent" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"invoiceIds":["invoice_id"]}'

Response

200: the answer's fields
FieldTypeDescription
batchKeystring
requestedinteger
succeededinteger
skippedinteger
failedinteger
resultsarray of object
results[].invoiceIdstring
results[].identifierstring or null
results[].outcomeenumOne of CREATED, SENT, QUEUED, MARKED, SKIPPED, FAILED.
results[].reasonstring or null
results[].channelenum or nullOne of EMAIL, SMS.
results[].messageIdstring or null

List progress billings

GET /organizations/{organizationId}/progress-billings

Quotes and jobs billed in progress invoices, newest first, with contract, billed to date, remaining and retainage. Filter by customer, quote, job or status (ACTIVE / COMPLETED). Read-only.

Permission: invoices.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices), Member); risk read (OAuth scope servflo.read).

Permissioninvoices.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolprogressBilling_list (toolset invoices)

Parameters

NameInTypeRequiredDescription
customerIdquerystringNo
quoteIdquerystringNoThe progress billing of this quote, if any.
jobIdquerystringNoThe progress billing of this job, if any.
statusqueryenumNoOne of ACTIVE, COMPLETED.
limitqueryintegerNoPage size, 1–200. Default 50.
offsetqueryintegerNoRows to skip; the answer's total counts every match. Default 0.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
itemsarray of object
items[].idstring
items[].customerIdstring
items[].customerobject
items[].customer.idstring
items[].customer.namestring
items[].quoteIdstring or null
items[].quoteobject or null
items[].quote.idstring
items[].quote.identifierstring
items[].quote.titlestring
items[].jobIdstring or null
items[].jobobject or null
items[].job.idstring
items[].job.identifierstring
items[].titlestring
items[].methodenumOne of MILESTONES, PERCENT_COMPLETE.
items[].contractCentsintegerThe contract before tax.
items[].taxRatenumber
items[].retainagePercentnumber
items[].statusenumOne of ACTIVE, COMPLETED.
items[].createdAtstring (date-time)
items[].linesarray of object
items[].lines[].idstring
items[].lines[].descriptionstring
items[].lines[].quantitynumber
items[].lines[].unitPriceCentsinteger
items[].lines[].contractCentsinteger
items[].lines[].billedCentsinteger
items[].lines[].percentCompletenumber
items[].milestonesarray of object
items[].milestones[].idstring
items[].milestones[].namestring
items[].milestones[].percentnumber
items[].milestones[].amountCentsinteger
items[].milestones[].invoiceIdstring or null
items[].milestones[].invoiceIdentifierstring or null
items[].invoicesarray of object
items[].invoices[].idstring
items[].invoices[].identifierstring
items[].invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
items[].invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
items[].invoices[].milestoneIdstring or null
items[].invoices[].billedCentsinteger
items[].invoices[].retainageCentsintegerHeld (positive) or released (negative).
items[].invoices[].totalCentsinteger
items[].invoices[].amountPaidCentsinteger
items[].invoices[].balanceDueCentsinteger
items[].invoices[].dueDatestring or null
items[].invoices[].createdAtstring (date-time)
items[].summaryobject
items[].summary.contractCentsinteger
items[].summary.billedCentsintegerContract work billed to date, before retainage and tax.
items[].summary.remainingCentsinteger
items[].summary.percentBillednumber
items[].summary.retainageHeldCentsinteger
items[].summary.retainageReleasedCentsinteger
items[].summary.retainageOutstandingCentsinteger
items[].summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
items[].summary.paidCentsinteger
items[].summary.hasFinalboolean
items[].summary.completeboolean
items[].summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
items[].summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.
totalinteger

Bill a quote or job in progress invoices

POST /organizations/{organizationId}/progress-billings

Freezes the contract from an APPROVED (or converted) quote not yet invoiced (its chosen lines and tax) or from a job not yet invoiced (service and materials, member-priced; a job sold through a quote uses the quote). method MILESTONES (milestones adding to 100%) or PERCENT_COMPLETE (bill each line as it progresses). retainagePercent (0–50) is held back on each invoice until the retainage release. Afterwards the quote/job can no longer be invoiced in one go (409): bill it through POST /progress-billings/{id}/invoices. Deposits on the quote move onto the first invoice. Creates no invoice. Emits progress_billing.created.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolprogressBilling_create (toolset invoices)

Request body

FieldTypeRequiredDescription
quoteIdstringNoAn approved quote (GET /quotes). Give this or jobId.
jobIdstringNoA job (GET /jobs). Give this or quoteId.
titlestring or nullNoUp to 200 characters.
methodenumYesOne of MILESTONES, PERCENT_COMPLETE.
retainagePercentnumberNoDefault 0 (none). 0–50.
milestonesarray of objectNoMILESTONES only: names and percents adding up to 100, e.g. Deposit 30, Rough-in 40, Completion 30. Up to 20 items.
milestones[].namestringYesUp to 200 characters.
milestones[].percentnumberYesPercent of the contract, e.g. 30. At most 100.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"method":"MILESTONES"}'

Response

201: the answer's fields
FieldTypeDescription
idstring
customerIdstring
customerobject
customer.idstring
customer.namestring
quoteIdstring or null
quoteobject or null
quote.idstring
quote.identifierstring
quote.titlestring
jobIdstring or null
jobobject or null
job.idstring
job.identifierstring
titlestring
methodenumOne of MILESTONES, PERCENT_COMPLETE.
contractCentsintegerThe contract before tax.
taxRatenumber
retainagePercentnumber
statusenumOne of ACTIVE, COMPLETED.
createdAtstring (date-time)
linesarray of object
lines[].idstring
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
lines[].contractCentsinteger
lines[].billedCentsinteger
lines[].percentCompletenumber
milestonesarray of object
milestones[].idstring
milestones[].namestring
milestones[].percentnumber
milestones[].amountCentsinteger
milestones[].invoiceIdstring or null
milestones[].invoiceIdentifierstring or null
invoicesarray of object
invoices[].idstring
invoices[].identifierstring
invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
invoices[].milestoneIdstring or null
invoices[].billedCentsinteger
invoices[].retainageCentsintegerHeld (positive) or released (negative).
invoices[].totalCentsinteger
invoices[].amountPaidCentsinteger
invoices[].balanceDueCentsinteger
invoices[].dueDatestring or null
invoices[].createdAtstring (date-time)
summaryobject
summary.contractCentsinteger
summary.billedCentsintegerContract work billed to date, before retainage and tax.
summary.remainingCentsinteger
summary.percentBillednumber
summary.retainageHeldCentsinteger
summary.retainageReleasedCentsinteger
summary.retainageOutstandingCentsinteger
summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
summary.paidCentsinteger
summary.hasFinalboolean
summary.completeboolean
summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.

Get a progress billing

GET /organizations/{organizationId}/progress-billings/{progressBillingId}

The contract (lines, milestones, retainage), its invoices and the summary: billed to date, remaining, retainage held and released, and whether a final or retainage-release invoice can be made (with the reason when not). Read-only.

Permission: invoices.read (Owner, Admin, Dispatcher, Technician (own jobs' invoices), Member); risk read (OAuth scope servflo.read).

Permissioninvoices.read
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolprogressBilling_get (toolset invoices)

Parameters

NameInTypeRequiredDescription
progressBillingIdpathstringYesA progress billing id, from GET /progress-billings.

Example

curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings/{progressBillingId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
customerIdstring
customerobject
customer.idstring
customer.namestring
quoteIdstring or null
quoteobject or null
quote.idstring
quote.identifierstring
quote.titlestring
jobIdstring or null
jobobject or null
job.idstring
job.identifierstring
titlestring
methodenumOne of MILESTONES, PERCENT_COMPLETE.
contractCentsintegerThe contract before tax.
taxRatenumber
retainagePercentnumber
statusenumOne of ACTIVE, COMPLETED.
createdAtstring (date-time)
linesarray of object
lines[].idstring
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
lines[].contractCentsinteger
lines[].billedCentsinteger
lines[].percentCompletenumber
milestonesarray of object
milestones[].idstring
milestones[].namestring
milestones[].percentnumber
milestones[].amountCentsinteger
milestones[].invoiceIdstring or null
milestones[].invoiceIdentifierstring or null
invoicesarray of object
invoices[].idstring
invoices[].identifierstring
invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
invoices[].milestoneIdstring or null
invoices[].billedCentsinteger
invoices[].retainageCentsintegerHeld (positive) or released (negative).
invoices[].totalCentsinteger
invoices[].amountPaidCentsinteger
invoices[].balanceDueCentsinteger
invoices[].dueDatestring or null
invoices[].createdAtstring (date-time)
summaryobject
summary.contractCentsinteger
summary.billedCentsintegerContract work billed to date, before retainage and tax.
summary.remainingCentsinteger
summary.percentBillednumber
summary.retainageHeldCentsinteger
summary.retainageReleasedCentsinteger
summary.retainageOutstandingCentsinteger
summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
summary.paidCentsinteger
summary.hasFinalboolean
summary.completeboolean
summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.

Change a progress billing

PATCH /organizations/{organizationId}/progress-billings/{progressBillingId}

The title any time; the method, retainage and milestones only while nothing has been invoiced (409 otherwise; cancelled invoices do not count). The contract itself never changes.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolprogressBilling_update (toolset invoices)

Parameters

NameInTypeRequiredDescription
progressBillingIdpathstringYesA progress billing id, from GET /progress-billings.

Request body

FieldTypeRequiredDescription
titlestring or nullNoUp to 200 characters.
methodenumNoOne of MILESTONES, PERCENT_COMPLETE.
retainagePercentnumberNo0–50.
milestonesarray of objectNoMILESTONES only: names and percents adding up to 100, e.g. Deposit 30, Rough-in 40, Completion 30. Up to 20 items.
milestones[].namestringYesUp to 200 characters.
milestones[].percentnumberYesPercent of the contract, e.g. 30. At most 100.

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings/{progressBillingId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"string","method":"MILESTONES","retainagePercent":1}'

Response

200: the answer's fields
FieldTypeDescription
idstring
customerIdstring
customerobject
customer.idstring
customer.namestring
quoteIdstring or null
quoteobject or null
quote.idstring
quote.identifierstring
quote.titlestring
jobIdstring or null
jobobject or null
job.idstring
job.identifierstring
titlestring
methodenumOne of MILESTONES, PERCENT_COMPLETE.
contractCentsintegerThe contract before tax.
taxRatenumber
retainagePercentnumber
statusenumOne of ACTIVE, COMPLETED.
createdAtstring (date-time)
linesarray of object
lines[].idstring
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
lines[].contractCentsinteger
lines[].billedCentsinteger
lines[].percentCompletenumber
milestonesarray of object
milestones[].idstring
milestones[].namestring
milestones[].percentnumber
milestones[].amountCentsinteger
milestones[].invoiceIdstring or null
milestones[].invoiceIdentifierstring or null
invoicesarray of object
invoices[].idstring
invoices[].identifierstring
invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
invoices[].milestoneIdstring or null
invoices[].billedCentsinteger
invoices[].retainageCentsintegerHeld (positive) or released (negative).
invoices[].totalCentsinteger
invoices[].amountPaidCentsinteger
invoices[].balanceDueCentsinteger
invoices[].dueDatestring or null
invoices[].createdAtstring (date-time)
summaryobject
summary.contractCentsinteger
summary.billedCentsintegerContract work billed to date, before retainage and tax.
summary.remainingCentsinteger
summary.percentBillednumber
summary.retainageHeldCentsinteger
summary.retainageReleasedCentsinteger
summary.retainageOutstandingCentsinteger
summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
summary.paidCentsinteger
summary.hasFinalboolean
summary.completeboolean
summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.

Stop billing in progress invoices

DELETE /organizations/{organizationId}/progress-billings/{progressBillingId}

Destructive: removes a progress billing that has no live invoices (409 otherwise: cancel them first), so the quote or job can be invoiced normally again.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissioninvoices.write
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolprogressBilling_delete (toolset invoices)
ApprovalOver MCP, this tool always waits for your approval in ServFlo. See Approvals.

Parameters

NameInTypeRequiredDescription
progressBillingIdpathstringYesA progress billing id, from GET /progress-billings.

Example

curl -X DELETE "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings/{progressBillingId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
oktrue

Create a progress invoice

POST /organizations/{organizationId}/progress-billings/{progressBillingId}/invoices

Creates a DRAFT progress invoice for part of the contract. Give exactly one of: milestoneId (MILESTONES: that milestone's share; 409 when already invoiced), percentOfContract or amountCents (MILESTONES: an extra partial invoice), or lines (PERCENT_COMPLETE: each line's new cumulative percent complete; the difference is billed; percents cannot go down). Never bills beyond the contract (409 with what is left). Retainage at the plan's percent is held back as a negative line; tax at the contract's rate. The first invoice receives the quote's deposits (it must be at least that much). Nothing is sent. Emits invoice.created and progress_billing.invoiced.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolprogressBilling_invoice (toolset invoices)

Parameters

NameInTypeRequiredDescription
progressBillingIdpathstringYesA progress billing id, from GET /progress-billings.

Request body

FieldTypeRequiredDescription
milestoneIdstringNoFrom the progress billing's milestones.
percentOfContractnumberNoAt most 100.
amountCentsintegerNo1–100000000.
linesarray of objectNoUp to 500 items.
lines[].lineIdstringYesFrom the progress billing's lines.
lines[].percentCompletenumberYes0–100.
dueDateempty string or string or nullNoDefaults from the customer's payment terms.
notesstring or nullNoPrinted on the invoice.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings/{progressBillingId}/invoices" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"milestoneId":"milestone_id","percentOfContract":1,"amountCents":12500}'

Response

201: the answer's fields
FieldTypeDescription
invoiceobject
invoice.idstring
invoice.identifierstring
invoice.statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
invoice.totalCentsinteger
progressBillingobject
progressBilling.idstring
progressBilling.customerIdstring
progressBilling.customerobject
progressBilling.customer.idstring
progressBilling.customer.namestring
progressBilling.quoteIdstring or null
progressBilling.quoteobject or null
progressBilling.quote.idstring
progressBilling.quote.identifierstring
progressBilling.quote.titlestring
progressBilling.jobIdstring or null
progressBilling.jobobject or null
progressBilling.job.idstring
progressBilling.job.identifierstring
progressBilling.titlestring
progressBilling.methodenumOne of MILESTONES, PERCENT_COMPLETE.
progressBilling.contractCentsintegerThe contract before tax.
progressBilling.taxRatenumber
progressBilling.retainagePercentnumber
progressBilling.statusenumOne of ACTIVE, COMPLETED.
progressBilling.createdAtstring (date-time)
progressBilling.linesarray of object
progressBilling.lines[].idstring
progressBilling.lines[].descriptionstring
progressBilling.lines[].quantitynumber
progressBilling.lines[].unitPriceCentsinteger
progressBilling.lines[].contractCentsinteger
progressBilling.lines[].billedCentsinteger
progressBilling.lines[].percentCompletenumber
progressBilling.milestonesarray of object
progressBilling.milestones[].idstring
progressBilling.milestones[].namestring
progressBilling.milestones[].percentnumber
progressBilling.milestones[].amountCentsinteger
progressBilling.milestones[].invoiceIdstring or null
progressBilling.milestones[].invoiceIdentifierstring or null
progressBilling.invoicesarray of object
progressBilling.invoices[].idstring
progressBilling.invoices[].identifierstring
progressBilling.invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
progressBilling.invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressBilling.invoices[].milestoneIdstring or null
progressBilling.invoices[].billedCentsinteger
progressBilling.invoices[].retainageCentsintegerHeld (positive) or released (negative).
progressBilling.invoices[].totalCentsinteger
progressBilling.invoices[].amountPaidCentsinteger
progressBilling.invoices[].balanceDueCentsinteger
progressBilling.invoices[].dueDatestring or null
progressBilling.invoices[].createdAtstring (date-time)
progressBilling.summaryobject
progressBilling.summary.contractCentsinteger
progressBilling.summary.billedCentsintegerContract work billed to date, before retainage and tax.
progressBilling.summary.remainingCentsinteger
progressBilling.summary.percentBillednumber
progressBilling.summary.retainageHeldCentsinteger
progressBilling.summary.retainageReleasedCentsinteger
progressBilling.summary.retainageOutstandingCentsinteger
progressBilling.summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
progressBilling.summary.paidCentsinteger
progressBilling.summary.hasFinalboolean
progressBilling.summary.completeboolean
progressBilling.summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
progressBilling.summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.

Create the final invoice

POST /organizations/{organizationId}/progress-billings/{progressBillingId}/final-invoice

Creates the DRAFT final invoice: every contract line in full, less a credit line per earlier progress invoice, so it bills exactly what is left. Retainage is held on that remainder, unless releaseRetainage: true releases every earlier retainage on it instead. 409 when nothing is left or a final exists. Emits progress_billing.invoiced (and .completed when nothing remains).

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolprogressBilling_finalInvoice (toolset invoices)

Parameters

NameInTypeRequiredDescription
progressBillingIdpathstringYesA progress billing id, from GET /progress-billings.

Request body

FieldTypeRequiredDescription
releaseRetainagebooleanNoRelease the retainage held so far on this invoice.
dueDateempty string or string or nullNoDefaults from the customer's payment terms.
notesstring or nullNoPrinted on the invoice.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings/{progressBillingId}/final-invoice" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"releaseRetainage":true,"dueDate":"2026-11-03","notes":"string"}'

Response

201: the answer's fields
FieldTypeDescription
invoiceobject
invoice.idstring
invoice.identifierstring
invoice.statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
invoice.totalCentsinteger
progressBillingobject
progressBilling.idstring
progressBilling.customerIdstring
progressBilling.customerobject
progressBilling.customer.idstring
progressBilling.customer.namestring
progressBilling.quoteIdstring or null
progressBilling.quoteobject or null
progressBilling.quote.idstring
progressBilling.quote.identifierstring
progressBilling.quote.titlestring
progressBilling.jobIdstring or null
progressBilling.jobobject or null
progressBilling.job.idstring
progressBilling.job.identifierstring
progressBilling.titlestring
progressBilling.methodenumOne of MILESTONES, PERCENT_COMPLETE.
progressBilling.contractCentsintegerThe contract before tax.
progressBilling.taxRatenumber
progressBilling.retainagePercentnumber
progressBilling.statusenumOne of ACTIVE, COMPLETED.
progressBilling.createdAtstring (date-time)
progressBilling.linesarray of object
progressBilling.lines[].idstring
progressBilling.lines[].descriptionstring
progressBilling.lines[].quantitynumber
progressBilling.lines[].unitPriceCentsinteger
progressBilling.lines[].contractCentsinteger
progressBilling.lines[].billedCentsinteger
progressBilling.lines[].percentCompletenumber
progressBilling.milestonesarray of object
progressBilling.milestones[].idstring
progressBilling.milestones[].namestring
progressBilling.milestones[].percentnumber
progressBilling.milestones[].amountCentsinteger
progressBilling.milestones[].invoiceIdstring or null
progressBilling.milestones[].invoiceIdentifierstring or null
progressBilling.invoicesarray of object
progressBilling.invoices[].idstring
progressBilling.invoices[].identifierstring
progressBilling.invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
progressBilling.invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressBilling.invoices[].milestoneIdstring or null
progressBilling.invoices[].billedCentsinteger
progressBilling.invoices[].retainageCentsintegerHeld (positive) or released (negative).
progressBilling.invoices[].totalCentsinteger
progressBilling.invoices[].amountPaidCentsinteger
progressBilling.invoices[].balanceDueCentsinteger
progressBilling.invoices[].dueDatestring or null
progressBilling.invoices[].createdAtstring (date-time)
progressBilling.summaryobject
progressBilling.summary.contractCentsinteger
progressBilling.summary.billedCentsintegerContract work billed to date, before retainage and tax.
progressBilling.summary.remainingCentsinteger
progressBilling.summary.percentBillednumber
progressBilling.summary.retainageHeldCentsinteger
progressBilling.summary.retainageReleasedCentsinteger
progressBilling.summary.retainageOutstandingCentsinteger
progressBilling.summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
progressBilling.summary.paidCentsinteger
progressBilling.summary.hasFinalboolean
progressBilling.summary.completeboolean
progressBilling.summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
progressBilling.summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.

Invoice the retainage held

POST /organizations/{organizationId}/progress-billings/{progressBillingId}/retainage-release

Creates a DRAFT retainage-release invoice for all retainage held and not yet released. Only once the whole contract is billed (after the final invoice; 409 otherwise). Emits progress_billing.invoiced and progress_billing.completed.

Permission: invoices.write (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk write (OAuth scope servflo.write).

Permissioninvoices.write
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolprogressBilling_releaseRetainage (toolset invoices)

Parameters

NameInTypeRequiredDescription
progressBillingIdpathstringYesA progress billing id, from GET /progress-billings.

Request body

FieldTypeRequiredDescription
dueDateempty string or string or nullNoDefaults from the customer's payment terms.
notesstring or nullNoPrinted on the invoice.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/progress-billings/{progressBillingId}/retainage-release" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dueDate":"2026-11-03","notes":"string"}'

Response

201: the answer's fields
FieldTypeDescription
invoiceobject
invoice.idstring
invoice.identifierstring
invoice.statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
invoice.totalCentsinteger
progressBillingobject
progressBilling.idstring
progressBilling.customerIdstring
progressBilling.customerobject
progressBilling.customer.idstring
progressBilling.customer.namestring
progressBilling.quoteIdstring or null
progressBilling.quoteobject or null
progressBilling.quote.idstring
progressBilling.quote.identifierstring
progressBilling.quote.titlestring
progressBilling.jobIdstring or null
progressBilling.jobobject or null
progressBilling.job.idstring
progressBilling.job.identifierstring
progressBilling.titlestring
progressBilling.methodenumOne of MILESTONES, PERCENT_COMPLETE.
progressBilling.contractCentsintegerThe contract before tax.
progressBilling.taxRatenumber
progressBilling.retainagePercentnumber
progressBilling.statusenumOne of ACTIVE, COMPLETED.
progressBilling.createdAtstring (date-time)
progressBilling.linesarray of object
progressBilling.lines[].idstring
progressBilling.lines[].descriptionstring
progressBilling.lines[].quantitynumber
progressBilling.lines[].unitPriceCentsinteger
progressBilling.lines[].contractCentsinteger
progressBilling.lines[].billedCentsinteger
progressBilling.lines[].percentCompletenumber
progressBilling.milestonesarray of object
progressBilling.milestones[].idstring
progressBilling.milestones[].namestring
progressBilling.milestones[].percentnumber
progressBilling.milestones[].amountCentsinteger
progressBilling.milestones[].invoiceIdstring or null
progressBilling.milestones[].invoiceIdentifierstring or null
progressBilling.invoicesarray of object
progressBilling.invoices[].idstring
progressBilling.invoices[].identifierstring
progressBilling.invoices[].statusenumOne of DRAFT, SENT, PAID, OVERDUE, CANCELLED.
progressBilling.invoices[].roleenum or nullOne of PROGRESS, FINAL, RETAINAGE_RELEASE.
progressBilling.invoices[].milestoneIdstring or null
progressBilling.invoices[].billedCentsinteger
progressBilling.invoices[].retainageCentsintegerHeld (positive) or released (negative).
progressBilling.invoices[].totalCentsinteger
progressBilling.invoices[].amountPaidCentsinteger
progressBilling.invoices[].balanceDueCentsinteger
progressBilling.invoices[].dueDatestring or null
progressBilling.invoices[].createdAtstring (date-time)
progressBilling.summaryobject
progressBilling.summary.contractCentsinteger
progressBilling.summary.billedCentsintegerContract work billed to date, before retainage and tax.
progressBilling.summary.remainingCentsinteger
progressBilling.summary.percentBillednumber
progressBilling.summary.retainageHeldCentsinteger
progressBilling.summary.retainageReleasedCentsinteger
progressBilling.summary.retainageOutstandingCentsinteger
progressBilling.summary.invoicedTotalCentsintegerInvoice totals (after retainage, with tax).
progressBilling.summary.paidCentsinteger
progressBilling.summary.hasFinalboolean
progressBilling.summary.completeboolean
progressBilling.summary.finalProblemstring or nullWhy a final invoice cannot be made now, or null.
progressBilling.summary.releaseProblemstring or nullWhy the retainage cannot be released now, or null.
POST /organizations/{organizationId}/invoices/{invoiceId}/link

Exposes to the customer: returns the token for /i/{token}, the customer's page for this one invoice (no sign-in; anyone with the link can read it). Made on first call and then reused, so calling it again is safe. Drafts and cancelled invoices are not shown on that page until issued. Nothing is emailed.

Permission: invoices.send (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissioninvoices.send
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolinvoiceLinks_ensure (toolset invoices)
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals.

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}/link" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
tokenstringOpen the invoice at /i/{token} on the web app.
createdAtstring (date-time)
POST /organizations/{organizationId}/invoices/{invoiceId}/link/regenerate

Destructive: issues a new token; the previous /i/{token} link stops working at once.

Permission: invoices.send (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).

Permissioninvoices.send
Risksensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access.
MCP toolinvoiceLinks_regenerate (toolset invoices)
ApprovalOver MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals.

Parameters

NameInTypeRequiredDescription
invoiceIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/invoices/{invoiceId}/link/regenerate" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
tokenstringOpen the invoice at /i/{token} on the web app.
createdAtstring (date-time)

On this page