Clientish Webhooks & API Reference

Read your workspace through the API, add and update records with the keys you allow, and get events from it through webhooks: clients, orders, invoices, tickets, services, leads, team and revenue.

REST API · v1 https://clientish.io/api/v1

Introduction

The API returns JSON. A key can read what you checked for it, and keys you allow can also add and update leads, clients, orders and tickets and prepare draft invoices; see Create and update. Nothing can ever be deleted through it.

A request needs an API key, made on the Webhooks & API → API keys tab of your dashboard. Each key reads only what you checked for it, only from your own workspace, and every call it makes is listed on that tab.

Successful responses come back under data:

{ "data": { "clients": [ … ], "pagination": { … } } }

If you want to hear about things as they happen instead of asking again and again, see Webhooks.

Authentication

Send the key as a Bearer token:

curl 'https://clientish.io/api/v1/clients?per_page=5' \
  -H 'Authorization: Bearer YOUR-API-KEY'

A tool that can’t set an Authorization header may send X-Api-Key: YOUR-API-KEY instead.

Keep keys on a server. A key reads your client, order and invoice data. Never put one in a web page, a mobile app or a public repository. If one leaks, revoke it and it stops working immediately.

A key that is missing, mistyped, revoked or expired gets 401. A key may also be limited to fixed addresses and given an expiration date.

Permissions

Permissions are chosen per key when it’s created: read per area and, for leads, clients, orders, tickets and invoices, permission to create and update them. Asking for something a key can’t do gets 403.

PermissionEndpoints
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.

Create & update for an area includes reading it.

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 doesn’t recognize is ignored.
  • Dates are YYYY-MM-DD, and from and to both include the day given.
  • Status filters take the label you see in Clientish, in any case: paid and Paid are 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 response 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 response says where you stand.

HeaderMeaning
X-RateLimit-LimitRequests this key may make per minute.
X-RateLimit-RemainingRequests left in the current minute.
Retry-AfterSent 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." } }
StatusMeaningWhen
401UnauthorizedThe key is missing, mistyped, revoked or expired.
403ForbiddenThe key has no permission for this data, the request came from an address the key does not allow, or the API isn’t turned on for the workspace.
404Not foundNo record with that id in your workspace, or the URL isn’t an API endpoint.
422UnprocessableA parameter could not be used, for example a date that isn’t YYYY-MM-DD.
429Too many requestsThe key went over its requests per minute. Wait for the seconds in the Retry-After header.
500Server errorSomething went wrong on our side. Try again; it’s 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

EventWhen
order.createdA new order is placed
order.status_changedAn order moves to another status; changes.status has from and to
order.completedAn order is completed
invoice.createdAn invoice is created
invoice.paidAn invoice is paid
invoice.overdueAn unpaid invoice passes its due date (checked once a day)
ticket.createdA client opens a ticket
ticket.repliedSomeone replies on a ticket
client.createdA client is added
lead.createdA 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

HeaderMeaning
Clientish-IdThe event id, the same on every retry. Use it to ignore an event you already handled.
Clientish-EventThe event name, for example invoice.paid.
Clientish-TimestampUnix time the attempt was signed.
Clientish-SignatureHex 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 2xx within 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 notified. Resume it from the Webhooks tab once it is fixed.
  • Events are sent within about a minute, and not in strict order. Use created_at and the record itself when order matters.
  • Endpoints must be HTTPS on a public address. Deliveries are kept for 30 days.

Create and update

A key can also add and change records where you allow it: on the key, check Create & update for Leads, Clients, Orders, Tickets or Invoices. Every change runs through the same rules as when your team makes it, including notifications, history and plan limits, and nothing can ever be deleted.

Request

Send the fields as JSON with Content-Type: application/json. POST creates or adds; PATCH changes only the fields you send.

Response

201 when something was created, 200 otherwise. id and type name the record, summary says what was done, and change_id is its entry under What it changed on the key. When nothing needed changing, such as an order already at that status, only done and summary come back.

Idempotency-Key

Tools like Zapier retry a request when a response is slow. Send a unique Idempotency-Key header, up to 100 characters, with each POST. For 24 hours the same Idempotency-Key on the same API key gets the first response back, with Idempotent-Replayed: true, instead of creating the record twice.

Limits and undo

  • Up to 500 changes a day per key, on top of its requests per minute.
  • Invoices are always drafts: your team approves them before the client sees anything. Nothing is ever marked paid.
  • Every change is listed on the key under What it changed. A change of status, stage or details can be undone there for 7 days.

Errors

CodeWhen
403The key may not make changes in this area. Check Create & update for it on the key.
404The record in the URL, or a client, service or order the body names, is not in your workspace.
422A field is missing or cannot be used, for example a status your workspace does not have.
429The key went over its requests per minute, or made its 500 changes in the last 24 hours.

Instant triggers

Zapier and Make can subscribe to your webhook events with the key: POST /hooks starts one, DELETE /hooks/{id} stops it.

Workspace

GET /api/v1/workspace reports:read

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://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"
    ]
  }
}
GET/api/v1/meany permission

Check a key

Which workspace this key belongs to and what it may do. Tools like Zapier use it as a connection test.

Example request

curl 'https://clientish.io/api/v1/me' \
  -H 'Authorization: Bearer YOUR-API-KEY'

Example response

{
  "data": {
    "workspace": "Northwind Studio",
    "key_name": "Zapier: leads",
    "read": [
      "clients",
      "leads"
    ],
    "create_and_update": [
      "leads"
    ]
  }
}

Clients

GET /api/v1/clients clients:read

List clients

Your clients, newest first.

Parameters

search string
Matches name, email, phone or company.
status string
active or inactive: whether the client account is turned on. The client stage (Client, Lead, Contact) comes back as stage.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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 /api/v1/clients/{id} clients:read

Get a client

One client in full: contact details, company, tax id, balance, and a summary of their recent orders, invoices and tickets.

Parameters

id integer · in the URL · required
The client id, as returned by List clients.

Example request

curl 'https://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

GET /api/v1/orders orders:read

List orders

Orders, newest first.

Parameters

status string
Status label, any case: Pending, Working, Complete, Canceled, or one your workspace added. Workspace overview lists yours.
client_id integer
Only this client’s orders.
search string
Matches order number or order note.
from date
Created on or after this date, YYYY-MM-DD.
to date
Created on or before this date, YYYY-MM-DD.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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 /api/v1/orders/{id} orders:read

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

id integer · in the URL · required
The order id, as returned by List orders.

Example request

curl 'https://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

GET /api/v1/invoices invoices:read

List invoices

Invoices, newest first, with the total of every invoice that matches the filters.

Parameters

status string
Status label, any case: Paid, Unpaid, Due, Pending Payment, Canceled or Refund.
client_id integer
Only this client’s invoices.
search string
Matches invoice number or order number.
from date
Issued on or after this date, YYYY-MM-DD.
to date
Issued on or before this date, YYYY-MM-DD.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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 /api/v1/invoices/{id} invoices:read

Get an invoice

One invoice in full: client, service, quantity, discount and coupon, tax, gateway charge, partial payments, refunds, payment method and status.

Parameters

id integer · in the URL · required
The invoice id, as returned by List invoices.

Example request

curl 'https://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

GET /api/v1/tickets tickets:read

List tickets

Support tickets, newest first.

Parameters

status string
Status label, any case: Open, Pending, Closed, Spam, or one your workspace added.
priority string
Low, Medium, High or Urgent.
client_id integer
Only this client’s tickets.
search string
Matches ticket number or subject.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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 /api/v1/tickets/{id} tickets:read

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

id integer · in the URL · required
The ticket id, as returned by List tickets.
message_limit integer
How many of the latest replies to include, 1 to 100. Defaults to 50.

Example request

curl 'https://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
  }
}

Catalog & people

GET /api/v1/services services:read

List services

Your service catalog: names, descriptions, one-time and recurring prices, and delivery deadlines.

Parameters

search string
Matches service name.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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
    }
  }
}
GET /api/v1/leads leads:read

List leads

Leads in your pipeline, newest first.

Parameters

status string
Pipeline stage: new, contacted, qualified, proposal, won or lost.
search string
Matches name, email, phone or company.
from date
Created on or after this date, YYYY-MM-DD.
to date
Created on or before this date, YYYY-MM-DD.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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
    }
  }
}
GET /api/v1/team team:read

List team members

The people in your workspace (co-admins, managers, team members and remote members) with role, designation, department and account status.

Parameters

search string
Matches name or email.
page integer
Page number, from 1.
per_page integer
Rows per page, 1 to 100. Defaults to 25.

Example request

curl 'https://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

GET /api/v1/reports/revenue reports:read

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

from date
Start of the range, YYYY-MM-DD.
to date
End of the range, YYYY-MM-DD.
group_by string
month or day. Defaults to month.

Example request

curl 'https://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
      }
    ]
  }
}

Create and update

POST/api/v1/leadsleads:write

Add a lead

Adds a lead to your pipeline at the "new" stage, as when the team adds one manually.

Parameters

first_namestring · required
First name.
last_namestring
Optional.
emailstring · required
Email address.
phonestring
Optional.
company_namestring
Optional.
messagestring · required
What they need, in a sentence or two.
expected_valuenumber
Optional deal value.
assigneestring
Optional team member: their id, email or full name.

Example request

curl -X POST 'https://clientish.io/api/v1/leads' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "first_name": "Nadia",
    "last_name": "Rahman",
    "email": "nadia@brightpath.io",
    "phone": "+880 1711 000000",
    "company_name": "BrightPath",
    "message": "Looking for monthly SEO.",
    "expected_value": 1200
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1841,
    "id": 772,
    "type": "lead",
    "summary": "Lead Nadia Rahman added",
    "created": true
  }
}
PATCH/api/v1/leads/{id}leads:write

Update a lead

Moves a lead to another stage, assigns it, sets a follow-up or adds a line to its internal notes. Only the fields you send change. Winning a lead is done by converting it in the app.

Parameters

idinteger · required
The lead id.
stagestring
new, contacted, meeting, qualified, proposal or lost.
lost_reasonstring
Optional, when the stage is lost.
assigneestring
Optional team member: their id, email or full name.
follow_up_atstring
Follow-up time, YYYY-MM-DD HH:MM in your workspace’s timezone.
follow_up_notestring
Optional, with follow_up_at.
notestring
A line to add to the internal notes.

Example request

curl -X PATCH 'https://clientish.io/api/v1/leads/772' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "stage": "qualified",
    "follow_up_at": "2026-10-12 11:00",
    "follow_up_note": "Send the proposal"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1842,
    "id": 772,
    "type": "lead",
    "summary": "Lead Nadia Rahman: stage New → Qualified, follow-up 2026-10-12 11:00"
  }
}
POST/api/v1/clientsclients:write

Create or update a client

Adds a client account. No invite or welcome email is sent; invite them from the app when ready. When a client with that email is already in your workspace, their name, phone and company are updated instead: the response is 200 with created false.

Parameters

first_namestring · required
First name.
last_namestring · required
Last name.
emailstring · required
Email address. Finds the client when they already exist.
phonestring
Optional.
company_namestring
Optional.

Example request

curl -X POST 'https://clientish.io/api/v1/clients' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "first_name": "Sara",
    "last_name": "Khan",
    "email": "sara@khanco.com",
    "phone": "+1 415 555 0199",
    "company_name": "Khan Co"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1843,
    "id": 2304,
    "type": "client",
    "summary": "Client Sara Khan added (no invite sent)",
    "created": true
  }
}
PATCH/api/v1/clients/{id}clients:write

Update a client

Changes a client’s name, phone, company, tax id or address. Only the fields you send change. The email address isn’t changed here.

Parameters

idinteger · required
The client id.
first_namestring
Optional.
last_namestring
Optional.
phonestring
Optional.
company_namestring
Optional.
tax_idstring
Optional.
addressstring
Optional.
citystring
Optional.
statestring
Optional.
zipstring
Optional.
countrystring
Optional.

Example request

curl -X PATCH 'https://clientish.io/api/v1/clients/2304' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "phone": "+1 415 555 0123",
    "city": "Oakland",
    "country": "US"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1844,
    "id": 2304,
    "type": "client",
    "summary": "Client Sara Khan: phone, city, country updated"
  }
}
POST/api/v1/clients/{id}/notesclients:write

Add a client note

Adds a line to the client’s team note, which only your team sees.

Parameters

idinteger · required
The client id.
notestring · required
What to add.

Example request

curl -X POST 'https://clientish.io/api/v1/clients/2304/notes' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "note": "Prefers WhatsApp over email."
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1845,
    "id": 2304,
    "type": "client",
    "summary": "Note added to client Sara Khan: Prefers WhatsApp over email."
  }
}
POST/api/v1/ordersorders:write

Create an order

Opens an order for one of your clients on one of your services, as when the team adds an order manually, with its invoice if the service has a price.

Parameters

client_idinteger · required
The client id.
service_idinteger · required
The service id, from List services.
notestring
Optional internal note.

Example request

curl -X POST 'https://clientish.io/api/v1/orders' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "client_id": 2304,
    "service_id": 31,
    "note": "Came in through the Shopify store"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1846,
    "id": 5121,
    "type": "order",
    "summary": "Order #ORD-5121 for Sara Khan (SEO Audit) created",
    "order_id": 5121,
    "order_no": "ORD-5121",
    "created": true
  }
}
PATCH/api/v1/orders/{id}orders:write

Change an order’s status

Moves an order to another of your order statuses. The client is notified just as when a person does it.

Parameters

idinteger · required
The order id.
statusstring · required
Status name, e.g. "Complete", or its id. Workspace overview lists yours.

Example request

curl -X PATCH 'https://clientish.io/api/v1/orders/5121' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "status": "Working"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1847,
    "id": 5121,
    "type": "order",
    "summary": "Order #ORD-5121 status Pending → Working"
  }
}
POST/api/v1/orders/{id}/tasksorders:write

Add a task to an order

Adds a task to an order, optionally with details, a due date and team members to do it.

Parameters

idinteger · required
The order id.
titlestring · required
Task title.
descriptionstring
Optional details.
due_datedate
Optional, YYYY-MM-DD.
assigneesstring[]
Optional team members, each an id, email or full name.

Example request

curl -X POST 'https://clientish.io/api/v1/orders/5121/tasks' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "title": "Kick-off call",
    "due_date": "2026-10-14",
    "assignees": [
      "rafi@northwind.studio"
    ]
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1848,
    "id": 5121,
    "type": "order",
    "summary": "Task \"Kick-off call\" added to order #ORD-5121"
  }
}
POST/api/v1/orders/{id}/notesorders:write

Add an order note

Adds a line to the order’s internal note, which only your team sees.

Parameters

idinteger · required
The order id.
notestring · required
What to add.

Example request

curl -X POST 'https://clientish.io/api/v1/orders/5121/notes' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "note": "Client paid the deposit by bank transfer."
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1849,
    "id": 5121,
    "type": "order",
    "summary": "Note added to order #ORD-5121: Client paid the deposit by bank transfer."
  }
}
POST/api/v1/ticketstickets:write

Open a ticket

Opens a support ticket for one of your clients at your "open" status, as when the team opens one manually.

Parameters

client_idinteger · required
The client id.
subjectstring · required
Subject.
messagestring · required
What the ticket is about.
prioritystring
Low, Medium, High or Urgent. Defaults to Medium.
order_idinteger
Optional related order id.

Example request

curl -X POST 'https://clientish.io/api/v1/tickets' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "client_id": 2304,
    "subject": "Login not working",
    "message": "Sara cannot sign in to the portal since this morning.",
    "priority": "High"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1850,
    "id": 843,
    "type": "ticket",
    "summary": "Ticket #TCK-843 opened for Sara Khan: Login not working",
    "ticket_id": 843,
    "ticket_no": "TCK-843",
    "created": true
  }
}
PATCH/api/v1/tickets/{id}tickets:write

Change a ticket’s status

Moves a ticket to another of your ticket statuses. The client is notified just as when a person does it.

Parameters

idinteger · required
The ticket id.
statusstring · required
Status name, e.g. "Closed", or its id.

Example request

curl -X PATCH 'https://clientish.io/api/v1/tickets/843' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "status": "Closed"
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1851,
    "id": 843,
    "type": "ticket",
    "summary": "Ticket #TCK-843 status Open → Closed"
  }
}
POST/api/v1/tickets/{id}/repliestickets:write

Reply on a ticket

Sends a reply to the client on a ticket, from your workspace. The client gets the usual email.

Parameters

idinteger · required
The ticket id.
messagestring · required
The reply, written to the client.

Example request

curl -X POST 'https://clientish.io/api/v1/tickets/843/replies' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "message": "Thanks Sara, we have reset your access. Try signing in again."
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1852,
    "id": 843,
    "type": "ticket",
    "summary": "Reply sent on ticket #TCK-843",
    "sent": true,
    "draft": false
  }
}
POST/api/v1/tickets/{id}/notestickets:write

Add a ticket note

Adds a line to the ticket’s internal note, which only your team sees.

Parameters

idinteger · required
The ticket id.
notestring · required
What to add.

Example request

curl -X POST 'https://clientish.io/api/v1/tickets/843/notes' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "note": "Same issue as last month, so check the SSO settings."
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1853,
    "id": 843,
    "type": "ticket",
    "summary": "Note added to ticket #TCK-843: Same issue as last month, so check the SSO settings."
  }
}
POST/api/v1/invoicesinvoices:write

Create a draft invoice

Prepares an invoice for one client. It’s always a draft: the client sees nothing until someone on your team approves it, and nothing is ever marked paid. Until then there is no invoice, so the response’s id is the client’s.

Parameters

client_idinteger · required
The client id.
itemsobject[] · required
1 to 50 lines, each with name, qty and price, and optionally service_id (from List services) and description.
due_datedate
Optional, YYYY-MM-DD. Defaults to 7 days.
notesstring
Optional note on the invoice.

Example request

curl -X POST 'https://clientish.io/api/v1/invoices' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: 6f1d2c3e-zap-run-4821' \
  -d '{
    "client_id": 2304,
    "items": [
      {
        "name": "Website care, October",
        "qty": 1,
        "price": 250
      },
      {
        "name": "Extra landing page",
        "qty": 2,
        "price": 120,
        "service_id": 31
      }
    ],
    "due_date": "2026-10-21",
    "notes": "Thank you for your business."
  }'

Example response

{
  "data": {
    "done": true,
    "change_id": 1854,
    "id": 2304,
    "type": "client",
    "summary": "Invoice draft for Sara Khan (490), waiting for the team to approve",
    "created": false,
    "draft": true,
    "note": "Saved as a draft. The client sees nothing until someone on the agency's team approves it; tell the user that."
  }
}

Instant triggers

POST/api/v1/hooksevent read permission

Subscribe to an event

For Zapier, Make and similar tools: sends one webhook event to target_url the moment it happens, signed like any webhook. The subscription is an endpoint on your Webhooks tab tied to this key; revoking the key removes it. The key needs read permission for the event’s record (Orders for order events, and so on). A workspace can have up to 10 endpoints.

Parameters

target_urlstring · required
Where to POST the events. HTTPS on a public address.
eventstring · required
One of: order.created, order.status_changed, order.completed, invoice.created, invoice.paid, invoice.overdue, ticket.created, ticket.replied, client.created, lead.created.

Example request

curl -X POST 'https://clientish.io/api/v1/hooks' \
  -H 'Authorization: Bearer YOUR-API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "target_url": "https://hooks.zapier.com/hooks/standard/123456/abcdef/",
    "event": "order.created"
  }'

Example response

{
  "data": {
    "id": 58,
    "event": "order.created"
  }
}
DELETE/api/v1/hooks/{id}event read permission

Unsubscribe

Stops a subscription made with this key. Tools call it when a Zap or scenario is turned off.

Parameters

idinteger · required
The id that Subscribe to an event returned.

Example request

curl -X DELETE 'https://clientish.io/api/v1/hooks/58' \
  -H 'Authorization: Bearer YOUR-API-KEY'

Example response

{
  "data": {
    "deleted": true
  }
}
Written from the code behind /api/v1. Example values are made up; nothing here is a real client.
Scroll to Top