REST API reference
Service Fusion API
15 endpoints for Service Fusion. Each one is a POST with a JSON body, called with your API key.
- Base URL
- https://cotlet.dev/api/v1/service-fusion
- Connects with
- Your own Service Fusion user
- Integration page
- Service Fusion
| Endpoint | Access | Description |
|---|---|---|
| list_customers | read | Customers matching a name, phone, email, address or tag, with last serviced date. |
| list_job_statuses | read | The job statuses your account uses, with their type. |
| list_jobs | read | Jobs with number, status, customer, assigned techs and start date. |
| get_job | read | Full job: description, location, techs, products, services, notes and charges. |
| list_estimates | read | Estimates with number, status, customer, requested date and options. |
| get_estimate | read | Estimate with each option, its line items and total. |
| list_invoices | read | Invoices with number, customer, total, date, paid flag and pay-online link. |
| create_customer | write | New customer with a primary contact and service location. |
| create_job | write | New job for a customer, scheduled or unscheduled, optionally with technicians assigned. |
| update_job_status | write | Move a job to another status, e.g. On The Way, Completed or Cancelled. |
| create_estimate | write | New estimate for a customer with products and services. Nothing is sent until send_estimate. |
| convert_estimate_to_job | write | Turn one estimate option into a job, the way Convert to Job does in the app. |
| create_invoice | write | Invoice a job from its products, services and charges. Nothing is sent or charged. |
| send_estimate | write | Email or text an estimate with a link to view and accept it online. |
| send_invoice | write | Email or text an invoice with a link to view and pay it online. |
List customers
readPOST https://cotlet.dev/api/v1/service-fusion/list_customers
Returns customers with their contacts, service location and last serviced date. Use it before create_customer to find a repeat caller by phone or email. create_job and create_estimate take the customer name exactly as shown here.
Parameters
| Name | Type | Description |
|---|---|---|
| query | string | Name, phone, email or address to search for. |
| tags | string[] | Only customers with these tags. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/list_customers \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("service-fusion", "list_customers") const result = await cotlet.call('service-fusion', 'list_customers'); cotlet call service-fusion list_customers Example response
{
"customers": [
{
"customer_id": "22814",
"customer_name": "Jane Doe",
"contact": {
"first_name": "Jane",
"last_name": "Doe",
"phone": "555-0142",
"email": "jane.doe@example.com"
},
"location": {
"street_1": "48 Maple Court",
"city": "Plano",
"state_prov": "TX",
"postal_code": "75024"
},
"tags": [
"Residential"
],
"last_serviced": "2026-04-18"
}
],
"page": 1
} List job statuses
readPOST https://cotlet.dev/api/v1/service-fusion/list_job_statuses
Returns the statuses from My Office > Estimate & Job Statuses. Accounts can rename statuses, so use these names in list_jobs and update_job_status. The defaults include Unscheduled, Scheduled, Dispatched, On The Way, On Site, Started, Completed, Cancelled, Job Closed and Invoiced.
Parameters
No parameters.
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/list_job_statuses \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("service-fusion", "list_job_statuses") const result = await cotlet.call('service-fusion', 'list_job_statuses'); cotlet call service-fusion list_job_statuses Example response
{
"statuses": [
{
"name": "Unscheduled",
"category": "Open"
},
{
"name": "Scheduled",
"category": "Open"
},
{
"name": "Dispatched",
"category": "Open"
},
{
"name": "On The Way",
"category": "In Progress"
},
{
"name": "Completed",
"category": "In Progress"
},
{
"name": "Invoiced",
"category": "Closed"
}
]
} List jobs
readPOST https://cotlet.dev/api/v1/service-fusion/list_jobs
Lists jobs. Filter by status, customer or a start date range, for example every job in Unscheduled. Each item carries the job_id for get_job, update_job_status and create_invoice. Status names are the ones from list_job_statuses.
Parameters
| Name | Type | Description |
|---|---|---|
| status | string | Status name from list_job_statuses, e.g. Unscheduled or Completed. |
| customer_name | string | Only jobs for this customer. |
| start_from | date | Jobs starting on or after this date. |
| start_to | date | Jobs starting on or before this date. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/list_jobs \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("service-fusion", "list_jobs") const result = await cotlet.call('service-fusion', 'list_jobs'); cotlet call service-fusion list_jobs Example response
{
"jobs": [
{
"job_id": "1048301",
"number": "10483",
"status": "Unscheduled",
"customer_name": "Mark Reyes",
"city": "Frisco",
"description": "Water heater leaking at base",
"start": null,
"techs_assigned": []
},
{
"job_id": "1048288",
"number": "10482",
"status": "Unscheduled",
"customer_name": "Priya Shah",
"city": "Plano",
"description": "Furnace not igniting",
"start": null,
"techs_assigned": []
}
],
"page": 1
} Get job
readPOST https://cotlet.dev/api/v1/service-fusion/get_job
Returns one job with its location, assigned technicians, products, services, other charges, notes and the invoice number once it is invoiced. Use it to check the status and charges before create_invoice.
Parameters
| Name | Type | Description |
|---|---|---|
| job_id required | string | Job ID from list_jobs. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/get_job \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_id":"job_8c41d2"}' result = client.call("service-fusion", "get_job", {"job_id":"job_8c41d2"}) const result = await cotlet.call('service-fusion', 'get_job', {"job_id":"job_8c41d2"}); cotlet call service-fusion get_job --data '{"job_id":"job_8c41d2"}' Example response
{
"job_id": "1048213",
"number": "10482",
"status": "Completed",
"customer_name": "Jane Doe",
"description": "AC tune-up, upstairs unit",
"location": {
"street_1": "48 Maple Court",
"city": "Plano",
"state_prov": "TX",
"postal_code": "75024"
},
"start": "2026-10-15T09:00:00-05:00",
"techs_assigned": [
"Alex Rivera"
],
"services": [
{
"name": "AC tune-up",
"quantity": 1,
"rate": 129
}
],
"products": [],
"total": 129,
"invoice_number": null
} List estimates
readPOST https://cotlet.dev/api/v1/service-fusion/list_estimates
Lists estimates. Filter by status, customer or the date the estimate was requested. Each item carries the estimate_id for get_estimate, convert_estimate_to_job and send_estimate.
Parameters
| Name | Type | Description |
|---|---|---|
| status | string | Estimate status as set in your account. |
| customer_name | string | Only estimates for this customer. |
| requested_from | date | Requested on or after this date. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/list_estimates \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("service-fusion", "list_estimates") const result = await cotlet.call('service-fusion', 'list_estimates'); cotlet call service-fusion list_estimates Example response
{
"estimates": [
{
"estimate_id": "3317",
"number": "3317",
"status": "Estimate Sent",
"customer_name": "Carol Smith",
"requested_on": "2026-10-08",
"options": [
"Good",
"Better",
"Best"
]
}
],
"page": 1
} Get estimate
readPOST https://cotlet.dev/api/v1/service-fusion/get_estimate
Returns one estimate with up to five options. Each option has a name, line items and a total, and shows Converted once it has become a job. Use the option name in convert_estimate_to_job.
Parameters
| Name | Type | Description |
|---|---|---|
| estimate_id required | string | Estimate ID from list_estimates. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/get_estimate \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"estimate_id":"estimate_8c41d2"}' result = client.call("service-fusion", "get_estimate", {"estimate_id":"estimate_8c41d2"}) const result = await cotlet.call('service-fusion', 'get_estimate', {"estimate_id":"estimate_8c41d2"}); cotlet call service-fusion get_estimate --data '{"estimate_id":"estimate_8c41d2"}' Example response
{
"estimate_id": "3317",
"number": "3317",
"status": "Estimate Sent",
"customer_name": "Carol Smith",
"options": [
{
"name": "Good",
"total": 1450,
"converted": false,
"items": [
{
"name": "40 gal water heater, install",
"quantity": 1,
"rate": 1450
}
]
},
{
"name": "Better",
"total": 1890,
"converted": false,
"items": [
{
"name": "50 gal water heater, install",
"quantity": 1,
"rate": 1890
}
]
}
]
} List invoices
readPOST https://cotlet.dev/api/v1/service-fusion/list_invoices
Lists invoices, read from the invoice itself rather than from the job status. The Paid In Full job status is applied by hand, so use the paid flag here. Each item carries the invoice_id for send_invoice.
Parameters
| Name | Type | Description |
|---|---|---|
| paid | boolean | false: only unpaid invoices. true: only paid invoices. Default: both. |
| from | date | Invoices dated on or after this date. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/list_invoices \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("service-fusion", "list_invoices") const result = await cotlet.call('service-fusion', 'list_invoices'); cotlet call service-fusion list_invoices Example response
{
"invoices": [
{
"invoice_id": "90412",
"number": "9041",
"job_number": "10479",
"customer_name": "Luis Ortega",
"total": 340,
"paid": false,
"date": "2026-10-06",
"pay_online_url": "https://example.com/pay/9041"
}
],
"page": 1
} Create customer
writePOST https://cotlet.dev/api/v1/service-fusion/create_customer
Creates the customer record and returns its customer_name for create_job and create_estimate. It sends nothing to anyone and adds no charge, so it runs without asking for your OK. Search with list_customers first so a repeat customer is not created twice.
Parameters
| Name | Type | Description |
|---|---|---|
| customer_name required | string | Customer or company name. |
| contact | object | first_name, last_name, phone, email. |
| location | object | street_1, city, state_prov, postal_code. |
| referral_source | string | One of your referral sources. |
| tags | string[] | Customer tags. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/create_customer \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_name":"customer name"}' result = client.call("service-fusion", "create_customer", {"customer_name":"customer name"}) const result = await cotlet.call('service-fusion', 'create_customer', {"customer_name":"customer name"}); cotlet call service-fusion create_customer --data '{"customer_name":"customer name"}' Example response
{
"customer_id": "22901",
"customer_name": "Jane Doe",
"contact": {
"first_name": "Jane",
"last_name": "Doe",
"phone": "555-0142",
"email": "jane.doe@example.com"
},
"location": {
"street_1": "48 Maple Court",
"city": "Plano",
"state_prov": "TX",
"postal_code": "75024"
}
} POST https://cotlet.dev/api/v1/service-fusion/create_job
Creates a job under an existing customer. Without a start the job is Unscheduled; with a start and no technician it is Scheduled. If your Outbound Email Settings email customers when a job is created, the customer gets that email. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| customer_name required | string | Existing customer name from list_customers. |
| description required | string | What the job is. |
| category | string | One of your job categories. |
| start | datetime | Scheduled start, ISO 8601. Empty: Unscheduled. |
| duration_minutes | integer | Expected duration. |
| techs_assigned | string[] | Technician names as shown in your account. |
| note_to_customer | string | Shown to the customer. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/create_job \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_name":"Jane Doe","description":"AC tune-up, upstairs unit","category":"Maintenance","start":"2026-10-15T09:00:00-05:00","duration_minutes":90}' result = client.call("service-fusion", "create_job", {"customer_name":"Jane Doe","description":"AC tune-up, upstairs unit","category":"Maintenance","start":"2026-10-15T09:00:00-05:00","duration_minutes":90}) const result = await cotlet.call('service-fusion', 'create_job', {"customer_name":"Jane Doe","description":"AC tune-up, upstairs unit","category":"Maintenance","start":"2026-10-15T09:00:00-05:00","duration_minutes":90}); cotlet call service-fusion create_job --data '{"customer_name":"Jane Doe","description":"AC tune-up, upstairs unit","category":"Maintenance","start":"2026-10-15T09:00:00-05:00","duration_minutes":90}' Example response
{
"job_id": "1048213",
"number": "10482",
"status": "Scheduled",
"customer_name": "Jane Doe",
"description": "AC tune-up, upstairs unit",
"category": "Maintenance",
"start": "2026-10-15T09:00:00-05:00",
"duration_minutes": 90,
"techs_assigned": []
} POST https://cotlet.dev/api/v1/service-fusion/update_job_status
Sets the job to the status you name, as a technician or dispatcher would in the app. Moving a job to On The Way can send the customer a text notification, and Completed stops labor time. Use names from list_job_statuses. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| job_id required | string | Job ID from list_jobs. |
| status required | string | Status name from list_job_statuses. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/update_job_status \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_id":"job_8c41d2","status":"status"}' result = client.call("service-fusion", "update_job_status", {"job_id":"job_8c41d2","status":"status"}) const result = await cotlet.call('service-fusion', 'update_job_status', {"job_id":"job_8c41d2","status":"status"}); cotlet call service-fusion update_job_status --data '{"job_id":"job_8c41d2","status":"status"}' Example response
{
"job_id": "1048213",
"number": "10482",
"previous_status": "Started",
"status": "Completed"
} Create estimate
writePOST https://cotlet.dev/api/v1/service-fusion/create_estimate
Creates the estimate under an existing customer and returns its estimate_id. The customer receives nothing; send_estimate does that. An estimate can hold up to five options, each with its own line items.
Parameters
| Name | Type | Description |
|---|---|---|
| customer_name required | string | Existing customer name from list_customers. |
| description required | string | What the estimate is for. |
| items | object[] | Products and services, each with name, quantity and rate. |
| note_to_customer | string | Shown on the estimate. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/create_estimate \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_name":"customer name","description":"description"}' result = client.call("service-fusion", "create_estimate", {"customer_name":"customer name","description":"description"}) const result = await cotlet.call('service-fusion', 'create_estimate', {"customer_name":"customer name","description":"description"}); cotlet call service-fusion create_estimate --data '{"customer_name":"customer name","description":"description"}' Example response
{
"estimate_id": "3318",
"number": "3318",
"status": "Estimate Draft",
"customer_name": "Jane Doe",
"description": "Water heater replacement",
"options": [
{
"name": "Option 1",
"total": 1450
}
]
} POST https://cotlet.dev/api/v1/service-fusion/convert_estimate_to_job
Converts the option you name into a new job and marks that option Converted. One call creates one job; call again with another option to create another. The job starts Unscheduled unless you pass a start. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| estimate_id required | string | Estimate ID from list_estimates. |
| option | string | Option name from get_estimate. Default: the first option. |
| start | datetime | Scheduled start for the new job, ISO 8601. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/convert_estimate_to_job \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"estimate_id":"3317","option":"Better","start":"2026-10-15T09:00:00-05:00"}' result = client.call("service-fusion", "convert_estimate_to_job", {"estimate_id":"3317","option":"Better","start":"2026-10-15T09:00:00-05:00"}) const result = await cotlet.call('service-fusion', 'convert_estimate_to_job', {"estimate_id":"3317","option":"Better","start":"2026-10-15T09:00:00-05:00"}); cotlet call service-fusion convert_estimate_to_job --data '{"estimate_id":"3317","option":"Better","start":"2026-10-15T09:00:00-05:00"}' Example response
{
"estimate_id": "3317",
"option": "Better",
"converted": true,
"job_id": "1048390",
"job_number": "10484",
"status": "Scheduled",
"start": "2026-10-15T09:00:00-05:00"
} POST https://cotlet.dev/api/v1/service-fusion/create_invoice
Does what Invoice, then Invoice Now does on a job. By default the job must be Completed or closed; with Allow Invoicing Prior To Job Completion on in Company Preferences it can be invoiced earlier. The invoice is not sent: use send_invoice. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| job_id required | string | Job ID from list_jobs. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/create_invoice \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_id":"1048213"}' result = client.call("service-fusion", "create_invoice", {"job_id":"1048213"}) const result = await cotlet.call('service-fusion', 'create_invoice', {"job_id":"1048213"}); cotlet call service-fusion create_invoice --data '{"job_id":"1048213"}' Example response
{
"invoice_id": "90413",
"number": "9042",
"job_id": "1048213",
"job_number": "10482",
"customer_name": "Jane Doe",
"total": 129,
"paid": false,
"date": "2026-10-15"
} POST https://cotlet.dev/api/v1/service-fusion/send_estimate
Sends the estimate from the Send button of the app. By text it uses your Text an estimate template and goes to the primary contact, another saved contact or a number you give. The customer can accept the estimate or request a change. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| estimate_id required | string | Estimate ID from list_estimates. |
| via | enum | email or text. Default: email. |
| to | string | Phone number or email address. Default: the primary contact on the record. |
| template | string | Name of one of your Communication Templates. Default: the system template for the channel. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/send_estimate \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"estimate_id":"3318","via":"text","to":"555-0142"}' result = client.call("service-fusion", "send_estimate", {"estimate_id":"3318","via":"text","to":"555-0142"}) const result = await cotlet.call('service-fusion', 'send_estimate', {"estimate_id":"3318","via":"text","to":"555-0142"}); cotlet call service-fusion send_estimate --data '{"estimate_id":"3318","via":"text","to":"555-0142"}' Example response
{
"estimate_id": "3317",
"sent": true,
"via": "text",
"to": "555-0173",
"template": "Text an estimate"
} POST https://cotlet.dev/api/v1/service-fusion/send_invoice
Sends the invoice from the Send button of the app. By text it uses your Text an invoice template; by email you can use any Job/WO Email template. The customer can pay in full or in part from the link. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| invoice_id required | string | Invoice ID from list_invoices. |
| via | enum | email or text. Default: email. |
| to | string | Phone number or email address. Default: the primary contact on the record. |
| template | string | Name of one of your Communication Templates. Default: the system template for the channel. |
Request
curl -X POST https://cotlet.dev/api/v1/service-fusion/send_invoice \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"invoice_id":"invoice_8c41d2"}' result = client.call("service-fusion", "send_invoice", {"invoice_id":"invoice_8c41d2"}) const result = await cotlet.call('service-fusion', 'send_invoice', {"invoice_id":"invoice_8c41d2"}); cotlet call service-fusion send_invoice --data '{"invoice_id":"invoice_8c41d2"}' Example response
{
"invoice_id": "90413",
"sent": true,
"via": "text",
"to": "555-0142",
"template": "Text an invoice"
}