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.

Read only · v1 https://workspace.clientish.io/api/v1

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.

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 — it stops working at once.

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.

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.

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

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 is not switched on for the workspace.
404Not foundNo record with that id in your workspace, or the URL is not an API endpoint.
422UnprocessableA parameter could not be used — for example a date that is not 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 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

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 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_at and the record itself when order matters.
  • Endpoints must be HTTPS on a public address. Deliveries are kept for 30 days.

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://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"
    ]
  }
}

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 switched 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://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 /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://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

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://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 /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://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

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://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 /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://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

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://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 /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://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

GET /api/v1/services services:read

List services

Your service catalogue: names, descriptions, one-off 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://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
    }
  }
}
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://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
    }
  }
}
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://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

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://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
      }
    ]
  }
}
Written from the code behind /api/v1. Example values are made up; nothing here is a real client.
Scroll to Top