Clientish Webhooks & API Reference
Read your workspace through the API, and get events from it through webhooks: clients, orders, invoices, tickets, services, leads, team and revenue.
Introduction
Every endpoint is a GET and answers JSON. There is nothing here that creates, changes or deletes a record: a key can read, and that is all.
A request needs an API key, made on the Webhooks & API → API keys tab of your dashboard. Each key reads only what you ticked for it, only from your own workspace, and every call it makes is listed on that tab.
Successful answers come back under data:
{ "data": { "clients": [ … ], "pagination": { … } } }If you want to hear about things as they happen rather than asking again and again, see Webhooks.
Authentication
Send the key as a Bearer token:
curl 'https://workspace.clientish.io/api/v1/clients?per_page=5' \
-H 'Authorization: Bearer YOUR-API-KEY'A tool that cannot set an Authorization header may send X-Api-Key: YOUR-API-KEY instead.
A key that is missing, mistyped, revoked or expired gets 401. A key may also be limited to fixed addresses and given an expiry date.
Permissions
Permissions are chosen per key when it is made. Asking for data a key may not read gets 403.
| Permission | Endpoints |
|---|---|
clients:read | /clients /clients/{id} |
orders:read | /orders /orders/{id} |
invoices:read | /invoices /invoices/{id} |
tickets:read | /tickets /tickets/{id} |
services:read | /services |
leads:read | /leads |
team:read | /team |
reports:read | /workspace /reports/revenue |
GET /search works with any permission, and searches only the record types the key may read.
Pagination & filters
Lists come back newest first, 25 rows at a time. Ask for more with per_page (up to 100) and move with page. Every list carries a pagination object; keep asking while has_more is true.
{ "pagination": { "page": 2, "per_page": 100, "total": 1204, "last_page": 13, "has_more": true } }- Filters are query parameters, listed on each endpoint. A parameter the endpoint does not know is ignored.
- Dates are
YYYY-MM-DD, andfromandtoboth include the day given. - Status filters take the label you see in Clientish, in any case —
paidandPaidare the same. - Times are ISO 8601 with their offset, for example
2026-09-12T10:21:04+06:00. Money is a plain number in the currency the answer names.
Rate limits
Each key has its own limit per minute, set when it is made — 120 unless you change it, anywhere from 1 to 1,000. Every successful answer says where you stand.
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests this key may make per minute. |
X-RateLimit-Remaining | Requests left in the current minute. |
Retry-After | Sent with 429: seconds to wait before trying again. |
Repeated tries with a wrong key from one address are refused with 429 for a minute.
Errors
Errors use the HTTP status and a body you can show or log:
{ "error": { "status": 403, "message": "This API key does not have permission for this data." } }| Status | Meaning | When |
|---|---|---|
401 | Unauthorized | The key is missing, mistyped, revoked or expired. |
403 | Forbidden | The key has no permission for this data, the request came from an address the key does not allow, or the API is not switched on for the workspace. |
404 | Not found | No record with that id in your workspace, or the URL is not an API endpoint. |
422 | Unprocessable | A parameter could not be used — for example a date that is not YYYY-MM-DD. |
429 | Too many requests | The key went over its requests per minute. Wait for the seconds in the Retry-After header. |
500 | Server error | Something went wrong on our side. Try again; it is logged on the key. |
Webhooks
Add an endpoint on the Webhooks tab and your workspace POSTs an event to it the moment something happens. Webhooks need no API key; each endpoint has its own signing secret.
Events
| Event | When |
|---|---|
order.created | A new order is placed |
order.status_changed | An order moves to another status; changes.status has from and to |
order.completed | An order is completed |
invoice.created | An invoice is created |
invoice.paid | An invoice is paid |
invoice.overdue | An unpaid invoice passes its due date (checked once a day) |
ticket.created | A client opens a ticket |
ticket.replied | Someone replies on a ticket |
client.created | A client is added |
lead.created | A lead comes in |
What is sent
data is the record in the same shape the matching Get endpoint returns, as it stands when the event is first sent. test is true for test events.
{
"id": "evt_k2m9q4w7x1c8v5b3n6z0",
"type": "order.status_changed",
"created_at": "2026-09-18T10:21:04+06:00",
"test": false,
"data": { "order": { "id": 5120, "order_no": "ORD-5120", "status": "Working" } },
"changes": { "status": { "from": "Pending", "to": "Working" } }
}Headers
| Header | Meaning |
|---|---|
Clientish-Id | The event id, the same on every retry. Use it to ignore an event you already handled. |
Clientish-Event | The event name, for example invoice.paid. |
Clientish-Timestamp | Unix time the attempt was signed. |
Clientish-Signature | Hex HMAC-SHA256 of timestamp.body with the endpoint’s signing secret. |
Check the signature
Compute it over the raw body, before parsing:
// Node.js (Express)
const crypto = require('crypto');
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const expected = crypto
.createHmac('sha256', SIGNING_SECRET)
.update(req.headers['clientish-timestamp'] + '.' + req.body)
.digest('hex');
if (expected !== req.headers['clientish-signature']) return res.sendStatus(401);
res.sendStatus(200);
});Answering and retries
- Answer any
2xxwithin 10 seconds. Anything else — another code, a redirect, a timeout — counts as failed. - A failed delivery is retried 5 more times: after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours.
- After 20 failed attempts in a row the endpoint is switched off and the owner is told. Resume it from the Webhooks tab once it is fixed.
- Events are sent within about a minute, and not in strict order. Use
created_atand the record itself when order matters. - Endpoints must be HTTPS on a public address. Deliveries are kept for 30 days.
Workspace
Workspace overview
Your agency at a glance: name, currency and timezone, how many clients, orders, invoices, tickets, services, leads and team members you have, and the order and ticket status labels your workspace uses. Needs the Reports permission.
Parameters
This endpoint takes no parameters.
Example request
curl 'https://workspace.clientish.io/api/v1/workspace' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"agency": {
"name": "Northwind Studio",
"workspace": "northwind",
"timezone": "Asia/Dhaka",
"currency": "USD"
},
"counts": {
"clients": 148,
"orders": 1204,
"invoices": 1311,
"tickets": 286,
"services": 24,
"leads": 97,
"team_members": 12
},
"granted_scopes": [
"clients",
"orders",
"invoices",
"reports"
],
"order_statuses": [
"Pending",
"Working",
"Complete",
"Canceled"
],
"ticket_statuses": [
"Open",
"Pending",
"Closed",
"Spam"
]
}
}Search
One search term across clients, orders, invoices, tickets, leads and services at once. Only the record types the key has permission for are searched; the others come back empty.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/search?query=harbor' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"query": "harbor",
"results": {
"clients": [
{
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co",
"company": "Harbor Coffee Co."
}
],
"orders": [
{
"id": 5120,
"order_no": "ORD-5120",
"status": "Working",
"created_at": "2026-09-12T10:21:04+06:00"
}
],
"invoices": [],
"tickets": [],
"leads": [],
"services": []
},
"total_matches": 2
}
}Clients
List clients
Your clients, newest first.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/clients?status=active&per_page=2' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"clients": [
{
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co",
"phone": "+1 415 555 0142",
"company": "Harbor Coffee Co.",
"balance": 0,
"currency": "USD",
"status": "active",
"stage": "Client",
"location": "San Francisco, US",
"joined_at": "2026-03-04T09:12:00+06:00",
"last_login_at": "2026-09-16T18:40:11+06:00"
}
],
"pagination": {
"page": 1,
"per_page": 2,
"total": 148,
"last_page": 74,
"has_more": true
}
}
}Get a client
One client in full: contact details, company, tax id, balance, and a summary of their recent orders, invoices and tickets.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/clients/2291' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co",
"email_verified": true,
"phone": "+1 415 555 0142",
"company": "Harbor Coffee Co.",
"tax_id": "US-88213",
"balance": 0,
"currency": "USD",
"status": "active",
"stage": "Client",
"address": "12 Pier St",
"location": "San Francisco, US",
"joined_at": "2026-03-04T09:12:00+06:00",
"last_login_at": "2026-09-16T18:40:11+06:00",
"orders": {
"total": 6,
"recent": [
{
"id": 5120,
"order_no": "ORD-5120",
"status": "Working",
"created_at": "2026-09-12T10:21:04+06:00"
}
]
},
"invoices": {
"total": 7,
"invoiced_amount": 3150,
"currency": "USD",
"recent": [
{
"id": 3310,
"invoice_no": "INV-3310",
"amount": 450,
"status": "Paid",
"issued_at": "2026-09-12T10:21:04+06:00",
"due_at": "2026-09-19T10:21:04+06:00"
}
]
},
"tickets": {
"total": 1,
"recent": [
{
"id": 842,
"ticket_no": "TCK-842",
"subject": "Report link not opening",
"priority": "High",
"status": "Open",
"created_at": "2026-09-15T14:02:00+06:00"
}
]
}
}
}
}Orders
List orders
Orders, newest first.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/orders?status=working&from=2026-09-01' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"orders": [
{
"id": 5120,
"order_no": "ORD-5120",
"status": "Working",
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co"
},
"service": "SEO Audit",
"is_subscription": false,
"note": "Focus on the new landing pages",
"created_at": "2026-09-12T10:21:04+06:00",
"started_at": "2026-09-12T11:00:00+06:00",
"completed_at": null
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total": 38,
"last_page": 2,
"has_more": true
}
}
}Get an order
One order in full: client, service, status, assigned team members, tags, notes and the answers the client gave on the intake form.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/orders/5120' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"order": {
"id": 5120,
"order_no": "ORD-5120",
"status": "Working",
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co"
},
"service": {
"id": 31,
"name": "SEO Audit",
"price": 450,
"currency": "USD"
},
"invoice_id": 3310,
"assigned_to": [
{
"id": 77,
"name": "Rafi Ahmed",
"email": "rafi@northwind.studio",
"designation": "SEO Lead"
}
],
"tags": [
"priority"
],
"note": "Focus on the new landing pages",
"project_details": [
{
"label": "Website URL",
"value": "https://harborcoffee.co"
},
{
"label": "Main competitors",
"value": "Bluebird Roasters, Pier 9 Coffee"
}
],
"is_subscription": false,
"next_payment_at": null,
"revision_count": 0,
"rating": null,
"created_at": "2026-09-12T10:21:04+06:00",
"started_at": "2026-09-12T11:00:00+06:00",
"completed_at": null
}
}
}Invoices
List invoices
Invoices, newest first, with the total of every invoice that matches the filters.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/invoices?status=paid&from=2026-09-01&to=2026-09-30' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"invoices": [
{
"id": 3310,
"invoice_no": "INV-3310",
"order_no": "ORD-5120",
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co"
},
"service": "SEO Audit",
"amount": 450,
"subtotal": 450,
"discount": 0,
"tax": 0,
"currency": "USD",
"status": "Paid",
"payment_method": "Stripe",
"issued_at": "2026-09-12T10:21:04+06:00",
"due_at": "2026-09-19T10:21:04+06:00"
}
],
"totals": {
"matched_invoice_total": 18420,
"currency": "USD"
},
"pagination": {
"page": 1,
"per_page": 25,
"total": 41,
"last_page": 2,
"has_more": true
}
}
}Get an invoice
One invoice in full: client, service, quantity, discount and coupon, tax, gateway charge, partial payments, refunds, payment method and status.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/invoices/3310' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"invoice": {
"id": 3310,
"invoice_no": "INV-3310",
"order_no": "ORD-5120",
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co"
},
"service": {
"id": 31,
"name": "SEO Audit",
"unit_price": 450
},
"quantity": 1,
"subtotal": 450,
"discount": 0,
"coupon_code": null,
"gateway_charge": 0,
"tax_percent": 0,
"tax_amount": 0,
"total": 450,
"partial_paid": 0,
"refunded": 0,
"currency": "USD",
"status": "Paid",
"payment_method": "Stripe",
"is_subscription": false,
"note": null,
"issued_at": "2026-09-12T10:21:04+06:00",
"due_at": "2026-09-19T10:21:04+06:00",
"next_payment_at": null,
"created_at": "2026-09-12T10:21:04+06:00"
}
}
}Tickets
List tickets
Support tickets, newest first.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/tickets?status=open&priority=High' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"tickets": [
{
"id": 842,
"ticket_no": "TCK-842",
"subject": "Report link not opening",
"priority": "High",
"status": "Open",
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co"
},
"related_order": "ORD-5120",
"source": "web",
"created_at": "2026-09-15T14:02:00+06:00",
"updated_at": "2026-09-16T09:30:12+06:00"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total": 9,
"last_page": 1,
"has_more": false
}
}
}Get a ticket
One ticket with its conversation: subject, description, priority, status, tags, rating and the replies, oldest first. Formatting is removed from messages.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/tickets/842?message_limit=20' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"ticket": {
"id": 842,
"ticket_no": "TCK-842",
"subject": "Report link not opening",
"description": "The monthly report link shows an error.",
"priority": "High",
"status": "Open",
"client": {
"id": 2291,
"name": "Harbor Coffee Co.",
"email": "hello@harborcoffee.co"
},
"related_order": "ORD-5120",
"tags": [],
"rating": null,
"rating_comment": null,
"source": "web",
"created_at": "2026-09-15T14:02:00+06:00",
"updated_at": "2026-09-16T09:30:12+06:00"
},
"messages": [
{
"id": 9911,
"author": "Harbor Coffee Co.",
"author_side": "client",
"is_auto_reply": false,
"message": "The monthly report link shows an error.",
"sent_at": "2026-09-15T14:02:00+06:00"
},
{
"id": 9914,
"author": "Rafi Ahmed",
"author_side": "agency",
"is_auto_reply": false,
"message": "Thanks — a fresh link is on its way.",
"sent_at": "2026-09-16T09:30:12+06:00"
}
],
"message_count_returned": 2
}
}Catalogue & people
List services
Your service catalogue: names, descriptions, one-off and recurring prices, and delivery deadlines.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/services' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"services": [
{
"id": 31,
"name": "SEO Audit",
"description": "A full technical and content audit.",
"type": "one-time-service",
"category": "SEO",
"price": 450,
"currency": "USD",
"is_recurring": false,
"deadline": 7,
"deadline_unit": "days",
"revision_limit": 2,
"status": "active",
"created_at": "2026-01-10T12:00:00+06:00"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total": 24,
"last_page": 1,
"has_more": false
}
}
}List leads
Leads in your pipeline, newest first.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/leads?status=new' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"leads": [
{
"id": 771,
"name": "Nadia Rahman",
"email": "nadia@brightpath.io",
"phone": "+880 1711 000000",
"company": "BrightPath",
"status": "new",
"source": "Contact form",
"message": "Looking for monthly SEO.",
"converted_client_id": null,
"converted_at": null,
"lost_reason": null,
"follow_up_at": "2026-09-18T11:00:00+06:00",
"created_at": "2026-09-16T20:14:00+06:00"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total": 97,
"last_page": 4,
"has_more": true
}
}
}List team members
The people in your workspace — co-admins, managers, team members and remote members — with role, designation, department and account status.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/team' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"team_members": [
{
"id": 77,
"name": "Rafi Ahmed",
"email": "rafi@northwind.studio",
"role": "Manager",
"kind": "team member",
"designation": "SEO Lead",
"department": "Marketing",
"status": "approved",
"joined_at": "2026-02-01T10:00:00+06:00",
"last_login_at": "2026-09-17T09:05:00+06:00"
}
],
"pagination": {
"page": 1,
"per_page": 25,
"total": 12,
"last_page": 1,
"has_more": false
}
}
}Reports
Revenue summary
Invoiced and collected money over a date range, broken down by month or day, by invoice status, and by your top-earning services. Without dates it covers the last 12 months.
Parameters
Example request
curl 'https://workspace.clientish.io/api/v1/reports/revenue?from=2026-07-01&to=2026-09-30' \
-H 'Authorization: Bearer YOUR-API-KEY'Example response
{
"data": {
"range": {
"from": "2026-07-01",
"to": "2026-09-30",
"group_by": "month"
},
"currency": "USD",
"totals": {
"invoices": 118,
"invoiced": 52340,
"collected": 48110,
"outstanding": 4230
},
"breakdown": [
{
"period": "2026-07",
"invoices": 37,
"invoiced": 16200
},
{
"period": "2026-08",
"invoices": 40,
"invoiced": 17720
},
{
"period": "2026-09",
"invoices": 41,
"invoiced": 18420
}
],
"by_status": [
{
"status": "Paid",
"invoices": 104,
"invoiced": 48110
},
{
"status": "Unpaid",
"invoices": 14,
"invoiced": 4230
}
],
"top_services": [
{
"service": "SEO Audit",
"invoices": 41,
"invoiced": 18450
}
]
}
}