Clients
A workspace is Esy’s customer: an agency, a freelancer, a company. A client is their customer: the dental group, the bakery, the law firm they do the work for. A client groups the projects that are its domains and carries the business — whether it is active, what it pays, and who approves the work.
Who is who
| Thing | What it is | Example |
|---|---|---|
| Workspace | Esy’s customer. Members, budgets and billing with Esy live here. | Bright Agency |
| Client | A business the workspace works for, with a status, terms and contacts. | Northside Dental |
| Project | A domain-level scope that runs, budgets and costs belong to. It may belong to a client. | northsidedental.com |
| Contact | A person at a client. Not an Esy login unless invited. | Dr. Patel, who approves the work |
There is no “agency” or “freelancer” kind of workspace, and no kind of client: a freelancer is a one-person agency, and the difference between a lead and a client is its status.
Billing terms
Terms are a small versioned document on the client. Esy validates every write; the database checks the envelope. Money is integer cents with a currency, never a float.
{
"version": 1,
"model": "retainer", // retainer | per_job | hourly | none
"amountCents": 300000, // integer cents, never floats
"currency": "usd",
"period": "quarter", // retainers only: month | quarter | year
"scope": [{ "what": "City pages", "count": 12, "per": "month" }],
"startsOn": "2026-10-01",
"renewsOn": "2027-04-01"
}A retainer needs a period; only a retainer has one; anything paid needs an amount. Esy records the terms and computes the expected monthly fee from them. It does not send invoices; payments, if you attribute them, live in your own Stripe, referenced by stripeCustomerId.
Status
lead → active ⇄ paused → past
every change is a row in the client's status history, with who and whyLeads and past clients stay out of totals. The history is append-only: a client with history can be archived, never deleted.
What rolls up to a client
| Figure | From | Who sees it |
|---|---|---|
| Cost to serve | Provider costs of runs in the client’s projects, this month | Owners and admins |
| Expected fee | The billing terms, per month | Owners and admins |
| Waiting on you | Runs in review in the client’s projects, last 14 days | Members |
| Made | Artifacts made in the client’s projects this month | Members |
A project can only belong to a client in its own workspace, and a contact can only belong to a client in its own workspace. The database enforces both with composite keys, so no bug in a client app can link across organizations. Edits carry the etag and creates take a requestId, as everywhere in the API.
The endpoints are in the Clients API. Characters (avatars) belong to a project, so a client’s avatars are its projects’ avatars: see Characters.
- A workspace is Esy’s customer; a client is the workspace’s customer.
- A client groups projects and carries the business: status, terms, contacts.
- A project with no client is one of your own brands.
- Tenancy is enforced by the database, not by careful code.
