Create a project and draft invoice
Create a new client, link a project to them, then create a draft invoice — all from a single script.
This guide chains three API calls to set up a client, a project, and a draft invoice ready for review in the dashboard.
APIs used: POST /clients · POST /projects · POST /invoices
Related concepts: Draft-only invoices · Errors
Prerequisites
KWILL_API_KEYset in your environment- Workspace on the Studio plan or above
Step 1 — Create the client
A 201 response returns the client object. Copy the id for the next step.
curl -X POST https://usekwill.com/api/v1/clients \
-H "Authorization: Bearer $KWILL_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Acme Corp", "email": "billing@acme.com"}'Step 2 — Create a project
Use the client's id as client_id, or use client_name and client_email to find-or-create:
curl -X POST https://usekwill.com/api/v1/projects \
-H "Authorization: Bearer $KWILL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_id": "<CLIENT_ID>",
"name": "Website Redesign"
}'Step 3 — Create a draft invoice
Provide the client details, line items, total, and due date:
curl -X POST https://usekwill.com/api/v1/invoices \
-H "Authorization: Bearer $KWILL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"client_name": "Acme Corp",
"client_email": "billing@acme.com",
"line_items": [
{ "description": "Website Redesign", "quantity": 1, "rate": 2500 }
],
"total": 2500,
"due_date": "2025-01-15"
}'The invoice is created as a draft. Open it in the dashboard to review and send it to the client. See Draft-only invoices for more context.
Complete script
// create-project-invoice.mjs — run with: node create-project-invoice.mjs
// Requires: KWILL_API_KEY in environment
const BASE = 'https://usekwill.com/api/v1';
async function kwillPost(path, body) {
const res = await fetch(BASE + path, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KWILL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`POST ${path} failed ${res.status}: ${err.error ?? ''}`);
}
return res.json();
}
const client = await kwillPost('/clients', {
name: 'Acme Corp',
email: 'billing@acme.com',
});
console.log(`Client: ${client.id}`);
const project = await kwillPost('/projects', {
client_id: client.id,
name: 'Website Redesign',
});
console.log(`Project: ${project.id}`);
const invoice = await kwillPost('/invoices', {
client_name: 'Acme Corp',
client_email: 'billing@acme.com',
line_items: [
{ description: 'Website Redesign', quantity: 1, rate: 2500 },
],
total: 2500,
due_date: '2025-01-15',
});
console.log(`Invoice: ${invoice.id} status=${invoice.status}`);
console.log('Open the Kwill dashboard to review and send.');