Klantly Developers

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, like message.received and message.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, add message.not_delivered yourself.

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) or google (fetched from the company's Google calendar). This field is added to every response containing an appointment, so an appointment's ETag changes once — an If-Match with an old value then returns 412; fetch the appointment again.
  • An appointment with source: "google" is read-only. PATCH, DELETE and the actions confirm, cancel, complete and notify return 409 with code conflict: 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.created would flood your endpoint with work that does not come from Klantly.
  • GET /availability does 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/availability read 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} and POST /v1/services/{id}/status (draft → planned → in progress → completed, plus cancelled).
  • Photos under /v1/services/{id}/photos: GET, POST (multipart) and DELETE. Every photo carries a kind: issue (the report), progress, completion (the result) or other. 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.write and services.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.signed and service.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, sort now only accepts created_at and updated_at (with or without a minus sign). stage_changed_at, starts_at, last_message_at and reviewed_at have 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 returns 422.
  • The default sort of conversations and reviews has therefore become -created_at (was -last_message_at and -reviewed_at respectively).
  • For calendar order, use filter[starts_from] and filter[starts_until] and sort yourself.

Idempotency.

  • An Idempotency-Key now 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 gets 422 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 /invoices next to the invoices, with the new field type (invoice or credit_note) and negative amounts. Without a filter you get both; use filter[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_at and credit_note_state; on a line credited_item_id. New status credited (fully credited).
  • Record the refund of a credit note with POST /invoices/{id}/refunded.
  • Refund via Mollie with via: mollie on POST /invoices/{id}/refunded, when the invoice was paid via Mollie.
  • New event invoice.credited. The credit note itself arrives as invoice.created with type credit_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-data in the field file; downloading through a signed link that is valid for fifteen minutes and needs no API key.
  • New scopes: messages.read, messages.send, reviews.read and reviews.write.
  • New events for the inbox and reviews, such as message.received, conversation.closed and review.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 (scope invoices.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 via POST /invoices with quote_id, returns 409 conflict with the id of the existing invoice in invoice_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.paid and order.status_changed.
  • New scopes: quotes.read, quotes.write, quotes.delete, quotes.send, invoices.read, invoices.write, invoices.delete, invoices.send, orders.read and orders.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}/test and POST /appointments/{id}/notify fall 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 gives 422 instead of a server error. PATCH /work-orders/{id} with only items now also gives work_order.updated.
  • PATCH /tasks/{id} with links keeps links the API does not show, and a change shows up in the task's history.
  • POST /appointments/{id}/notify gives 409 if the company has switched the template off. An appointment created as confirmed has confirmed_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, title is no longer required when you pass an appointment_type_id: the title then becomes the name of the appointment type. Without either you get 422.
  • 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.moved and work_order.completed.
  • New scopes: appointments.read, appointments.write, appointments.delete, appointments.send, tasks.read, tasks.write, tasks.delete, work_orders.read, work_orders.write and work_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 /events to 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.manage and events.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 /me to test your connection.
  • Customers: list, retrieve, create, update and convert a lead into a customer.
  • Cursor pagination, filters, Idempotency-Key and If-Match.
  • Error messages according to RFC 9457, in four languages.
  • OpenAPI 3.1 specification in four languages.

Last updated on October 14, 2026