Client
4 fieldsA client you bill.
- owner
- required
- name
- required, at most 120 characters
- required, an email address
- address
- optional, at most 400 characters
Only the freelancer who signed in sees their clients.
Invoices get numbers that are never reused, go out by email, and a reminder follows when one is late.
For a freelancer who bills clients and chases late payments.
Start the freelancer invoices and reminders blueprint on Typestate
Asked you for the name on your invoices and the address your emails come from.
Started Freelancer invoices and reminders: 2 record types, 5 web endpoints.
6 actions are compiling.
Deployed to Sandbox. Try it at your Sandbox address.
Each step has 3 parts: who starts it, how it reaches your backend, and the action that answers.
The rule you will want to change first
A reminder goes 7 days after the due date, and a second one after 14. After that, the invoice is marked overdue and nobody is emailed again.
It lives in the action Chase Late Invoices. Tell your agent how you want it instead: one sentence changes, and you read the change before it goes Live.
The map shows the records it keeps, the actions it runs and the services it calls.
What it keeps
What it does
What it reaches
A client you bill.
Only the freelancer who signed in sees their clients.
One invoice, with its number and whether it is paid.
Only the freelancer who signed in sees their invoices.
Open an action to read its steps: each sentence, with the calls that do it under it.
From the webPOST /clients
Nametext, needed
EmailEmail, needed
Addresstext, optional
Create a Client with name, email and address set to the given name, email address and billing address, and owner_id set to the signed-in person.
Add a Client
set address to input.address
set email to input.email
set name to input.name
set owner_id to caller.id
Answer with that Client's id, name, email and address.
Answer
address is client.address
email is client.email
id is client.id
name is client.name
From the webPOST /invoices
Client IDtext, needed
Days to paya whole number, needed
Descriptiontext, needed
Totala whole number, needed
Find the Client whose id is the given client id.When none matches, refuse with "No such client".
client is
Find the first Client where
id is input.client_id
when none refuse "No such client"
When the given days to pay are less than 1 or more than 90, refuse with "Give between 1 and 90 days to pay".
When input.days_to_pay is less than 1
or input.days_to_pay is more than 90
Refuse "Give between 1 and 90 days to pay"
The number is this year, a dash, and the count of Invoices whose owner_id is the signed-in person and whose number starts with this year, plus one, written with 3 digits: 2026-001, 2026-002.
A number never repeats inside a year, because a tax office reads the numbers as one unbroken row.
year is the first item of today as the text of a date cut at each "-"
invoices_this_year is
Count Invoices where
number starts with year
owner_id is caller.id
next is invoices_this_year + 1
number is "year-"00{the value named next as the text of a number}" when the value named next is less than 10, otherwise "0{the value named next as the text of a number}" when the value named next is less than 100, otherwise the value named next as the text of a number"
Create an Invoice with number set to that number, client_id set to the given client id, description and total_cents set to the given description and total, due_on set to that many days from today, state set to draft, reminders set to 0, and owner_id set to the signed-in person.
Add an Invoice
set client_id to client.id
set description to input.description
set due_on to today plus the given days to pay days
set number to number
set owner_id to caller.id
set reminders to 0
set state to "draft"
set total_cents to input.total
Answer with that Invoice's id, number, description, total_cents, due_on and state.
Answer
description is invoice.description
due_on is invoice.due_on
id is invoice.id
number is invoice.number
state is invoice.state
total_cents is invoice.total_cents
From the webPOST /invoices/send
Invoice IDtext, needed
Find the Invoice whose id is the given invoice id.When none matches, refuse with "No such invoice".
invoice is
Find the first Invoice where
id is input.invoice_id
when none refuse "No such invoice"
When that Invoice's state is not draft, refuse with "This invoice was already sent".
When invoice.state is not "draft"
Refuse "This invoice was already sent"
Find the Client whose id is that Invoice's client_id.
client is
Find the first Client where
id is invoice.client_id
when none carry on without one
Email that Client's email with the subject "Invoice" and that Invoice's number, from the name in the BUSINESS_NAME setting, with the description, the total in euros and the due date.
business_name is the setting BUSINESS_NAME
When client holds a value
Send an email
to client.email
template "Send an Invoice Email"
business_name business_name
description invoice.description
due_on date of invoice.due_on
number invoice.number
total_cents invoice.total_cents as money
Change that Invoice's state to sent.
Change the Invoice where
id is invoice.id
set state to "sent"
Answer with that Invoice's id, number, state and due_on.
Answer
due_on is sent.due_on
id is sent.id
number is sent.number
state is sent.state
From the webPOST /invoices/paid
Invoice IDtext, needed
Find the Invoice whose id is the given invoice id.When none matches, refuse with "No such invoice".
invoice is
Find the first Invoice where
id is input.invoice_id
when none refuse "No such invoice"
When that Invoice's state is draft, refuse with "Send the invoice first".
When invoice.state is "draft"
Refuse "Send the invoice first"
When that Invoice's state is paid, answer with its id, number and state, and change nothing.
When invoice.state is "paid"
Answer
id is invoice.id
number is invoice.number
state is invoice.state
Otherwise change that Invoice's state to paid, and answer with its id, number and state.
Change the Invoice where
id is invoice.id
set state to "paid"
Answer
id is paid.id
number is paid.number
state is paid.state
From the webGET /invoices
Stateone of draft, sent, paid, overdue, optional
List the Invoices whose owner_id is the signed-in person, newest first.When a state is given, list only the Invoices in that state.
all_invoices is
Find Invoices where
owner_id is caller.id
sorted by created_at, highest first
invoices is
each invoice of all_invoices where
input.state is not given
or invoice.state is input.state
For each Invoice, answer its number, its total_cents, its due_on and its state, and the name of the Client whose id is its client_id.
rows is
each invoice of invoices as
client_name is invoice.client.name
due_on is invoice.due_on
number is invoice.number
state is invoice.state
total_cents is invoice.total_cents
Also answer the total of total_cents over every Invoice whose owner_id is the signed-in person and whose state is sent or overdue.
owed is
Add up total_cents of Invoices where
owner_id is caller.id
state is one of "sent" or "overdue"
Answer
invoices is rows
owed_cents is owed
A timer, every day at 09:00, Berlin time
Nothing. It needs no input.
For the freelancer this run is for, list each of their Invoices whose state is sent.
invoices is
Find Invoices where
owner_id is caller.id
state is "sent"
For each of those Invoices, find the Client whose id is its client_id and who belongs to the freelancer this run is for.When none matches, go on to the next Invoice.
For each invoice in invoices, at most 50
client is
Find the first Client where
id is invoice.client_id
owner_id is caller.id
when none skip this one
When it is 7 or more days past its due_on and its reminders is 0, email that Client's email with the subject "Reminder: invoice" and the Invoice's number, with the total and the due date, and change its reminders to 1.
When days since invoice.due_on is at least 7
and invoice.reminders is 0
Send an email
to client.email
template "Chase Late Invoices Email"
due_on date of invoice.due_on
number invoice.number
total_cents invoice.total_cents as money
Change the Invoice where
id is invoice.id
set reminders to 1
When it is 14 or more days past its due_on and its reminders is 1, email that Client's email the same way, and change its reminders to 2.
When days since invoice.due_on is at least 14
and invoice.reminders is 1
Send an email
to client.email
template "Chase Late Invoices Email"
due_on date of invoice.due_on
number invoice.number
total_cents invoice.total_cents as money
Change the Invoice where
id is invoice.id
set reminders to 2
When it is more than 21 days past its due_on and its reminders is 2, change its state to overdue and email nobody.
After 2 reminders the owner takes it up by hand, so a third email would only annoy the client.
When days since invoice.due_on is more than 21
and invoice.reminders is 2
Change the Invoice where
id is invoice.id
set state to "overdue"
People sign up and sign in with an email address and a password, or with a provider you add, such as Google.
| What it does | Who may call | Address |
|---|---|---|
| You add a client. | a signed-in person | POST /clients |
| You draft an invoice. | a signed-in person | POST /invoices |
| You send an invoice. | a signed-in person | POST /invoices/send |
| You mark an invoice paid. | a signed-in person | POST /invoices/paid |
| You read your invoices. | a signed-in person | GET /invoices |
Typestate is the backend. Paste this brief into Lovable, v0, Claude Code or the tool that builds your pages.
Starting from this blueprint does not count against the action changes of your plan. The Free plan runs it on Sandbox. Live needs a paid plan.
Start the freelancer invoices and reminders blueprint on TypestateChoose a planReaders subscribe for free or pay monthly. Paid issues go to paying readers first, and open to everyone after 30 days.
Each sale and delivery moves the stock, and the owner gets one email when a product runs low.
People join a waiting list from your page, and each friend they bring moves them up.