REST API reference
Kickserv API
15 endpoints for Kickserv. Each one is a POST with a JSON body, called with your API key.
- Base URL
- https://cotlet.dev/api/v1/kickserv
- Connects with
- Your own Kickserv account (admin role for invoices and payments)
- Integration page
- Kickserv
| Endpoint | Access | Description |
|---|---|---|
| list_customers | read | Customers and contacts, searchable by name, email, phone or address. |
| get_customer | read | One customer with addresses, contacts, notes and jobs. |
| list_jobs | read | Jobs with status, service type, customer, assigned techs and scheduled date. |
| get_job | read | Full job: description, scope of work, charges, schedule events, time entries, notes and invoice status. |
| list_opportunities | read | Opportunities with customer, service type and whether an estimate was sent. |
| list_invoices | read | Invoices with customer, total, balance and whether they were sent and viewed. |
| create_customer | write | New person or company contact with service and billing address. |
| create_job | write | New job, or a new opportunity to estimate first, for a customer. |
| add_charge | write | Add one charge item to the estimate or invoice of a job or opportunity. |
| schedule_event | write | Add a work event to a job, optionally assigned to a technician. |
| log_time_entry | write | Record hours worked on a job by an employee. |
| send_estimate | write | Email the estimate of an opportunity to the customer. |
| generate_invoice | write | Create the invoice for a job from its charges. Nothing is sent. |
| send_invoice | write | Email the invoice of a job to the customer. |
| record_payment | write | Log a manual payment (check, cash, other) on a job and mark the invoice paid. No card is charged. |
List customers
readPOST https://cotlet.dev/api/v1/kickserv/list_customers
Returns customers with their number, contact details and service address. Search matches name, email, phone and street address. Pass the customer_number to get_customer, create_job and the other tools.
Parameters
| Name | Type | Description |
|---|---|---|
| query | string | Name, email, phone or street address. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/list_customers \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("kickserv", "list_customers") const result = await cotlet.call('kickserv', 'list_customers'); cotlet call kickserv list_customers Example response
{
"customers": [
{
"customer_number": "2218",
"name": "Lee Residence",
"email": "lee@example.com",
"phone": "555-0142",
"service_address": "41 Maple St, Austin, TX 78701"
}
],
"page": 1
} Get customer
readPOST https://cotlet.dev/api/v1/kickserv/get_customer
Returns one customer with service and billing addresses, saved contacts, special instructions, notes and the list of their jobs with status. Use it before creating a job so your agent reuses the right contact.
Parameters
| Name | Type | Description |
|---|---|---|
| customer_number required | string | Customer number from list_customers. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/get_customer \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_number":"customer number"}' result = client.call("kickserv", "get_customer", {"customer_number":"customer number"}) const result = await cotlet.call('kickserv', 'get_customer', {"customer_number":"customer number"}); cotlet call kickserv get_customer --data '{"customer_number":"customer number"}' Example response
{
"customer_number": "2218",
"name": "Lee Residence",
"billing_address": "41 Maple St, Austin, TX 78701",
"contacts": [
{
"name": "Mrs. Lee",
"email": "lee@example.com",
"primary": true
}
],
"jobs": [
{
"job_number": "1037",
"service_type": "Water heater replacement",
"status": "complete"
}
]
} List jobs
readPOST https://cotlet.dev/api/v1/kickserv/list_jobs
Returns jobs 30 per page, filtered by status, scheduled date or technician. Use job_number in get_job and the write tools.
Parameters
| Name | Type | Description |
|---|---|---|
| status | enum | unscheduled, scheduled, in_progress, on_hold, canceled or complete. |
| scheduled | enum | today, tomorrow, yesterday, this_week, next_week, last_week, this_month, next_month, last_month, this_year or last_year. |
| employee | string | Only jobs assigned to this employee. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/list_jobs \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status":"complete","scheduled":"yesterday","page":1}' result = client.call("kickserv", "list_jobs", {"status":"complete","scheduled":"yesterday","page":1}) const result = await cotlet.call('kickserv', 'list_jobs', {"status":"complete","scheduled":"yesterday","page":1}); cotlet call kickserv list_jobs --data '{"status":"complete","scheduled":"yesterday","page":1}' Example response
{
"jobs": [
{
"job_number": "1037",
"service_type": "Water heater replacement",
"customer": "Lee Residence",
"status": "complete",
"scheduled_on": "2026-10-09",
"employees": [
"Dana Ruiz"
]
}
],
"page": 1
} Get job
readPOST https://cotlet.dev/api/v1/kickserv/get_job
Returns one job or opportunity with its internal description, the customer-facing scope of work, charges with the running total, schedule events, time entries, notes and whether an invoice exists and is paid.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/get_job \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"job number"}' result = client.call("kickserv", "get_job", {"job_number":"job number"}) const result = await cotlet.call('kickserv', 'get_job', {"job_number":"job number"}); cotlet call kickserv get_job --data '{"job_number":"job number"}' Example response
{
"job_number": "1042",
"service_type": "Water heater replacement",
"customer": "Lee Residence",
"status": "new",
"description": "Website lead: 40 gal tank leaking",
"scope_of_work": "Replace the 40 gallon gas water heater.",
"charges": [],
"total": "0.00",
"events": [],
"time_entries": [],
"invoice": null
} List opportunities
readPOST https://cotlet.dev/api/v1/kickserv/list_opportunities
Returns opportunities from the Opportunities page. A new opportunity appears under the New tab; in the app an opportunity can be marked won or lost.
Parameters
| Name | Type | Description |
|---|---|---|
| stage | enum | new, won or lost. Default: new. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/list_opportunities \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("kickserv", "list_opportunities") const result = await cotlet.call('kickserv', 'list_opportunities'); cotlet call kickserv list_opportunities Example response
{
"opportunities": [
{
"job_number": "1042",
"service_type": "Water heater replacement",
"customer": "Lee Residence",
"stage": "new",
"estimate_sent": false,
"created_on": "2026-10-10"
}
],
"page": 1
} List invoices
readPOST https://cotlet.dev/api/v1/kickserv/list_invoices
Returns the invoices shown on the Invoices page, from its unpaid or paid tab. Each has a sent flag and a viewed flag, the same as the blue letter and purple eye icons in the app.
Parameters
| Name | Type | Description |
|---|---|---|
| tab | enum | unpaid or paid. Default: unpaid. |
| page | integer | Page number. Default: 1. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/list_invoices \
-H "Authorization: Bearer $COTLET_API_KEY" result = client.call("kickserv", "list_invoices") const result = await cotlet.call('kickserv', 'list_invoices'); cotlet call kickserv list_invoices Example response
{
"invoices": [
{
"job_number": "1037",
"customer": "Lee Residence",
"total": "480.00",
"balance_remaining": "480.00",
"sent": true,
"viewed": true
}
],
"page": 1
} Create customer
writePOST https://cotlet.dev/api/v1/kickserv/create_customer
Creates a customer and returns its customer_number. It sends nothing to the customer and one edit in Kickserv undoes it, so it runs without asking first. Search with list_customers before creating one, to avoid duplicates.
Parameters
| Name | Type | Description |
|---|---|---|
| name required | string | Person or company name. |
| is_company | boolean | Company instead of a person. Default: false. |
| string | Customer email. | |
| phone | string | Phone number. |
| service_address | object | street, city, state, zip_code, country. |
| billing_address | object | Same fields. Default: the service address. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/create_customer \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Jane Doe"}' result = client.call("kickserv", "create_customer", {"name":"Jane Doe"}) const result = await cotlet.call('kickserv', 'create_customer', {"name":"Jane Doe"}); cotlet call kickserv create_customer --data '{"name":"Jane Doe"}' Example response
{
"customer_number": "2219",
"name": "Rivera Dental",
"status": "created"
} Create job
writePOST https://cotlet.dev/api/v1/kickserv/create_job
Creates a job for a customer and returns its job_number. With as_opportunity set to true it creates an opportunity under the New tab instead. The description is internal and the scope of work is shown to the customer. It sends nothing to the customer, so it runs without asking first.
Parameters
| Name | Type | Description |
|---|---|---|
| customer_number required | string | Customer number from list_customers. |
| service_type required | string | Service type as named in your account. |
| description | string | Internal description, not shown to the customer. |
| scope_of_work | string | Customer-facing scope of work. |
| as_opportunity | boolean | Create an opportunity for an estimate instead of a job. Default: false. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/create_job \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"customer_number":"2231","service_type":"Water heater replacement","description":"Lead from website form, 11:40 p.m.: 40 gal tank leaking in garage","scope_of_work":"Inspect and replace the 40 gallon gas water heater.","as_opportunity":true}' result = client.call("kickserv", "create_job", {"customer_number":"2231","service_type":"Water heater replacement","description":"Lead from website form, 11:40 p.m.: 40 gal tank leaking in garage","scope_of_work":"Inspect and replace the 40 gallon gas water heater.","as_opportunity":True}) const result = await cotlet.call('kickserv', 'create_job', {"customer_number":"2231","service_type":"Water heater replacement","description":"Lead from website form, 11:40 p.m.: 40 gal tank leaking in garage","scope_of_work":"Inspect and replace the 40 gallon gas water heater.","as_opportunity":true}); cotlet call kickserv create_job --data '{"customer_number":"2231","service_type":"Water heater replacement","description":"Lead from website form, 11:40 p.m.: 40 gal tank leaking in garage","scope_of_work":"Inspect and replace the 40 gallon gas water heater.","as_opportunity":true}' Example response
{
"job_number": "1051",
"type": "opportunity",
"stage": "new",
"customer_number": "2218",
"service_type": "Water heater replacement"
} Add charge
writePOST https://cotlet.dev/api/v1/kickserv/add_charge
Adds one charge line and returns the running total of the job. Call it once per line. Nothing reaches the customer until you send the estimate or invoice, so it runs without asking first.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
| item required | string | Charge item from your account, or a new item name. |
| description | string | Line description. |
| quantity | string | Decimal, e.g. 1 or 2.5. Default: 1. |
| price_per_unit | string | Price as a decimal, e.g. 129.00. |
| taxable | boolean | Apply sales tax. Default: the item setting. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/add_charge \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"1042","item":"Labor","description":"Remove old tank, install and test new heater","quantity":"3","price_per_unit":"95.00","taxable":false}' result = client.call("kickserv", "add_charge", {"job_number":"1042","item":"Labor","description":"Remove old tank, install and test new heater","quantity":"3","price_per_unit":"95.00","taxable":False}) const result = await cotlet.call('kickserv', 'add_charge', {"job_number":"1042","item":"Labor","description":"Remove old tank, install and test new heater","quantity":"3","price_per_unit":"95.00","taxable":false}); cotlet call kickserv add_charge --data '{"job_number":"1042","item":"Labor","description":"Remove old tank, install and test new heater","quantity":"3","price_per_unit":"95.00","taxable":false}' Example response
{
"job_number": "1042",
"charge": {
"item": "Labor",
"quantity": "2.5",
"price_per_unit": "95.00",
"line_total": "237.50"
},
"total": "886.50"
} POST https://cotlet.dev/api/v1/kickserv/schedule_event
Adds an event with a start time, an optional end, a task type and an optional technician. Without a technician the event stays unassigned. Depending on your reminder settings, Kickserv can send reminders for the event. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
| starts_at required | datetime | ISO 8601 with time zone. |
| ends_at | datetime | End of the event. |
| task_type | string | Task type as named in your account, e.g. Work. Default: Work. |
| employee | string | Technician to assign. Default: unassigned. |
| description | string | Notes for the technician. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/schedule_event \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"job number","starts_at":"2026-10-01T09:00:00Z"}' result = client.call("kickserv", "schedule_event", {"job_number":"job number","starts_at":"2026-10-01T09:00:00Z"}) const result = await cotlet.call('kickserv', 'schedule_event', {"job_number":"job number","starts_at":"2026-10-01T09:00:00Z"}); cotlet call kickserv schedule_event --data '{"job_number":"job number","starts_at":"2026-10-01T09:00:00Z"}' Example response
{
"job_number": "1050",
"event_id": "ev_3381",
"task_type": "Work",
"starts_at": "2026-10-15T09:00:00-05:00",
"employee": "Dana Ruiz",
"job_status": "scheduled"
} Log time entry
writePOST https://cotlet.dev/api/v1/kickserv/log_time_entry
Adds a time entry to the job and returns it. Hours are a decimal. It reaches no one and an admin can delete the entry in Kickserv, so it runs without asking first.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
| hours required | string | Number of hours as a decimal, e.g. 2.5. |
| date required | date | Day the work was done. |
| employee required | string | Employee name. |
| billable | boolean | Billable time. Default: true. |
| note | string | What was done. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/log_time_entry \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"job number","hours":"hours","date":"2026-10-01","employee":"employee"}' result = client.call("kickserv", "log_time_entry", {"job_number":"job number","hours":"hours","date":"2026-10-01","employee":"employee"}) const result = await cotlet.call('kickserv', 'log_time_entry', {"job_number":"job number","hours":"hours","date":"2026-10-01","employee":"employee"}); cotlet call kickserv log_time_entry --data '{"job_number":"job number","hours":"hours","date":"2026-10-01","employee":"employee"}' Example response
{
"job_number": "1049",
"time_entry_id": "te_7702",
"employee": "Dana Ruiz",
"date": "2026-10-09",
"hours": "2.5",
"billable": true,
"note": "Installed water heater"
} POST https://cotlet.dev/api/v1/kickserv/send_estimate
Emails the estimate to the main customer of the job and copies the addresses you add. Subject and message come from your Messaging Templates unless you set them. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
| to | string[] | Recipient emails. Default: the main customer of the job. |
| cc | string[] | Extra emails to copy. |
| subject | string | Email subject. Default: your Messaging Templates subject. |
| message | string | Custom message. Default: your Messaging Templates message. |
| attach_pdf | boolean | Also attach a PDF copy. Default: your Messaging Templates setting. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/send_estimate \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"job number"}' result = client.call("kickserv", "send_estimate", {"job_number":"job number"}) const result = await cotlet.call('kickserv', 'send_estimate', {"job_number":"job number"}); cotlet call kickserv send_estimate --data '{"job_number":"job number"}' Example response
{
"job_number": "1042",
"status": "sent",
"sent_to": [
"lee@example.com"
],
"cc": [
"office@example.com"
],
"total": "886.50"
} Generate invoice
writePOST https://cotlet.dev/api/v1/kickserv/generate_invoice
Creates the invoice from the charges on the job, the same as Generate Invoice in the job header, and returns its total. It sends nothing to the customer and the invoice can be edited afterwards, so it runs without asking first.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/generate_invoice \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"job number"}' result = client.call("kickserv", "generate_invoice", {"job_number":"job number"}) const result = await cotlet.call('kickserv', 'generate_invoice', {"job_number":"job number"}); cotlet call kickserv generate_invoice --data '{"job_number":"job number"}' Example response
{
"job_number": "1037",
"invoice_status": "not_sent",
"total": "480.00",
"balance_remaining": "480.00"
} POST https://cotlet.dev/api/v1/kickserv/send_invoice
Emails the invoice to the main customer of the job and copies the addresses you add. The invoice then shows as sent on the Invoices page. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
| to | string[] | Recipient emails. Default: the main customer of the job. |
| cc | string[] | Extra emails to copy. |
| subject | string | Email subject. Default: your Messaging Templates subject. |
| message | string | Custom message. Default: your Messaging Templates message. |
| attach_pdf | boolean | Also attach a PDF copy. Default: your Messaging Templates setting. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/send_invoice \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"1037"}' result = client.call("kickserv", "send_invoice", {"job_number":"1037"}) const result = await cotlet.call('kickserv', 'send_invoice', {"job_number":"1037"}); cotlet call kickserv send_invoice --data '{"job_number":"1037"}' Example response
{
"job_number": "1037",
"invoice_status": "sent",
"sent_to": [
"lee@example.com"
],
"total": "480.00",
"balance_remaining": "480.00"
} POST https://cotlet.dev/api/v1/kickserv/record_payment
Logs the payment type on the job, then marks the invoice paid when mark_paid is true. Kickserv does not bring the balance to zero for manual payments on its own, so the type is recorded first. No card is charged. By default you confirm each call; you can switch that off for this tool.
Parameters
| Name | Type | Description |
|---|---|---|
| job_number required | string | Job number from list_jobs, list_opportunities or get_job. |
| amount required | string | Amount as a decimal, e.g. 480.00. |
| method required | string | Payment type as named in your account, e.g. Check. |
| paid_on | date | Default: today. |
| mark_paid | boolean | Mark the invoice paid afterwards. Default: true. |
Request
curl -X POST https://cotlet.dev/api/v1/kickserv/record_payment \
-H "Authorization: Bearer $COTLET_API_KEY" \
-H "Content-Type: application/json" \
-d '{"job_number":"1051","amount":"150.00","method":"Cash","paid_on":"2026-10-12","mark_paid":false}' result = client.call("kickserv", "record_payment", {"job_number":"1051","amount":"150.00","method":"Cash","paid_on":"2026-10-12","mark_paid":False}) const result = await cotlet.call('kickserv', 'record_payment', {"job_number":"1051","amount":"150.00","method":"Cash","paid_on":"2026-10-12","mark_paid":false}); cotlet call kickserv record_payment --data '{"job_number":"1051","amount":"150.00","method":"Cash","paid_on":"2026-10-12","mark_paid":false}' Example response
{
"job_number": "1037",
"amount": "480.00",
"method": "Check",
"paid_on": "2026-10-10",
"invoice_status": "paid",
"balance_remaining": "0.00"
}