API reference
Deals
The cards on your pipeline board: create, update, move to another stage, win, lose and archive.
Endpoints
-
GET
/dealsList deals -
GET
/deals/{deal}Retrieve a deal -
POST
/dealsCreate a deal -
PATCH
/deals/{deal}Update a deal -
POST
/deals/{deal}/moveMove a deal -
POST
/deals/{deal}/winWin a deal -
POST
/deals/{deal}/loseLose a deal -
POST
/deals/{deal}/archiveArchive a deal -
POST
/deals/{deal}/unarchiveRestore a deal
List deals
/api/v1/deals
A list of deals, newest first. Archived deals are left out by default; use filter[archived] to include them. Filter by status, stage, customer, owner or modification date.
- Scope
-
deals.read— Read deals and pipeline stages - Required feature
pipeline
Query parameters
| Name | Type | Description |
|---|---|---|
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, updated_at or stage_changed_at; a minus sign in front means descending. one of: -created_at, created_at, -updated_at, updated_at, -stage_changed_at, stage_changed_at · default: -created_at |
filter[status] |
string | Only open, won or lost deals. one of: open, won, lost |
filter[stage_id] |
integer | Only deals in this stage (the id from List pipeline stages). |
filter[customer_id] |
string (uuid) | Only deals of this customer. |
filter[assigned_user_id] |
integer | Only deals assigned to this user. |
filter[archived] |
string | false (default) shows only deals on the board, true only archived ones, all both. one of: false, true, all · default: false |
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 "https://app.klantly.com/api/v1/deals?filter[status]=open" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'deals', [
'query' => [
'filter[status]' => 'open',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals?filter[status]=open', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/deals",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "open"
},
)
data = response.json()["data"]Response 200
The response is a list with cursor pagination: data contains the objects, meta the pagination.
{
"data": [
{
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_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
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid.
Retrieve a deal
/api/v1/deals/{deal}
One deal by id. The response includes an ETag you can send in If-Match when updating.
- Scope
-
deals.read— Read deals and pipeline stages - Required feature
pipeline
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Example request
curl "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('GET', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.get(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found.
Create a deal
/api/v1/deals
Puts a customer on the pipeline board. A customer can have only one open deal at a time: if there already is one, you get 409 with its id in deal_id. Without stage_id the deal lands in the default stage. If you send value, Klantly keeps that amount instead of calculating it from the quotes.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Body (JSON)
| Field | Type | Description |
|---|---|---|
customer_id
required
|
string (uuid) | The customer of the deal. |
title
optional
|
string | Title of the deal. can be empty (null) · at most 255 characters |
description
optional
|
string | Description. can be empty (null) · at most 10000 characters |
value
optional
|
number | Value as a string with two decimals, for example "8450.00". When creating or updating you may also send a number. can be empty (null) · from 0 to 99999999 |
stage_id
optional
|
integer | The stage the deal goes into. Without stage_id: the default stage. can be empty (null) |
assigned_user_id
optional
|
integer | The user handling the deal, or null. can be empty (null) |
Example request
curl -X POST "https://app.klantly.com/api/v1/deals" \
-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",
"title": "Veranda 5x3 m",
"value": "8450.00"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'deals', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'customer_id' => '9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70',
'title' => 'Veranda 5x3 m',
'value' => '8450.00',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals', {
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",
"title": "Veranda 5x3 m",
"value": "8450.00"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/deals",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"title": "Veranda 5x3 m",
"value": "8450.00"
},
)
data = response.json()["data"]Response 201
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
409
conflict— This conflicts with the current state. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Update a deal
/api/v1/deals/{deal}
Changes only the fields you send. To change the stage, use move, win or lose. Sending value sets value_source to manual.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send the ETag in If-Match and you will never accidentally overwrite a newer version.
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
title
optional
|
string | Title of the deal. at most 255 characters |
description
optional
|
string | Description. can be empty (null) · at most 10000 characters |
value
optional
|
number | Value as a string with two decimals, for example "8450.00". When creating or updating you may also send a number. from 0 to 99999999 |
assigned_user_id
optional
|
integer | The user handling the deal, or null. can be empty (null) |
Example request
curl -X PATCH "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"value": "9100.00",
"assigned_user_id": 7
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
'json' => [
'value' => '9100.00',
'assigned_user_id' => 7,
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"value": "9100.00",
"assigned_user_id": 7
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"value": "9100.00",
"assigned_user_id": 7
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
412
precondition_failed— The record has been changed in the meantime.
Move a deal
/api/v1/deals/{deal}/move
Moves the deal to another stage, exactly like dragging it on the board. If that is a won or lost stage, the deal becomes won or lost too.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
stage_id
required
|
integer | The id of the stage the deal moves to (from List pipeline stages). |
notes
optional
|
string | Optional note; kept in the history of the deal together with the stage change. can be empty (null) · at most 500 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/move" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"stage_id": 3
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/move', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'stage_id' => 3,
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/move', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"stage_id": 3
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/move",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"stage_id": 3
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Win a deal
/api/v1/deals/{deal}/win
Moves the deal to the stage your company has set up as won. If it is already won, nothing changes.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Example request
curl -X POST "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/win" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/win', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/win', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/win",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Lose a deal
/api/v1/deals/{deal}/lose
Moves the deal to the stage your company has set up as lost, optionally with a reason.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Body (JSON)
| Field | Type | Description |
|---|---|---|
lost_reason
optional
|
string | Why the deal was lost, or null. can be empty (null) · at most 255 characters |
Example request
curl -X POST "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/lose" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"lost_reason": "Te duur"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/lose', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'lost_reason' => 'Te duur',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/lose', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"lost_reason": "Te duur"
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/lose",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"lost_reason": "Te duur"
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
422
validation_failed— The input is invalid. -
422
unknown_field— The input contains an unknown field. -
415
unsupported_media_type— This format is not supported. -
413
payload_too_large— The request body is too large. -
404
not_found— Not found. -
409
invalid_state_transition— This action is not possible in the current status. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Archive a deal
/api/v1/deals/{deal}/archive
Takes the deal off the board without deleting it. Deals cannot be deleted through the API.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Example request
curl -X POST "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/archive" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/archive', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/archive', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/archive",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
Restore a deal
/api/v1/deals/{deal}/unarchive
Puts an archived deal back on the board.
- Scope
-
deals.write— Create, update, move and archive deals - Required feature
pipeline
Send an Idempotency-Key and a retry after a timeout will never create a duplicate record.
Path parameters
| Name | Type | Description |
|---|---|---|
deal required |
string (uuid) | The id (UUID) of the deal. |
Example request
curl -X POST "https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/unarchive" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f"$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/unarchive', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/unarchive', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/deals/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/unarchive",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Response 200
{
"data": {
"object": "deal",
"id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"title": "Veranda 5x3 m",
"description": null,
"status": "open",
"value": "8450.00",
"currency": "EUR",
"value_source": "manual",
"stage": {
"object": "pipeline_stage",
"id": "3"
},
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"assigned_user_id": "7",
"source": "api",
"lost_reason": null,
"is_archived": false,
"stage_changed_at": "2026-09-14T10:15:00Z",
"won_at": null,
"lost_at": null,
"archived_at": null,
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Possible errors
-
403
insufficient_scope— This API key has no access to this action. -
404
not_found— Not found. -
422
idempotency_key_reused— This Idempotency-Key was already used for a different request. -
409
idempotency_in_progress— A request with this Idempotency-Key is still in progress.
The object
All fields are always present; a field without a value is null.
| Field | Type | Description |
|---|---|---|
object |
string | Always "deal". |
id |
string (uuid) | Unique id (UUID). |
title |
string | Title of the deal. |
description |
string | Description. can be empty (null) |
status |
string | open, won or lost; follows from the stage. one of: open, won, lost |
value |
string | Value as a string with two decimals, for example "8450.00". When creating or updating you may also send a number. |
currency |
string | Always "EUR". |
value_source |
string | quotes: the value follows from the linked quotes; manual: set by hand. one of: quotes, manual |
stage |
object | The stage the deal is in; the names are in List pipeline stages. can be empty (null) |
stage.object |
string | Always "pipeline_stage". |
stage.id |
string | Id of the stage. |
customer_id |
string (uuid) | The customer of the deal. can be empty (null) |
assigned_user_id |
string | The user handling the deal, or null. can be empty (null) |
source |
string | Where the deal came from; through the API that is api. can be empty (null) |
lost_reason |
string | Why the deal was lost, or null. can be empty (null) |
is_archived |
boolean | Archived: no longer on the board. |
stage_changed_at |
string (date-time) | When the deal last changed stage. can be empty (null) |
won_at |
string (date-time) | When the deal was won. can be empty (null) |
lost_at |
string (date-time) | When the deal was lost. can be empty (null) |
archived_at |
string (date-time) | When the deal was archived. can be empty (null) |
created_at |
string (date-time) | Created at (UTC). |
updated_at |
string (date-time) | Last updated at (UTC). |