Changelog
All changes to the Klantly API, newest first.
Within version 1 things are only added. A change that could break existing integrations only comes in a new version and is announced well in advance with the Deprecation and Sunset headers.
14 October 2026
New event: message.not_delivered. WhatsApp did not deliver a message you or the company sent. Meta
reports this through its status webhook, sometimes minutes after sending, so message.sent alone does not
mean it arrived.
- Scope
messages.read, likemessage.receivedandmessage.sent. The payload is the message itself. - Most common reasons: the number cannot receive WhatsApp, or more than 24 hours have passed since the customer last wrote (only an approved template gets through then).
- Endpoints subscribed with
["*"]receive this event automatically; with a fixed list, addmessage.not_deliveredyourself.
13 October 2026
Appointments from the Google calendar. A company can now let its Google calendar run the other way: the items from the selected Google calendars then appear as appointments in Klantly.
- Every appointment carries
source:klantly(the normal case) orgoogle(fetched from the company's Google calendar). This field is added to every response containing an appointment, so an appointment'sETagchanges once — anIf-Matchwith an old value then returns412; fetch the appointment again. - An appointment with
source: "google"is read-only.PATCH,DELETEand the actionsconfirm,cancel,completeandnotifyreturn409with codeconflict: the work on it happens in Google, so that is where the change belongs. Reading works as usual, and it counts towards the calendar being busy. - Such appointments produce no webhook events. A company calendar can hold hundreds of items; sending
those as
appointment.createdwould flood your endpoint with work that does not come from Klantly. GET /availabilitydoes count them, because they are ordinary appointments. If fetching was off, nothing changes.- Availability: an all-day appointment now blocks the whole day.
GET /v1/availabilityread the raw start and end time, and for an all-day appointment both sit at 00:00 of the last day — so the window was empty and the day came back as completely free. The same now goes for an appointment that runs past midnight: it counts on every day it touches. Your Google calendar still does not count here; that remains the deliberate exception.
27 September 2026
Service requests. A new resource: the REPORT that comes before the work — a fault, a warranty case, a complaint or a question from a customer. The actual work with hours and amounts stays the work order; a service request has no lines and never ends up on an invoice.
GET|POST /v1/services,GET|PATCH|DELETE /v1/services/{id}andPOST /v1/services/{id}/status(draft → planned → in progress → completed, plus cancelled).- Photos under
/v1/services/{id}/photos:GET,POST(multipart) andDELETE. Every photo carries akind:issue(the report),progress,completion(the result) orother. The same limits as work order photos: 10 MB per photo, 30 per request, and a download link that is valid for fifteen minutes. - Scopes
services.read,services.writeandservices.delete; they belong to the work orders feature, so a key that may use work orders can grant them. - Events
service.created,service.updated,service.status_changed,service.completed,service.signedandservice.deleted. - Signing, scheduling the appointment and converting to a work order deliberately do not go through the
API: those are things a person does on site. You can link an appointment yourself with
appointment_id.
20 September 2026
Sorting.
- For deals, appointments, conversations and reviews,
sortnow only acceptscreated_atandupdated_at(with or without a minus sign).stage_changed_at,starts_at,last_message_atandreviewed_athave been dropped: those columns can be empty, and the cursor then skipped the rest of the list — rows without a value were unreachable. A dropped value now returns422. - The default sort of conversations and reviews has therefore become
-created_at(was-last_message_atand-reviewed_atrespectively). - For calendar order, use
filter[starts_from]andfilter[starts_until]and sort yourself.
Idempotency.
- An
Idempotency-Keynow applies per company instead of per key. That way a retry keeps its guarantee when your integration switches to the new token during key rotation. If two integrations of the same company use the same value for a different request, the second gets422 idempotency_key_reused; give each integration its own prefix. - A 404 or 422 is no longer stored for 24 hours: the same key with a corrected body simply executes the request.
18 September 2026
Security.
- A webhook endpoint can no longer point to an internal address wrapped as IPv4-in-IPv6 (such as
[::ffff:7f00:1]for 127.0.0.1). Such addresses are now refused just like their normal form.
Credit notes.
- Credit a sent or paid invoice with
POST /invoices/{id}/credit: everything, a quantity per line, or a free amount including VAT. The response is the credit note. - Credit notes are in
GET /invoicesnext to the invoices, with the new fieldtype(invoiceorcredit_note) and negative amounts. Without a filter you get both; usefilter[type]to pick one. - New fields on an invoice:
type,credited_amount,credit_note_ids,credited_invoice_id,credited_invoice_number,credit_reason,refund_amount,refunded_atandcredit_note_state; on a linecredited_item_id. New statuscredited(fully credited). - Record the refund of a credit note with
POST /invoices/{id}/refunded. - Refund via Mollie with
via: mollieonPOST /invoices/{id}/refunded, when the invoice was paid via Mollie. - New event
invoice.credited. The credit note itself arrives asinvoice.createdwithtypecredit_note. - A (partly) credited invoice can no longer be cancelled, and you cannot record a payment on a credit note (
409). Sending leaves the status of a credit note or credited invoice unchanged. - The guide Invoices to your accounting system explains how to book credit notes.
17 September 2026
Inbox, reviews and attachments.
- Inbox: read conversations and messages, reply on behalf of the company through the chat widget, WhatsApp or email, and close a conversation. Replying requires the feature of that conversation's channel.
- Reviews: list reviews and review requests, and ask a customer for a review with
POST /review-requests. Creating a review yourself is deliberately not possible — it should come from the customer. - Attachments on a quote and photos on a work order: list, upload and delete. Uploading goes as
multipart/form-datain the fieldfile; downloading through a signed link that is valid for fifteen minutes and needs no API key. - New scopes:
messages.read,messages.send,reviews.readandreviews.write. - New events for the inbox and reviews, such as
message.received,conversation.closedandreview.received.
Sales and finance.
- Quotes: list, create, update (drafts only), send, accept, reject and delete, and fetch the PDF with
GET /quotes/{id}/pdf. - Invoices: list, create, update (drafts only), send, cancel, record payments and delete, and fetch the PDF with
GET /invoices/{id}/pdf. - Fetch the customer's payment page with
GET /invoices/{id}/payment-link(scopeinvoices.send, Online payments feature). - Turn an accepted quote into a draft invoice with
POST /quotes/{id}/invoice. A quote has at most one invoice: a second request, also viaPOST /invoiceswithquote_id, returns409 conflictwith the id of the existing invoice ininvoice_id. - Quote and invoice lines support
unit_price_incl: a price including VAT that is exact to the cent. - Web shop orders: list them, change the status, pass on track and trace and add notes.
- Events for quotes, invoices and orders, such as
quote.accepted,invoice.paidandorder.status_changed. - New scopes:
quotes.read,quotes.write,quotes.delete,quotes.send,invoices.read,invoices.write,invoices.delete,invoices.send,orders.readandorders.write. - New guide: Invoices to your accounting system.
15 September 2026
Security.
- Changing, turning off, deleting or rotating the secret of a webhook endpoint requires a key that can read everything the endpoint receives (otherwise
403 insufficient_scope). - Webhooks can no longer point to Klantly itself, not through an IP address or your own domain name either, nor to IPv6 forms of internal addresses.
- A body that is not a valid JSON object gives
400 bad_request(it used to be read as an empty body). - Times outside 1970–2100 and dates that do not exist, such as 31 February, give
422. POST /webhook-endpoints/{id}/testandPOST /appointments/{id}/notifyfall under the limit for heavy actions.- API responses have
Cache-Control: no-store. - Administrators get a security email when an API key is revoked, gains permissions or gets a different IP restriction, and when a webhook endpoint is created, gets a different address or a new secret.
- An IP restriction must be at least /8 (IPv4) or /32 (IPv6).
Improvements after a review.
- A webhook endpoint set up through the API follows the key that did so: if you revoke that key or take a read scope off it, the endpoint no longer receives those events. A different URL or a new secret requires a key that can read everything the endpoint receives (otherwise
403 insufficient_scope). - A retried or resent delivery checks again whether the event is still allowed.
- A webhook URL with a trailing dot or with characters outside ASCII is refused; use the xn-- form.
- Cursors are encrypted: use them as you receive them.
- A replayed response (same
Idempotency-Key) when creating or rotating a webhook endpoint no longer contains the secret. - Work order lines have the field
discount_amount(a fixed discount); an amount that is too large gives422instead of a server error.PATCH /work-orders/{id}with onlyitemsnow also giveswork_order.updated. PATCH /tasks/{id}withlinkskeeps links the API does not show, and a change shows up in the task's history.POST /appointments/{id}/notifygives409if the company has switched the template off. An appointment created as confirmed hasconfirmed_at.
Appointments, tasks and work orders.
- Appointments: list, retrieve, schedule, update, confirm, cancel, complete and delete, and email the customer a confirmation, reschedule, cancellation or reminder.
- With
POST /appointments,titleis no longer required when you pass anappointment_type_id: the title then becomes the name of the appointment type. Without either you get422. - List appointment types and free time slots.
- Tasks: list, retrieve, create, link to customers and other records, move, complete, reopen, cancel and delete, plus comments and task boards.
- Work orders: list, retrieve, create with lines and a checklist, update, change the status and delete.
- Events for appointments, tasks and work orders, such as
appointment.created,task.movedandwork_order.completed. - New scopes:
appointments.read,appointments.write,appointments.delete,appointments.send,tasks.read,tasks.write,tasks.delete,work_orders.read,work_orders.writeandwork_orders.delete. - New guides: Appointments from your own planning and Work orders from your ERP.
Webhooks, deals and more.
- Webhooks following Standard Webhooks: manage endpoints in Klantly and through the API, with signing, retries, a delivery log and a test message. Read the Webhooks guide.
- Events:
GET /eventsto catch up on missed webhooks. - Deals: list, retrieve, create, update, move, win, lose and archive, and list the pipeline stages.
- Notes on customers and deals.
- The timeline of a customer.
- Retrieve users and company details.
- New scopes:
deals.read,deals.write,users.read,company.read,webhooks.manageandevents.read. - New guides: Website form to lead and Make and n8n.
14 September 2026
First version of the Klantly API.
- Authentication with API keys, with scopes, expiry date, IP allowlist and renewing.
GET /meto test your connection.- Customers: list, retrieve, create, update and convert a lead into a customer.
- Cursor pagination, filters,
Idempotency-KeyandIf-Match. - Error messages according to RFC 9457, in four languages.
- OpenAPI 3.1 specification in four languages.
Last updated on October 14, 2026