Klantly Developers

API reference

Reviews

Customer reviews and the requests to write one. Reading is fully supported; creating a review yourself deliberately is not — that should come from the customer.

Endpoints

List reviews

GET /api/v1/reviews

A list of reviews, newest first. Filter by status, source, rating, customer or change date. With filter[status]=published you get what is public.

Scope
reviews.read — Read reviews and review requests
Required feature
reviews

Query parameters

NameTypeDescription
limit integer Number of results per page. from 1 to 100 · default: 50
cursor string The next_cursor or prev_cursor from meta of the previous response.
sort string Sort by created_at or updated_at; a leading minus sign sorts descending. one of: -reviewed_at, reviewed_at, -created_at, created_at, -updated_at, updated_at · default: -reviewed_at
filter[status] string Only reviews with this status: published or hidden. one of: published, hidden
filter[source] string Only reviews from this source: request (through a review request), manual (entered by the company), configurator or google. one of: request, manual, configurator, google
filter[rating] integer Only reviews with this rating (1 to 5). from 1 to 5
filter[customer_id] string (uuid) Only what belongs to this customer.
filter[updated_since] string (date-time) Only what changed since this moment: ISO 8601 with a time zone, for example 2026-09-14T10:15:00Z. Useful for synchronising.

Example request

cURL
curl "https://app.klantly.com/api/v1/reviews?filter[status]=published&sort=-reviewed_at" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.klantly.com/api/v1/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);

$response = $client->request('GET', 'reviews', [
    'query' => [
        'filter[status]' => 'published',
        'sort' => '-reviewed_at',
    ],
]);

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/reviews?filter[status]=published&sort=-reviewed_at', {
  headers: {
    Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
  },
});

const { data } = await response.json();
Python
import os

import requests

response = requests.get(
    "https://app.klantly.com/api/v1/reviews",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
    params={
        "filter[status]": "published",
        "sort": "-reviewed_at"
    },
)
data = response.json()["data"]

Response 200

The response is a list with cursor pagination: data contains the objects, meta the pagination.

Example response
{
  "data": [
    {
      "object": "review",
      "id": "9d3f9c34-8d9e-4f01-9123-c4d5e6f7a8b3",
      "status": "published",
      "hidden_reason": null,
      "source": "request",
      "rating": 5,
      "comment": "Strakke veranda, netjes geplaatst en goed opgeruimd.",
      "author": {
        "name": "Jan de Vries",
        "city": "Utrecht"
      },
      "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
      "language": "nl",
      "is_verified": true,
      "consent_publish": true,
      "reply": null,
      "replied_at": null,
      "reviewed_at": null,
      "created_at": "2026-09-14T10:15:00Z",
      "updated_at": "2026-09-14T10:15:00Z"
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
    "prev_cursor": null
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Get review

GET /api/v1/reviews/{review}

One review by id, with the rating, the text, the author and the reply from the company.

Scope
reviews.read — Read reviews and review requests
Required feature
reviews

Path parameters

NameTypeDescription
review required string (uuid) The id (UUID) of the review.

Example request

cURL
curl "https://app.klantly.com/api/v1/reviews/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.klantly.com/api/v1/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);

$response = $client->request('GET', 'reviews/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/reviews/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
  headers: {
    Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
  },
});

const { data } = await response.json();
Python
import os

import requests

response = requests.get(
    "https://app.klantly.com/api/v1/reviews/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
)
data = response.json()["data"]

Response 200

Example response
{
  "data": {
    "object": "review",
    "id": "9d3f9c34-8d9e-4f01-9123-c4d5e6f7a8b3",
    "status": "published",
    "hidden_reason": null,
    "source": "request",
    "rating": 5,
    "comment": "Strakke veranda, netjes geplaatst en goed opgeruimd.",
    "author": {
      "name": "Jan de Vries",
      "city": "Utrecht"
    },
    "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
    "language": "nl",
    "is_verified": true,
    "consent_publish": true,
    "reply": null,
    "replied_at": null,
    "reviewed_at": null,
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

List review requests

GET /api/v1/review-requests

The sent and open review requests, newest first, with their status and the link for the customer.

Scope
reviews.read — Read reviews and review requests
Required feature
reviews

Query parameters

NameTypeDescription
limit integer Number of results per page. from 1 to 100 · default: 50
cursor string The next_cursor or prev_cursor from meta of the previous response.
filter[status] string Only requests with this status: scheduled, sent, opened, completed, failed, cancelled or expired. one of: scheduled, sent, opened, completed, failed, cancelled, expired
filter[customer_id] string (uuid) Only what belongs to this customer.
filter[updated_since] string (date-time) Only what changed since this moment: ISO 8601 with a time zone, for example 2026-09-14T10:15:00Z. Useful for synchronising.

Example request

cURL
curl "https://app.klantly.com/api/v1/review-requests" \
  -H "Authorization: Bearer $KLANTLY_API_KEY"
PHP
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.klantly.com/api/v1/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);

$response = $client->request('GET', 'review-requests');

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/review-requests', {
  headers: {
    Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
  },
});

const { data } = await response.json();
Python
import os

import requests

response = requests.get(
    "https://app.klantly.com/api/v1/review-requests",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
)
data = response.json()["data"]

Response 200

The response is a list with cursor pagination: data contains the objects, meta the pagination.

Example response
{
  "data": [
    {
      "object": "review_request",
      "id": "9d3f9d45-9e0f-4012-a234-d5e6f7a8b9c4",
      "status": "sent",
      "channel": "email",
      "trigger": "manual",
      "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
      "recipient": {
        "name": "Jan de Vries",
        "email": "jan@example.com",
        "phone": null
      },
      "language": "nl",
      "url": "https://app.klantly.com/review/Xk2p9Qm4Rt7vB1nC8dE5fG3hJ6kL0mN2pQ4rS7tU",
      "review_id": null,
      "scheduled_at": null,
      "sent_at": null,
      "opened_at": null,
      "completed_at": null,
      "created_at": "2026-09-14T10:15:00Z",
      "updated_at": "2026-09-14T10:15:00Z"
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
    "prev_cursor": null
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

Send review request

POST /api/v1/review-requests

Asks a customer for a review. With channel=email or whatsapp Klantly sends the message using the company template; with channel=link you only create the link and share it yourself. Pass customer_id, or fill in name plus email or phone. The same rules as on screen: no second open request to the same customer, the company cooldown, and nobody who unsubscribed — otherwise you get 409 conflict.

Scope
reviews.write — Send review requests to customers (creating a review itself is not possible)
Required feature
reviews

Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.

Body (JSON)

FieldTypeDescription
customer_id optional string (uuid) The customer who receives the request.
name optional string Name of the recipient. Required when you do not pass a customer_id. at most 120 characters
email optional string (email) Email address of the recipient. Required for channel=email. at most 255 characters
phone optional string Phone number of the recipient. Required for channel=whatsapp. at most 50 characters
channel optional string How the request reaches the customer: email, whatsapp or link (Klantly then sends nothing). one of: email, whatsapp, link
language optional string The language of the message to the customer. one of: nl, en, de, fr

Example request

cURL
curl -X POST "https://app.klantly.com/api/v1/review-requests" \
  -H "Authorization: Bearer $KLANTLY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
  -d '{
  "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
  "channel": "email"
}'
PHP
$client = new \GuzzleHttp\Client([
    'base_uri' => 'https://app.klantly.com/api/v1/',
    'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);

$response = $client->request('POST', 'review-requests', [
    'headers' => [
        'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
    ],
    'json' => [
        'customer_id' => '9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70',
        'channel' => 'email',
    ],
]);

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/review-requests', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
  },
  body: JSON.stringify({
  "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
  "channel": "email"
}),
});

const { data } = await response.json();
Python
import os

import requests

response = requests.post(
    "https://app.klantly.com/api/v1/review-requests",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
        "Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
    },
    json={
        "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
        "channel": "email"
    },
)
data = response.json()["data"]

Response 201

Example response
{
  "data": {
    "object": "review_request",
    "id": "9d3f9d45-9e0f-4012-a234-d5e6f7a8b9c4",
    "status": "sent",
    "channel": "email",
    "trigger": "manual",
    "customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
    "recipient": {
      "name": "Jan de Vries",
      "email": "jan@example.com",
      "phone": null
    },
    "language": "nl",
    "url": "https://app.klantly.com/review/Xk2p9Qm4Rt7vB1nC8dE5fG3hJ6kL0mN2pQ4rS7tU",
    "review_id": null,
    "scheduled_at": null,
    "sent_at": null,
    "opened_at": null,
    "completed_at": null,
    "created_at": "2026-09-14T10:15:00Z",
    "updated_at": "2026-09-14T10:15:00Z"
  }
}

Possible errors

In addition, every endpoint can return the general errors, such as an invalid key or a reached limit. See all error codes.

The object

All fields are always present; a field without a value is null.

FieldTypeDescription
object string Always "review".
id string (uuid) Id of the review.
status string published or hidden. one of: published, hidden
hidden_reason string Why the review is hidden, if it is. can be empty (null) · one of: spam, offensive, not_a_customer, privacy, duplicate, other
source string Where the review came from: request, manual, configurator or google. one of: request, manual, configurator, google
rating integer The rating, 1 to 5. from 1 to 5
comment string What the customer wrote. can be empty (null)
author object The author, as shown publicly.
author.name string Name of the author. can be empty (null)
author.city string City of the author. can be empty (null)
customer_id string (uuid) The customer who wrote the review, if known. can be empty (null)
language string The language the review was written in. can be empty (null) · one of: nl, en, de, fr
is_verified boolean True when the review came in through a Klantly review request: the author is then demonstrably a customer.
consent_publish boolean Whether the author gave permission to show the review.
reply string The company's reply to this review. can be empty (null)
replied_at string (date-time) When the company replied. can be empty (null)
reviewed_at string (date-time) When the review was written. can be empty (null)
created_at string (date-time) When the review arrived in Klantly.
updated_at string (date-time) When the review last changed.