DevelopersAPI referenceProjects

Change orders API

REST endpoints for change orders in ServFlo: 10 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 change ordersGET /organizations/{organizationId}/change-ordersread
A change orderGET /organizations/{organizationId}/change-orders/{changeOrderId}read
Change a draft change orderPATCH /organizations/{organizationId}/change-orders/{changeOrderId}write
Delete a change orderDELETE /organizations/{organizationId}/change-orders/{changeOrderId}sensitive
Write a change orderPOST /organizations/{organizationId}/projects/{projectId}/change-orderswrite
Send a change order to the customerPOST /organizations/{organizationId}/change-orders/{changeOrderId}/sendsensitive
Text or email a change order's link againPOST /organizations/{organizationId}/change-orders/{changeOrderId}/resendsensitive
Back to draftPOST /organizations/{organizationId}/change-orders/{changeOrderId}/reopenwrite
Record the customer's approvalPOST /organizations/{organizationId}/change-orders/{changeOrderId}/approvesensitive
Record that the customer declinedPOST /organizations/{organizationId}/change-orders/{changeOrderId}/declinewrite

List change orders

GET /organizations/{organizationId}/change-orders

Change orders, newest first, of a project (projectId) or all, optionally by status (DRAFT, SENT, APPROVED, DECLINED). Read-only.

Permissionprojects.read (Owner, Admin, Dispatcher, Technician, Member)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolchangeOrders_list (toolset projects)

Parameters

NameInTypeRequiredDescription
projectIdquerystringNo
statusqueryenumNoOne of DRAFT, SENT, APPROVED, DECLINED.
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/change-orders" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
itemsarray of object
items[].idstring
items[].projectIdstring
items[].labelstringCO-n within the project.
items[].titlestring
items[].statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
items[].priceChangeCentsintegerBefore tax; negative for a credit.
items[].scheduleDaysinteger
items[].linesarray of object
items[].lines[].descriptionstring
items[].lines[].quantitynumber
items[].lines[].unitPriceCentsinteger
items[].urlstringThe customer's page to approve it (/co/{token}); works once SENT.
totalinteger

A change order

GET /organizations/{organizationId}/change-orders/{changeOrderId}

A change order with its lines, status, the customer's decision and its approval link. Read-only.

Permissionprojects.read (Owner, Admin, Dispatcher, Technician, Member)
Riskread, OAuth scope servflo.read. Read: reads only.
MCP toolchangeOrders_get (toolset projects)

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Example

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

Response

200: the answer's fields
FieldTypeDescription
idstring
projectIdstring
labelstringCO-n within the project.
titlestring
statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
priceChangeCentsintegerBefore tax; negative for a credit.
scheduleDaysinteger
linesarray of object
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
urlstringThe customer's page to approve it (/co/{token}); works once SENT.

Change a draft change order

PATCH /organizations/{organizationId}/change-orders/{changeOrderId}

Changes a DRAFT (its title, description, schedule days or all its lines). 409 once sent; reopen it first.

Permissionprojects.field (Owner, Admin, Dispatcher, Technician)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolchangeOrders_update (toolset projects)

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Request body

FieldTypeRequiredDescription
titlestringNoUp to 200 characters.
descriptionstring or nullNoUp to 5000 characters.
scheduleDaysintegerNo-365–365.
linesarray of objectNoUp to 100 items.
lines[].descriptionstringYesUp to 500 characters.
lines[].quantitynumberYes-1000000–1000000.
lines[].unitPriceCentsintegerYesPrice to the customer per unit; negative for a credit. -100000000–100000000.
lines[].costCentsinteger or nullNoOur unit cost (goes to the budget when approved). -100000000–100000000.
lines[].costCategoryenum or nullNoBudget bucket: LABOR, MATERIALS, SUBCONTRACTORS, EQUIPMENT or OTHER. One of LABOR, MATERIALS, SUBCONTRACTORS, EQUIPMENT, OTHER.
lines[].pricebookItemIdstring or nullNoPicked from the pricebook (GET /pricebook/items).

Example

curl -X PATCH "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/change-orders/{changeOrderId}" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"string","description":"string","scheduleDays":1}'

Response

200: the answer's fields
FieldTypeDescription
idstring
projectIdstring
labelstringCO-n within the project.
titlestring
statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
priceChangeCentsintegerBefore tax; negative for a credit.
scheduleDaysinteger
linesarray of object
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
urlstringThe customer's page to approve it (/co/{token}); works once SENT.

Delete a change order

DELETE /organizations/{organizationId}/change-orders/{changeOrderId}

Destructive: deletes a DRAFT or DECLINED change order. An approved one is part of the contract (write a new one to undo it).

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

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Example

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

Response

200: the answer's fields
FieldTypeDescription
oktrue

Write a change order

POST /organizations/{organizationId}/projects/{projectId}/change-orders

A DRAFT change order (CO-n) on a project: a title, lines (from the pricebook with pricebookItemId, or typed; a negative price is a credit) and the schedule change in working days. Nothing reaches the customer until it is sent (POST …/send). Technicians: their own projects (a draft for the office).

Permissionprojects.field (Owner, Admin, Dispatcher, Technician)
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolchangeOrders_create (toolset projects)

Parameters

NameInTypeRequiredDescription
projectIdpathstringYesThe project (GET /projects).

Request body

FieldTypeRequiredDescription
titlestringYesUp to 200 characters.
descriptionstring or nullNoUp to 5000 characters.
scheduleDaysintegerNoWorking days added (negative: taken off). -365–365. Default 0.
linesarray of objectNoUp to 100 items. Default [].
lines[].descriptionstringYesUp to 500 characters.
lines[].quantitynumberYes-1000000–1000000.
lines[].unitPriceCentsintegerYesPrice to the customer per unit; negative for a credit. -100000000–100000000.
lines[].costCentsinteger or nullNoOur unit cost (goes to the budget when approved). -100000000–100000000.
lines[].costCategoryenum or nullNoBudget bucket: LABOR, MATERIALS, SUBCONTRACTORS, EQUIPMENT or OTHER. One of LABOR, MATERIALS, SUBCONTRACTORS, EQUIPMENT, OTHER.
lines[].pricebookItemIdstring or nullNoPicked from the pricebook (GET /pricebook/items).

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/projects/{projectId}/change-orders" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"string"}'

Response

201: the answer's fields
FieldTypeDescription
idstring
projectIdstring
labelstringCO-n within the project.
titlestring
statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
priceChangeCentsintegerBefore tax; negative for a credit.
scheduleDaysinteger
linesarray of object
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
urlstringThe customer's page to approve it (/co/{token}); works once SENT.

Send a change order to the customer

POST /organizations/{organizationId}/change-orders/{changeOrderId}/send

Sends to the customer: marks it SENT so the customer can approve or decline it on its link and in the portal, and (unless channels is empty) emails and/or texts them the link (consent and quiet hours apply). Technicians only when the business lets the crew send change orders (else 409: it stays a draft for the office).

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

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

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Request body

FieldTypeRequiredDescription
channelsarray of enumNoUp to 2 items. Default ["EMAIL","SMS"].
notestring or nullNoUp to 1000 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/change-orders/{changeOrderId}/send" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channels":["EMAIL","SMS"],"note":"string"}'

Response

200: the answer's fields
FieldTypeDescription
changeOrderobject
changeOrder.idstring
changeOrder.projectIdstring
changeOrder.labelstringCO-n within the project.
changeOrder.titlestring
changeOrder.statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
changeOrder.priceChangeCentsintegerBefore tax; negative for a credit.
changeOrder.scheduleDaysinteger
changeOrder.linesarray of object
changeOrder.lines[].descriptionstring
changeOrder.lines[].quantitynumber
changeOrder.lines[].unitPriceCentsinteger
changeOrder.urlstringThe customer's page to approve it (/co/{token}); works once SENT.
messagesarray of object
messages[].channelstring
messages[].statusstring

Text or email a change order's link again

POST /organizations/{organizationId}/change-orders/{changeOrderId}/resend

Sends to the customer: the link of a SENT change order again by email and/or text (consent and quiet hours apply).

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

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

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Request body

FieldTypeRequiredDescription
channelsarray of enumNoDefault both; each is subject to the customer's consent and quiet hours. Up to 2 items. Default ["EMAIL","SMS"].
notestring or nullNoUp to 1000 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/change-orders/{changeOrderId}/resend" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channels":["EMAIL","SMS"],"note":"string"}'

Response

200: the answer's fields

An array of objects:

FieldTypeDescription
channelstring
statusstring

Back to draft

POST /organizations/{organizationId}/change-orders/{changeOrderId}/reopen

Takes a SENT or DECLINED change order back to DRAFT to edit it (the customer can no longer answer until it is sent again).

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

PermissionchangeOrders.send
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolchangeOrders_reopen (toolset projects)

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/change-orders/{changeOrderId}/reopen" \
  -H "Authorization: Bearer $SERVFLO_API_KEY"

Response

200: the answer's fields
FieldTypeDescription
idstring
projectIdstring
labelstringCO-n within the project.
titlestring
statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
priceChangeCentsintegerBefore tax; negative for a credit.
scheduleDaysinteger
linesarray of object
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
urlstringThe customer's page to approve it (/co/{token}); works once SENT.

Record the customer's approval

POST /organizations/{organizationId}/change-orders/{changeOrderId}/approve

ACTS FOR THE CUSTOMER (signed on site or said yes by phone): APPROVED is final. The contract grows by the price change, each line's cost joins the budget, the project's target end moves by the schedule days, and a progress billing gains it as a contract line. signatureDataUrl is the PNG signature drawn on the device.

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

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

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Request body

FieldTypeRequiredDescription
signatureNamestring or nullNoUp to 200 characters.
signatureDataUrlstring or nullNo
termsstring or nullNoThe terms shown; omitted: the business's change-order terms. Up to 5000 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/change-orders/{changeOrderId}/approve" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"signatureName":"string","signatureDataUrl":"https://example.com","terms":"string"}'

Response

200: the answer's fields
FieldTypeDescription
idstring
projectIdstring
labelstringCO-n within the project.
titlestring
statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
priceChangeCentsintegerBefore tax; negative for a credit.
scheduleDaysinteger
linesarray of object
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
urlstringThe customer's page to approve it (/co/{token}); works once SENT.

Record that the customer declined

POST /organizations/{organizationId}/change-orders/{changeOrderId}/decline

Marks it DECLINED with the reason; nothing changes in the contract.

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

PermissionchangeOrders.send
Riskwrite, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it.
MCP toolchangeOrders_decline (toolset projects)

Parameters

NameInTypeRequiredDescription
changeOrderIdpathstringYes

Request body

FieldTypeRequiredDescription
reasonstring or nullNoUp to 2000 characters.

Example

curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/change-orders/{changeOrderId}/decline" \
  -H "Authorization: Bearer $SERVFLO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason":"string"}'

Response

200: the answer's fields
FieldTypeDescription
idstring
projectIdstring
labelstringCO-n within the project.
titlestring
statusenumOne of DRAFT, SENT, APPROVED, DECLINED.
priceChangeCentsintegerBefore tax; negative for a credit.
scheduleDaysinteger
linesarray of object
lines[].descriptionstring
lines[].quantitynumber
lines[].unitPriceCentsinteger
urlstringThe customer's page to approve it (/co/{token}); works once SENT.

On this page