REST API reference

Knowify API

15 endpoints for Knowify. Each one is a POST with a JSON body, called with your API key.

Base URL
https://cotlet.dev/api/v1/knowify
Connects with
Your own Knowify account (admin permissions for costs and invoicing)
Integration page
Knowify
Knowify Endpoints
Endpoint Access Description
list_jobs read Contract jobs with client, contract status, revenue, cost and profitability.
get_job read One job: client, jobsite address, contract style, contract lines, change orders and phases.
get_job_cost read Cost to date vs budget for the job and each phase, from Plan & Track.
list_invoices read Invoices with job, client, amount, balance due and date.
list_clients read Clients with contact email, phone and address.
list_time_entries read Time entries from Review Time: person, job, phase, hours, billable and approval state.
create_client write Add a client to your client list.
create_job write New contract job. It starts as a lead until a proposal is saved.
save_proposal write Save the draft proposal (bid) of a job. The job moves to bidding. Nothing is sent.
send_proposal_for_signature write Email the proposal to the client for e-signature. The job turns active when they sign.
add_change_order write Add a change order to a fixed price job, as a draft or active so it can be invoiced.
create_invoice write Invoice an active job by percent of progress or dollar amount per line, as a draft or finalized.
send_invoice write Email a finalized invoice to the client.
record_payment write Record a full or partial client payment on an invoice.
log_time write Enter hours on a job phase in the Time Tracker and submit them for review.

List jobs

read

POST https://cotlet.dev/api/v1/knowify/list_jobs

Returns contract jobs with client, status, revenue, cost and profit. Without a status filter it returns all open jobs, newest first. Search matches job name, client name, address or tag. Use job_id from each item in the other tools.

Parameters

Parameters: list_jobs
NameTypeDescription
status enum lead, bidding, out_for_signature, active, rejected or closed. Default: all open.
search string Job name, client name, address or tag.
limit integer 1 to 100. Default: 25.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/list_jobs \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("knowify", "list_jobs")
Node.js
const result = await cotlet.call('knowify', 'list_jobs');
CLI
cotlet call knowify list_jobs

Example response

{
  "jobs": [
    {
      "job_id": "418203",
      "name": "Maple St Remodel",
      "client": "Dana Whitfield",
      "status": "active",
      "contract_style": "fixed_price",
      "revenue": 42400,
      "cost": 22500,
      "profit": 19900
    },
    {
      "job_id": "418410",
      "name": "Riverside Dental Office",
      "client": "Riverside Dental",
      "status": "lead",
      "contract_style": "fixed_price",
      "revenue": 0,
      "cost": 0,
      "profit": 0
    }
  ]
}

Get job

read

POST https://cotlet.dev/api/v1/knowify/get_job

Returns one job with its contract lines, change orders and phases. Contract line and change order IDs go into create_invoice; phase IDs go into log_time. Phases show whether labor is enabled.

Parameters

Parameters: get_job
NameTypeDescription
job_id required string Job ID from list_jobs.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/get_job \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"job_8c41d2"}'
Python
result = client.call("knowify", "get_job", {"job_id":"job_8c41d2"})
Node.js
const result = await cotlet.call('knowify', 'get_job', {"job_id":"job_8c41d2"});
CLI
cotlet call knowify get_job --data '{"job_id":"job_8c41d2"}'

Example response

{
  "job_id": "418203",
  "name": "Maple St Remodel",
  "client": {
    "client_id": "cl-2201",
    "name": "Dana Whitfield"
  },
  "jobsite_address": "214 Maple St, Springfield",
  "contract_style": "fixed_price",
  "contract_lines": [
    {
      "line_id": "line-1",
      "description": "Rough-in electrical",
      "price": 22000
    },
    {
      "line_id": "line-2",
      "description": "Fixtures and finish",
      "price": 18000
    }
  ],
  "change_orders": [
    {
      "change_order_id": "co-1",
      "description": "Extra outlets in the kitchen",
      "price": 2400,
      "status": "active"
    }
  ],
  "phases": [
    {
      "phase_id": "ph-1",
      "name": "Framing",
      "labor_enabled": true
    },
    {
      "phase_id": "ph-2",
      "name": "Rough-in",
      "labor_enabled": true
    }
  ]
}

POST https://cotlet.dev/api/v1/knowify/get_job_cost

Returns cost to date against budget for the whole job and for each phase, the figures from Plan & Track. Phases over budget have a negative variance. The job totals are the sums of the phases.

Parameters

Parameters: get_job_cost
NameTypeDescription
job_id required string Job ID from list_jobs.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/get_job_cost \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"418203"}'
Python
result = client.call("knowify", "get_job_cost", {"job_id":"418203"})
Node.js
const result = await cotlet.call('knowify', 'get_job_cost', {"job_id":"418203"});
CLI
cotlet call knowify get_job_cost --data '{"job_id":"418203"}'

Example response

{
  "job_id": "418203",
  "budget_total": 29000,
  "cost_total": 22500,
  "phases": [
    {
      "phase": "Framing",
      "budget": 14000,
      "cost": 15200,
      "variance": -1200
    },
    {
      "phase": "Rough-in",
      "budget": 9000,
      "cost": 6100,
      "variance": 2900
    },
    {
      "phase": "Finish",
      "budget": 6000,
      "cost": 1200,
      "variance": 4800
    }
  ]
}

POST https://cotlet.dev/api/v1/knowify/list_invoices

Returns invoices newest first with job, client, amount, balance due and date. Set unpaid_only to see invoices with a balance due. Use invoice_id in send_invoice and record_payment.

Parameters

Parameters: list_invoices
NameTypeDescription
job_id string Only invoices of this job.
unpaid_only boolean Skip invoices that are fully paid.
since date Only invoices dated on or after this date.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/list_invoices \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("knowify", "list_invoices")
Node.js
const result = await cotlet.call('knowify', 'list_invoices');
CLI
cotlet call knowify list_invoices

Example response

{
  "invoices": [
    {
      "invoice_id": "90488",
      "job_id": "418120",
      "client": "Riverside Dental",
      "amount": 12500,
      "balance_due": 12500,
      "date": "2026-09-28"
    }
  ]
}

POST https://cotlet.dev/api/v1/knowify/list_clients

Returns clients with contact email, phone and address. Search matches the client name. Use client_id in create_job.

Parameters

Parameters: list_clients
NameTypeDescription
search string Client name.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/list_clients \
  -H "Authorization: Bearer $COTLET_API_KEY"
Python
result = client.call("knowify", "list_clients")
Node.js
const result = await cotlet.call('knowify', 'list_clients');
CLI
cotlet call knowify list_clients

Example response

{
  "clients": [
    {
      "client_id": "cl-3310",
      "name": "Riverside Dental",
      "email": "office@riverside-dental.example",
      "phone": "555-0142",
      "address": "88 River Rd, Springfield"
    }
  ]
}

POST https://cotlet.dev/api/v1/knowify/list_time_entries

Returns the time entries you see in Review Time between two dates, with person, job, phase, hours, billable flag and approval state. Filter by job or by approved and unapproved to find entries waiting for review.

Parameters

Parameters: list_time_entries
NameTypeDescription
job_id string Only entries on this job.
from required date First day.
to required date Last day.
approved enum all, approved or unapproved. Default: all.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/list_time_entries \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"from":"2026-10-01","to":"2026-10-01"}'
Python
result = client.call("knowify", "list_time_entries", {"from":"2026-10-01","to":"2026-10-01"})
Node.js
const result = await cotlet.call('knowify', 'list_time_entries', {"from":"2026-10-01","to":"2026-10-01"});
CLI
cotlet call knowify list_time_entries --data '{"from":"2026-10-01","to":"2026-10-01"}'

Example response

{
  "entries": [
    {
      "entry_id": "te-7701",
      "person": "Sam Ortiz",
      "job_id": "418203",
      "phase": "Framing",
      "date": "2026-10-09",
      "hours": 8,
      "billable": true,
      "approved": false
    }
  ]
}

POST https://cotlet.dev/api/v1/knowify/create_client

Adds a client to your client list and returns its client_id for create_job. The email is used for proposals and invoices. It reaches no one, so it runs without asking first.

Parameters

Parameters: create_client
NameTypeDescription
name required string Client name.
email string Contact email, used for proposals and invoices.
phone string Contact phone.
address string Billing address.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/create_client \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe"}'
Python
result = client.call("knowify", "create_client", {"name":"Jane Doe"})
Node.js
const result = await cotlet.call('knowify', 'create_client', {"name":"Jane Doe"});
CLI
cotlet call knowify create_client --data '{"name":"Jane Doe"}'

Example response

{
  "client_id": "cl-3311",
  "name": "Lakeview Dental",
  "email": "office@lakeview-dental.example",
  "status": "created"
}

Create job

write

POST https://cotlet.dev/api/v1/knowify/create_job

Creates a contract job for a client. The job starts as a lead and moves to bidding when save_proposal runs. Contract style is fixed_price or cost_plus. It reaches no one, so it runs without asking first.

Parameters

Parameters: create_job
NameTypeDescription
name required string Job name.
client_id required string Client ID from list_clients or create_client.
contract_style required enum fixed_price or cost_plus.
jobsite_address string Jobsite address.
bid_due_date date When the bid is due.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/create_job \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"Jane Doe","client_id":"client_8c41d2","contract_style":"fixed_price"}'
Python
result = client.call("knowify", "create_job", {"name":"Jane Doe","client_id":"client_8c41d2","contract_style":"fixed_price"})
Node.js
const result = await cotlet.call('knowify', 'create_job', {"name":"Jane Doe","client_id":"client_8c41d2","contract_style":"fixed_price"});
CLI
cotlet call knowify create_job --data '{"name":"Jane Doe","client_id":"client_8c41d2","contract_style":"fixed_price"}'

Example response

{
  "job_id": "418410",
  "name": "Riverside Dental Office",
  "client_id": "cl-3310",
  "contract_style": "fixed_price",
  "status": "lead"
}

POST https://cotlet.dev/api/v1/knowify/save_proposal

Saves the contract lines of the proposal as a draft and moves the job to bidding. Nothing is sent to the client; send_proposal_for_signature does that. It reaches no one, so it runs without asking first.

Parameters

Parameters: save_proposal
NameTypeDescription
job_id required string Job ID from list_jobs.
line_items required object List of { description, price } contract lines.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/save_proposal \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"job_8c41d2","line_items":"line items"}'
Python
result = client.call("knowify", "save_proposal", {"job_id":"job_8c41d2","line_items":"line items"})
Node.js
const result = await cotlet.call('knowify', 'save_proposal', {"job_id":"job_8c41d2","line_items":"line items"});
CLI
cotlet call knowify save_proposal --data '{"job_id":"job_8c41d2","line_items":"line items"}'

Example response

{
  "job_id": "418410",
  "status": "bidding",
  "proposal_total": 38500,
  "line_items": 2
}

POST https://cotlet.dev/api/v1/knowify/send_proposal_for_signature

Emails the saved proposal to the client for e-signature. The recipient is the client's contact email unless you pass to_email. The job turns active when the client signs. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: send_proposal_for_signature
NameTypeDescription
job_id required string Job ID from list_jobs.
to_email string Recipient. Default: the client's contact email.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/send_proposal_for_signature \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"418410","to_email":"pm@riverside-dental.example"}'
Python
result = client.call("knowify", "send_proposal_for_signature", {"job_id":"418410","to_email":"pm@riverside-dental.example"})
Node.js
const result = await cotlet.call('knowify', 'send_proposal_for_signature', {"job_id":"418410","to_email":"pm@riverside-dental.example"});
CLI
cotlet call knowify send_proposal_for_signature --data '{"job_id":"418410","to_email":"pm@riverside-dental.example"}'

Example response

{
  "job_id": "418410",
  "status": "out_for_signature",
  "sent_to": "office@riverside-dental.example"
}

POST https://cotlet.dev/api/v1/knowify/add_change_order

Adds a change order to a fixed price job. It is a draft unless make_active is true, and only an active change order can be invoiced. It reaches no client, so it runs without asking first.

Parameters

Parameters: add_change_order
NameTypeDescription
job_id required string Job ID from list_jobs.
description required string What changed.
price required string Change order price as a decimal, e.g. 2400.00.
make_active boolean Make it active right away. Default: false.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/add_change_order \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"job_8c41d2","description":"description","price":"price"}'
Python
result = client.call("knowify", "add_change_order", {"job_id":"job_8c41d2","description":"description","price":"price"})
Node.js
const result = await cotlet.call('knowify', 'add_change_order', {"job_id":"job_8c41d2","description":"description","price":"price"});
CLI
cotlet call knowify add_change_order --data '{"job_id":"job_8c41d2","description":"description","price":"price"}'

Example response

{
  "change_order_id": "co-1",
  "job_id": "418203",
  "description": "Extra outlets in the kitchen",
  "price": 2400,
  "status": "active"
}

Create invoice

write Confirm

POST https://cotlet.dev/api/v1/knowify/create_invoice

Invoices an active job by percent of progress or by dollar amount per contract line or change order. It saves a draft unless finalize is true. The invoice amount is the sum of the billed lines. It does not email anyone; send_invoice does. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: create_invoice
NameTypeDescription
job_id required string Job ID from list_jobs.
method required enum percent or amount.
lines required object Map of contract line or change order ID to percent or amount to bill.
finalize boolean Verify and finalize instead of saving a draft. Default: false.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/create_invoice \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"418203","method":"percent","lines":{"line-1":40,"line-2":40,"co-1":100},"finalize":false}'
Python
result = client.call("knowify", "create_invoice", {"job_id":"418203","method":"percent","lines":{"line-1":40,"line-2":40,"co-1":100},"finalize":False})
Node.js
const result = await cotlet.call('knowify', 'create_invoice', {"job_id":"418203","method":"percent","lines":{"line-1":40,"line-2":40,"co-1":100},"finalize":false});
CLI
cotlet call knowify create_invoice --data '{"job_id":"418203","method":"percent","lines":{"line-1":40,"line-2":40,"co-1":100},"finalize":false}'

Example response

{
  "invoice_id": "90517",
  "job_id": "418203",
  "status": "draft",
  "amount": 18400,
  "balance_due": 18400,
  "date": "2026-10-10"
}

Send invoice

write Confirm

POST https://cotlet.dev/api/v1/knowify/send_invoice

Emails a finalized invoice to the client. The recipient is the client's contact email unless you pass to_email. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: send_invoice
NameTypeDescription
invoice_id required string Invoice ID from list_invoices or create_invoice.
to_email string Recipient. Default: the client's contact email.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/send_invoice \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"invoice_id":"invoice_8c41d2"}'
Python
result = client.call("knowify", "send_invoice", {"invoice_id":"invoice_8c41d2"})
Node.js
const result = await cotlet.call('knowify', 'send_invoice', {"invoice_id":"invoice_8c41d2"});
CLI
cotlet call knowify send_invoice --data '{"invoice_id":"invoice_8c41d2"}'

Example response

{
  "invoice_id": "90517",
  "status": "sent",
  "sent_to": "dana.whitfield@example.com"
}

Record payment

write Confirm

POST https://cotlet.dev/api/v1/knowify/record_payment

Records a full or partial client payment on an invoice and returns the new balance due. The memo holds a check number or note. By default you confirm each call; you can switch that off for this tool.

Parameters

Parameters: record_payment
NameTypeDescription
invoice_id required string Invoice ID from list_invoices.
amount required string Amount received as a decimal, e.g. 12500.00.
date required date Payment date.
memo string Check number or note.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/record_payment \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"invoice_id":"invoice_8c41d2","amount":"amount","date":"2026-10-01"}'
Python
result = client.call("knowify", "record_payment", {"invoice_id":"invoice_8c41d2","amount":"amount","date":"2026-10-01"})
Node.js
const result = await cotlet.call('knowify', 'record_payment', {"invoice_id":"invoice_8c41d2","amount":"amount","date":"2026-10-01"});
CLI
cotlet call knowify record_payment --data '{"invoice_id":"invoice_8c41d2","amount":"amount","date":"2026-10-01"}'

Example response

{
  "invoice_id": "90488",
  "amount": 12500,
  "date": "2026-10-09",
  "memo": "Check 4471",
  "balance_due": 0
}

Log time

write

POST https://cotlet.dev/api/v1/knowify/log_time

Enters hours on a job phase in the Time Tracker and submits them for review, where they appear in Review Time. The phase must have labor enabled. It reaches no client, so it runs without asking first.

Parameters

Parameters: log_time
NameTypeDescription
job_id required string Job ID from list_jobs.
phase_id required string Phase ID from get_job. The phase must have labor enabled.
date required date Day worked.
hours required string Hours worked as a decimal, e.g. 7.5.
description string What was done.

Request

cURL
curl -X POST https://cotlet.dev/api/v1/knowify/log_time \
  -H "Authorization: Bearer $COTLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"job_id":"job_8c41d2","phase_id":"phase_8c41d2","date":"2026-10-01","hours":"hours"}'
Python
result = client.call("knowify", "log_time", {"job_id":"job_8c41d2","phase_id":"phase_8c41d2","date":"2026-10-01","hours":"hours"})
Node.js
const result = await cotlet.call('knowify', 'log_time', {"job_id":"job_8c41d2","phase_id":"phase_8c41d2","date":"2026-10-01","hours":"hours"});
CLI
cotlet call knowify log_time --data '{"job_id":"job_8c41d2","phase_id":"phase_8c41d2","date":"2026-10-01","hours":"hours"}'

Example response

{
  "entry_id": "te-7702",
  "job_id": "418203",
  "phase": "Framing",
  "date": "2026-10-09",
  "hours": 8,
  "status": "submitted"
}