Field app API
REST endpoints for field app in ServFlo: 8 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.
| Operation | Endpoint | Risk |
|---|---|---|
| Share where I am | POST /organizations/{organizationId}/field/location | write |
| The technician app's offline packet | GET /organizations/{organizationId}/field/packet | read |
| Send the office a lead from a job | POST /organizations/{organizationId}/jobs/{jobId}/leads | write |
| The leads I sent | GET /organizations/{organizationId}/field/leads | read |
| The parts I asked for | GET /organizations/{organizationId}/field/parts-requests | read |
| Ask the office for parts | POST /organizations/{organizationId}/field/parts-requests | write |
| What my truck needs | GET /organizations/{organizationId}/field/restock | read |
| Send the customer the job's pay link | POST /organizations/{organizationId}/jobs/{jobId}/pay-link/send | sensitive |
Share where I am
POST /organizations/{organizationId}/field/location
The technician app sends its recent positions (oldest first or not; up to 100). Kept only while the caller is clocked in or on the way to a visit (sharing: false otherwise, and nothing is kept): the newest becomes their position on the office's map and the customer's tracking link, the rest their trail (two weeks).
| Permission | time.track.own (Owner, Admin, Dispatcher, Technician) |
| Risk | write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | tracking_share (toolset scheduling) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
pings | array of object | Yes | Up to 100 items. |
pings[].latitude | number | Yes | -90–90. |
pings[].longitude | number | Yes | -180–180. |
pings[].accuracyM | number or null | No | 0–100000. |
pings[].headingDeg | number or null | No | 0–360. |
pings[].speedMps | number or null | No | 0–200. |
pings[].at | string (date-time) | Yes |
Example
curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/field/location" \
-H "Authorization: Bearer $SERVFLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"pings":[{"latitude":1,"longitude":1,"at":"2026-11-03T15:00:00Z"}]}'Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
kept | integer | Positions stored. |
sharing | boolean | Whether the technician shares their position now (on the clock or on the way). |
The technician app's offline packet
GET /organizations/{organizationId}/field/packet
Everything the technician app keeps on the phone to work without signal: the caller's My day for today and tomorrow, each of those jobs in full (customer, property and access notes, equipment, checklist, notes, materials, forms with answers, invoices) with its pay-later link token, the pricebook at selling prices when the caller may quote, the shift clock, and the business's switches for quoting and collecting. A technician gets only their own jobs. Read-only; safe to call on every sync.
Permission: jobs.read (Owner, Admin, Dispatcher, Technician (own jobs), Member); risk read (OAuth scope servflo.read).
| Permission | jobs.read |
| Risk | read, OAuth scope servflo.read. Read: reads only. |
| MCP tool | fieldApp_packet (toolset scheduling) |
Example
curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/field/packet" \
-H "Authorization: Bearer $SERVFLO_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
generatedAt | string | When the packet was built (ISO timestamp). |
today | string | The business's today, YYYY-MM-DD. |
timeZone | string | |
days | any | My day for today and tomorrow, as GET /my-day returns it: the caller's visits in route order. |
jobs | array of object | Every job on those days, as GET /jobs/{jobId} returns it, with its forms. |
jobs[].id | string | Job id. |
jobs[].organizationId | string | |
jobs[].identifier | string | JOB-n, unique per organization. |
jobs[].customerId | string | |
jobs[].propertyId | string or null | |
jobs[].assetId | string or null | The equipment worked on. |
jobs[].technicianId | string or null | The lead technician of the next open visit. |
jobs[].serviceTypeId | string or null | |
jobs[].status | enum | SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED or CANCELLED. A job's status is its next open visit's. One of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED. |
jobs[].priority | enum | One of LOW, NORMAL, HIGH, URGENT. |
jobs[].scheduledDate | string | The next open visit's day, YYYY-MM-DD. |
jobs[].scheduledTime | string or null | Hh: MM |
jobs[].durationMinutes | integer | |
jobs[].priceCents | integer | Job price before tax, in integer cents. |
jobs[].address | string or null | |
jobs[].notes | string or null | |
jobs[].completionNotes | string or null | What was done. |
jobs[].recurrenceInterval | enum or null | One of WEEKLY, BIWEEKLY, MONTHLY, QUARTERLY, YEARLY. |
jobs[].recurrenceParentId | string or null | |
jobs[].onMyWayAt | string (date-time) or null | |
jobs[].arrivedAt | string (date-time) or null | |
jobs[].startedAt | string (date-time) or null | |
jobs[].completedAt | string (date-time) or null | |
jobs[].signedBy | string or null | Who signed the work off on site. |
jobs[].signedAt | string (date-time) or null | |
jobs[].businessUnitId | string or null | |
jobs[].customFields | any | Custom field values by key (definitions: GET /custom-fields?entity=JOB). |
jobs[].weatherSensitive | boolean or null | Plans around the weather: overrides the service type; null follows it. |
jobs[].createdAt | string (date-time) | |
jobs[].updatedAt | string (date-time) | |
jobs[].customer | object | |
jobs[].customer.id | string | Customer id. |
jobs[].customer.name | string | |
jobs[].customer.phone | string or null | |
jobs[].technician | object or null | |
jobs[].technician.id | string | Technician id (GET /technicians). |
jobs[].technician.name | string | |
jobs[].technician.color | string | Calendar colour, #rrggbb. |
jobs[].serviceType | object or null | |
jobs[].serviceType.id | string | |
jobs[].serviceType.name | string | |
jobs[].serviceType.color | string | |
jobs[].businessUnit | object or null | The business unit. |
jobs[].businessUnit.id | string | |
jobs[].businessUnit.name | string | |
jobs[].asset | object or null | |
jobs[].asset.id | string | |
jobs[].asset.name | string | |
jobs[].asset.serialNumber | string | |
jobs[].property | object or null | |
jobs[].property.id | string | Property id. |
jobs[].property.name | string or null | |
jobs[].property.address | string or null | |
jobs[].property.city | string or null | |
jobs[].property.taxRate | number or null | |
jobs[].property.gateCode | string or null | |
jobs[].property.accessInstructions | string or null | |
jobs[].property.safetyNotes | string or null | |
jobs[].property.petsOnSite | boolean | |
jobs[].property.petNotes | string or null | |
jobs[].property.contactName | string or null | |
jobs[].property.contactPhone | string or null | |
jobs[].property.latitude | number or null | |
jobs[].property.longitude | number or null | |
jobs[].nextJobId | string or null | After an update: the next occurrence booked when a recurring job completed. |
jobs[].notesLog | array of object | Internal notes, newest first. |
jobs[].notesLog[].id | string | Job note id. |
jobs[].notesLog[].jobId | string | |
jobs[].notesLog[].content | string | |
jobs[].notesLog[].authorUserId | string or null | |
jobs[].notesLog[].createdAt | string (date-time) | |
jobs[].checklist | array of object | |
jobs[].checklist[].id | string | Checklist item id. |
jobs[].checklist[].jobId | string | |
jobs[].checklist[].label | string | |
jobs[].checklist[].checked | boolean | |
jobs[].checklist[].sortOrder | integer | |
jobs[].materials | array of object | |
jobs[].materials[].id | string | Material line id. |
jobs[].materials[].jobId | string | |
jobs[].materials[].materialId | string or null | |
jobs[].materials[].pricebookItemId | string or null | |
jobs[].materials[].description | string or null | |
jobs[].materials[].unit | string or null | |
jobs[].materials[].quantity | number | |
jobs[].materials[].unitCostCents | integer | The unit PRICE billed for this line (the original API's name), in integer cents. |
jobs[].materials[].costCents | integer or null | Our unit cost in cents (costing.read only). |
jobs[].materials[].locationId | string or null | The stock location it was drawn from. |
jobs[].materials[].createdAt | string (date-time) | |
jobs[].materials[].material | object | The original API's material shape; id null for a pricebook-only line. |
jobs[].materials[].pricebookItem | object or null | |
jobs[].materials[].location | object or null | |
jobs[].invoices | array of object | Invoices for the job (INV-n). |
jobs[].invoices[].id | string | |
jobs[].invoices[].identifier | string | INV-n |
jobs[].invoices[].status | enum | DRAFT (not issued), SENT (issued, money due), PAID, OVERDUE (past dueDate) or CANCELLED. One of DRAFT, SENT, PAID, OVERDUE, CANCELLED. |
jobs[].invoices[].totalCents | integer | |
jobs[].quotes | array of object | Quotes the job came from (Q-n). |
jobs[].quotes[].id | string | |
jobs[].quotes[].identifier | string | Q-n |
jobs[].quotes[].status | enum | DRAFT → 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. |
jobs[].requests | array of object | |
jobs[].requests[].id | string | |
jobs[].requests[].identifier | string | |
jobs[].assessmentRequests | array of object | |
jobs[].assessmentRequests[].id | string | |
jobs[].assessmentRequests[].identifier | string | |
jobs[].visits | array of object | The job's visits in order. |
jobs[].visits[].id | string | Visit id (for /jobs/{jobId}/visits/{visitId}/…). |
jobs[].visits[].jobId | string | |
jobs[].visits[].scheduledDate | string or null | YYYY-MM-DD; null = not scheduled yet. |
jobs[].visits[].scheduledTime | string or null | Hh: MM |
jobs[].visits[].durationMinutes | integer | |
jobs[].visits[].status | enum | SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED or CANCELLED. A job's status is its next open visit's. One of SCHEDULED, CONFIRMED, IN_PROGRESS, COMPLETED, CANCELLED. |
jobs[].visits[].notes | string or null | |
jobs[].visits[].sortOrder | integer | |
jobs[].visits[].routeOrder | integer or null | |
jobs[].visits[].onMyWayAt | string (date-time) or null | |
jobs[].visits[].trackingToken | string or null | The customer's tracking link token (/t/{token}), made on On my way. |
jobs[].visits[].arrivedAt | string (date-time) or null | |
jobs[].visits[].startedAt | string (date-time) or null | |
jobs[].visits[].completedAt | string (date-time) or null | |
jobs[].visits[].createdAt | string (date-time) | |
jobs[].visits[].updatedAt | string (date-time) | |
jobs[].visits[].crew | array of object | Technicians on the visit, lead first. |
jobs[].forms | any | The job's forms with their fields, answers, status and missing required fields (as GET /jobs/{jobId}/forms). |
jobs[].photoCount | integer | |
jobs[].membership | object or null | The Member badge: the customer's membership that covers the job. |
jobs[].membership.membershipId | string | |
jobs[].membership.identifier | string | MEM-n |
jobs[].membership.planName | string | |
jobs[].membership.priority | boolean | The plan flags its members' work as priority on the board. |
jobs[].membership.dueMonth | string or null | YYYY-MM when the job is an included member visit; null for other work for a member. |
jobs[].membership.dueDate | string or null | YYYY-MM-DD when the member visit is on a week cadence (every N weeks); null for month-based visits. |
jobs[].payToken | string | The job's pay-later link token (/p/{token}). |
jobs[].background | any | Why the technician is going: the request, the quote sold, recent visits, equipment and contacts. |
pricebook | array of object | Active pricebook items for quoting offline (empty when the caller cannot quote). |
pricebook[].id | string | Pricebook item id. |
pricebook[].kind | enum | One of SERVICE, MATERIAL, EQUIPMENT, FEE. |
pricebook[].code | string or null | |
pricebook[].name | string | |
pricebook[].description | string or null | |
pricebook[].unit | string | |
pricebook[].categoryId | string or null | |
pricebook[].priceCents | integer | Selling price in cents. |
pricebook[].memberPriceCents | integer or null | |
pricebook[].taxable | boolean | |
pricebook[].imageId | string or null | |
pricebook[].imageUrl | string or null | The item's photo, valid for a short time. |
clock | any | The caller's time clock (as GET /time/clock). |
customFields | any | Custom field definitions for jobs, visits and equipment (all types and each type's, equipmentTypeId); the values travel on each record's customFields. |
equipmentTypes | any | The business's equipment types (id, name, icon, customerOwned, built-in columns and labels), for recording new equipment on site. |
equipmentLabels | object | What equipment is called here (e.g. Vehicle / Vehicles). |
equipmentLabels.singular | string | |
equipmentLabels.plural | string | |
switches | object | |
switches.technicianQuotes | boolean | |
switches.technicianPayments | boolean | |
features | object | |
features.equipment | boolean | |
features.expenses | boolean | |
features.timeTracking | boolean | The clock (clock in, breaks, non-job time) is time tracking; without it only a running shift can be ended. |
features.serviceReports | boolean | Service reports can be filled in (Grow and Pro, or the add-on); without it they are read-only. |
features.inventory | boolean | My truck and parts requests (inventory, Pro); without it the app hides them. |
features.phone | boolean | The phone system add-on (numbers and calls). |
payLinkBase | string | The pay-later link base URL; append the job's payToken. |
access | object | |
access.office | boolean | Sees the whole business (office mode). |
access.technician | boolean | The caller is a technician. |
access.permissions | array of string | The caller's permissions: which actions the app offers. |
signingTerms | object | What customers agree to when they sign on the phone. |
signingTerms.signOffTerms | string | |
signingTerms.quoteTerms | string | |
signingTerms.membershipTerms | string | |
serviceReports | object | Service reports to fill offline; empty without serviceReports.read. |
serviceReports.reports | any | The service reports on these jobs as GET /service-reports/{reportId} returns them (sections, items with status and notes, findings with photos, settings with statuses and severities, release state), without client links, agreements or extra documents. |
serviceReports.library | any | The comment library of those reports' templates (tagged to template items by itemIds), for searching and adding findings offline. |
serviceReports.extraSections | any | The extra-section catalog (Detached garage, Well, Radon …: items, describe fields, comments) to add offline with serviceReports.addExtraSection; empty without reports. |
serviceReports.agreements | any | Pending and signed pre-service agreements of the jobs with reports: frozen wording (safe HTML) and its textHash, status, signer name and time. Never the drawn signature, IP or link. Sign on site with POST …/agreements/{agreementId}/sign and the textHash shown. |
serviceReports.agreementDrafts | any | For a job with reports but no pending or signed agreement: the wording POST …/agreements would freeze now (agreementTemplateId, title, text, textHash), to show and sign offline; prepare it, then sign with that textHash (refused if it changed). |
membershipPlans | array of object | Plans a technician can sell on site (empty otherwise). |
membershipPlans[].id | string | Membership plan id. |
membershipPlans[].name | string | |
membershipPlans[].description | string or null | |
membershipPlans[].priceCents | integer | |
membershipPlans[].billingFrequency | enum | One of WEEKLY, BIWEEKLY, EVERY_4_WEEKS, MONTHLY, QUARTERLY, SEMI_ANNUAL, ANNUAL, ONE_TIME. |
membershipPlans[].termMonths | integer or null | |
membershipPlans[].autoRenewDefault | boolean | |
membershipPlans[].serviceDiscountPercent | number | |
membershipPlans[].materialDiscountPercent | number | |
membershipPlans[].priority | boolean | |
membershipPlans[].includedVisitCount | integer |
Send the office a lead from a job
POST /organizations/{organizationId}/jobs/{jobId}/leads
A technician recommends work they saw on one of their jobs (an aging unit, a leak, an upgrade): creates a NEW request (source FIELD) for the job's customer and property, with the job, the technician and how soon (urgency: NOW unsafe or failing, SOON within weeks, LATER plan for it). The office sees it in Requests and quotes it. Technicians: their own jobs (404 otherwise). Returns the request.
| Permission | jobs.work (Owner, Admin, Dispatcher, Technician) |
| Risk | write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | fieldApp_sendLead (toolset scheduling) |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
jobId | path | string | Yes |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
description | string | Yes | Up to 5000 characters. |
urgency | enum | Yes | One of NOW, SOON, LATER. |
serviceTypeId | string or null | No | From GET /service-types, when the technician knows it. |
Example
curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/leads" \
-H "Authorization: Bearer $SERVFLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"description":"string","urgency":"NOW"}'Response
201: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | Request id. |
organizationId | string | |
identifier | string | REQ-n, unique per organization. |
customerId | string or null | Null while the request is from a new contact. |
contactName | string or null | |
contactCompany | string or null | |
contactPhone | string or null | |
contactEmail | string or null | |
propertyId | string or null | |
address | string or null | |
serviceTypeId | string or null | |
description | string | What the customer asked for. |
preferredDate | string or null | YYYY-MM-DD |
alternateDate | string or null | YYYY-MM-DD |
preferredTimeWindow | enum | One of ANY, MORNING, AFTERNOON, EVENING. |
bookedDate | string or null | Online booking's slot day, YYYY-MM-DD. |
bookedTime | string or null | Hh: MM |
bookedDurationMinutes | integer or null | |
bookedTechnicianId | string or null | |
source | enum | Where it came from: MANUAL (staff), ONLINE_BOOKING, PORTAL, FIELD (a technician's lead) or PHONE (taken by the AI receptionist on a call). One of MANUAL, ONLINE_BOOKING, PORTAL, FIELD, PHONE. |
leadTechnicianId | string or null | The technician who sent a FIELD lead. |
sourceJobId | string or null | The job a FIELD lead came from. |
leadUrgency | enum or null | One of NOW, SOON, LATER. |
status | enum | NEW → ASSESSMENT_SCHEDULED / QUOTED → CONVERTED (a job was booked), or ARCHIVED. One of NEW, ASSESSMENT_SCHEDULED, QUOTED, CONVERTED, ARCHIVED. |
internalNotes | string or null | |
jobId | string or null | The job booked from it. |
assessmentJobId | string or null | The on-site assessment job. |
convertedAt | string (date-time) or null | |
archivedAt | string (date-time) or null | |
businessUnitId | string or null | |
adCampaignId | string or null | The ad campaign that brought it; null when not from an ad. |
createdAt | string (date-time) | |
updatedAt | string (date-time) | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
property | object or null | |
property.id | string | Property id. |
property.name | string or null | |
property.address | string or null | |
property.city | string or null | |
serviceType | object or null | |
serviceType.id | string | |
serviceType.name | string | |
serviceType.color | string | |
businessUnit | object or null | The business unit. |
businessUnit.id | string | |
businessUnit.name | string | |
bookedTechnician | object or null | |
bookedTechnician.id | string | Technician id (GET /technicians). |
bookedTechnician.name | string | |
bookedTechnician.color | string | Calendar colour, #rrggbb. |
leadTechnician | object or null | |
leadTechnician.id | string | Technician id (GET /technicians). |
leadTechnician.name | string | |
leadTechnician.color | string | Calendar colour, #rrggbb. |
sourceJob | object or null | |
sourceJob.id | string | |
sourceJob.identifier | string |
The leads I sent
GET /organizations/{organizationId}/field/leads
The caller's leads from the field, newest first, with their status (NEW, ASSESSMENT_SCHEDULED, QUOTED, CONVERTED, ARCHIVED) and the latest quote made from each. Read-only.
Permission: jobs.read (Owner, Admin, Dispatcher, Technician (own jobs), Member); risk read (OAuth scope servflo.read).
| Permission | jobs.read |
| Risk | read, OAuth scope servflo.read. Read: reads only. |
| MCP tool | fieldApp_myLeads (toolset scheduling) |
Example
curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/field/leads" \
-H "Authorization: Bearer $SERVFLO_API_KEY"Response
200: the answer's fields
An array of objects:
| Field | Type | Description |
|---|---|---|
id | string | Request id. |
identifier | string | REQ-n |
description | string | |
status | enum | NEW → ASSESSMENT_SCHEDULED / QUOTED → CONVERTED (a job was booked), or ARCHIVED. One of NEW, ASSESSMENT_SCHEDULED, QUOTED, CONVERTED, ARCHIVED. |
leadUrgency | enum or null | One of NOW, SOON, LATER. |
createdAt | string (date-time) | |
customer | object or null | |
customer.id | string | |
customer.name | string | |
sourceJob | object or null | |
sourceJob.id | string | |
sourceJob.identifier | string | |
quotes | array of object | The latest quote made from it, if any. |
quotes[].id | string | |
quotes[].identifier | string | |
quotes[].status | enum | DRAFT → 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. |
The parts I asked for
GET /organizations/{organizationId}/field/parts-requests
The caller's parts requests, newest first, with status and the office's note. Read-only.
Permission: jobs.read (Owner, Admin, Dispatcher, Technician (own jobs), Member); risk read (OAuth scope servflo.read).
| Permission | jobs.read |
| Risk | read, OAuth scope servflo.read. Read: reads only. |
| MCP tool | fieldApp_myPartsRequests (toolset scheduling) |
Example
curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/field/parts-requests" \
-H "Authorization: Bearer $SERVFLO_API_KEY"Response
200: the answer's fields
An array of objects:
| Field | Type | Description |
|---|---|---|
id | string | Parts request id. |
organizationId | string | |
identifier | string | PRQ-n, unique per organization. |
kind | enum | For a job, or to restock the truck. One of JOB, RESTOCK. |
status | enum | REQUESTED → ORDERED (from a supplier) → READY (waiting for pickup) → DONE (on the truck), or CANCELLED. One of REQUESTED, ORDERED, READY, DONE, CANCELLED. |
technicianId | string or null | |
requestedByUserId | string | |
jobId | string or null | |
truckId | string or null | The technician's truck (inventory location). |
neededBy | string or null | YYYY-MM-DD |
note | string or null | |
officeNote | string or null | The office's answer to the technician. |
statusChangedAt | string (date-time) | |
createdAt | string (date-time) | |
updatedAt | string (date-time) | |
technician | object or null | |
technician.id | string | Technician id (GET /technicians). |
technician.name | string | |
technician.color | string | Calendar colour, #rrggbb. |
job | object or null | |
job.id | string | |
job.identifier | string | JOB-n |
job.customer | object | |
job.customer.name | string | |
truck | object or null | |
truck.id | string | |
truck.name | string | |
lines | array of object | |
lines[].id | string | |
lines[].itemId | string or null | The pricebook item, when it is one. |
lines[].description | string | |
lines[].quantity | number | |
lines[].item | object or null | |
lines[].item.code | string or null | |
lines[].item.name | string |
Ask the office for parts
POST /organizations/{organizationId}/field/parts-requests
A technician asks for parts for one of their jobs (kind JOB, jobId) or to restock their truck (RESTOCK): lines from the pricebook (itemId) or described in words, with when they're needed. Creates PRQ-n for the office (Inventory → Parts requests); the technician's phone is told when it's on order or ready. Returns it.
| Permission | jobs.work (Owner, Admin, Dispatcher, Technician) |
| Risk | write, OAuth scope servflo.write. Write: creates or changes records inside the business; nothing leaves it. |
| MCP tool | fieldApp_requestParts (toolset scheduling) |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
kind | enum | Yes | One of JOB, RESTOCK. |
jobId | string or null | No | |
neededBy | string or null | No | |
note | string or null | No | Up to 2000 characters. |
lines | array of object | Yes | Up to 100 items. |
lines[].itemId | string or null | No | |
lines[].description | string | Yes | Up to 500 characters. |
lines[].quantity | number | Yes | At most 100000. |
Example
curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/field/parts-requests" \
-H "Authorization: Bearer $SERVFLO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"kind":"JOB","lines":[{"description":"string","quantity":1}]}'Response
201: the answer's fields
| Field | Type | Description |
|---|---|---|
id | string | Parts request id. |
organizationId | string | |
identifier | string | PRQ-n, unique per organization. |
kind | enum | For a job, or to restock the truck. One of JOB, RESTOCK. |
status | enum | REQUESTED → ORDERED (from a supplier) → READY (waiting for pickup) → DONE (on the truck), or CANCELLED. One of REQUESTED, ORDERED, READY, DONE, CANCELLED. |
technicianId | string or null | |
requestedByUserId | string | |
jobId | string or null | |
truckId | string or null | The technician's truck (inventory location). |
neededBy | string or null | YYYY-MM-DD |
note | string or null | |
officeNote | string or null | The office's answer to the technician. |
statusChangedAt | string (date-time) | |
createdAt | string (date-time) | |
updatedAt | string (date-time) | |
technician | object or null | |
technician.id | string | Technician id (GET /technicians). |
technician.name | string | |
technician.color | string | Calendar colour, #rrggbb. |
job | object or null | |
job.id | string | |
job.identifier | string | JOB-n |
job.customer | object | |
job.customer.name | string | |
truck | object or null | |
truck.id | string | |
truck.name | string | |
lines | array of object | |
lines[].id | string | |
lines[].itemId | string or null | The pricebook item, when it is one. |
lines[].description | string | |
lines[].quantity | number | |
lines[].item | object or null | |
lines[].item.code | string or null | |
lines[].item.name | string |
What my truck needs
GET /organizations/{organizationId}/field/restock
Every item on the caller's truck at or below its reorder point, with the quantity to ask for (the reorder quantity, else back above the point). Read-only.
Permission: jobs.read (Owner, Admin, Dispatcher, Technician (own jobs), Member); risk read (OAuth scope servflo.read).
| Permission | jobs.read |
| Risk | read, OAuth scope servflo.read. Read: reads only. |
| MCP tool | fieldApp_restockSuggestion (toolset scheduling) |
Example
curl "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/field/restock" \
-H "Authorization: Bearer $SERVFLO_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
truckId | string or null | The technician's truck; null when they have none. |
lines | array of object | Items at or below their reorder point, with the quantity to ask for. |
lines[].itemId | string | Pricebook item id. |
lines[].description | string | |
lines[].quantity | number |
Send the customer the job's pay link
POST /organizations/{organizationId}/jobs/{jobId}/pay-link/send
Sends to the customer: texts (else emails) the job's customer its pay-later link (/p/{token}), which opens the job's invoice to pay online. The technician app calls it when the phone reconnects after a customer chose to pay later. Consent rules apply (no text without SMS consent; then email). Technicians: their own jobs. Returns the link and how it went out.
Permission: invoices.send (Owner, Admin, Dispatcher, Technician (own jobs' invoices)); risk sensitive (OAuth scope servflo.sensitive).
| Permission | invoices.send |
| Risk | sensitive, OAuth scope servflo.sensitive. Sensitive: moves money, deletes, sends or shows something to a customer, or grants or revokes access. |
| MCP tool | fieldApp_sendPayLink (toolset scheduling) |
| Approval | Over MCP, this tool asks for your OK first (in the AI app when it supports it, otherwise in ServFlo). See Approvals. |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
jobId | path | string | Yes |
Example
curl -X POST "https://app.servflo.ai/api/organizations/$ORGANIZATION_ID/jobs/{jobId}/pay-link/send" \
-H "Authorization: Bearer $SERVFLO_API_KEY"Response
200: the answer's fields
| Field | Type | Description |
|---|---|---|
url | string | The pay-later link (/p/{token}). |
sent | boolean | Whether a text or email went out. |
channel | enum or null | One of SMS, EMAIL. |