API-referentie
Offertes
Offertes met hun regels: opstellen, bijwerken zolang ze concept zijn, naar de klant mailen en de uitkomst vastleggen.
Endpoints
-
GET
/quotesOffertes opvragen -
GET
/quotes/{quote}Offerte ophalen -
POST
/quotesOfferte opstellen -
PATCH
/quotes/{quote}Offerte bijwerken -
POST
/quotes/{quote}/sendOfferte versturen -
POST
/quotes/{quote}/acceptOfferte accepteren -
POST
/quotes/{quote}/rejectOfferte afwijzen -
DELETE
/quotes/{quote}Offerte verwijderen
Offertes opvragen
/api/v1/quotes
Een lijst van offertes, nieuwste eerst, met hun regels. Filter op status, klant of wijzigingsdatum. Met filter[status]=sent en filter[updated_since] haal je op wat er sinds je vorige synchronisatie is verstuurd.
- Scope
-
quotes.read— Offertes lezen, met regels, bedragen en de klantgegevens erop - Vereiste functie
quotes
Queryparameters
| Naam | Type | Omschrijving |
|---|---|---|
limit
|
integer | Aantal resultaten per pagina. van 1 tot 100 · standaard: 50 |
cursor
|
string | De next_cursor of prev_cursor uit meta van het vorige antwoord. |
sort
|
string | Sortering op created_at of updated_at; een min-teken ervoor is aflopend. een van: -created_at, created_at, -updated_at, updated_at · standaard: -created_at |
filter[status]
|
string | Alleen offertes met deze status, bijvoorbeeld sent of accepted. een van: draft, pending_review, sent, viewed, accepted, rejected, expired, completed |
filter[customer_id]
|
string (uuid) | Alleen wat bij deze klant hoort. |
filter[updated_since]
|
string (date-time) | Alleen wat sinds dit tijdstip is gewijzigd: ISO 8601 mét tijdzone, bijvoorbeeld 2026-09-14T10:15:00Z. Handig om te synchroniseren. |
Voorbeeldverzoek
curl "https://app.klantly.com/api/v1/quotes?filter[status]=sent&sort=-updated_at" \
-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', 'quotes', [
'query' => [
'filter[status]' => 'sent',
'sort' => '-updated_at',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/quotes?filter[status]=sent&sort=-updated_at', {
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/quotes",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
params={
"filter[status]": "sent",
"sort": "-updated_at"
},
)
data = response.json()["data"]Antwoord 200
Het antwoord is een lijst met cursorpaginering: data bevat de objecten, meta de paginering.
{
"data": [
{
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
],
"meta": {
"limit": 50,
"next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
"prev_cursor": null
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig.
Offerte ophalen
/api/v1/quotes/{quote}
Eén offerte op id, met haar regels en bedragen. Het antwoord bevat een ETag die je bij bijwerken in If-Match kunt meesturen.
- Scope
-
quotes.read— Offertes lezen, met regels, bedragen en de klantgegevens erop - Vereiste functie
quotes
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
quote verplicht |
string (uuid) | De id (UUID) van de offerte. |
Voorbeeldverzoek
curl "https://app.klantly.com/api/v1/quotes/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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/quotes/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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Antwoord 200
{
"data": {
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden.
Offerte opstellen
/api/v1/quotes
Maakt een offerte aan bij een klant, altijd als concept. Klantly geeft hem een nummer en rekent de totalen uit; naam, adres en contactgegevens komen van de klant. Versturen doe je daarna met Offerte versturen.
- Scope
-
quotes.write— Offertes aanmaken en wijzigen (alleen concepten), en accepteren of afwijzen namens de klant - Vereiste functie
quotes
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
customer_id
verplicht
|
string (uuid) | De klant. Verplicht bij aanmaken; naam, adres en contactgegevens komen van de klant. |
title
verplicht
|
string | Titel van de offerte. Verplicht bij aanmaken. maximaal 255 tekens |
items
verplicht
|
array | De regels (maximaal 200). Bij invoer: een lijst met per regel name (verplicht) en optioneel type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate en is_taxable. Ze vervangen alle bestaande regels. |
description
optioneel
|
string | Korte omschrijving; staat boven de regels. kan leeg zijn (null) · maximaal 20000 tekens |
language
optioneel
|
string | Taal van de offerte: nl, en, de of fr. een van: nl, en, de, fr |
quote_date
optioneel
|
string (date) | Offertedatum (JJJJ-MM-DD). |
valid_until
optioneel
|
string (date) | Geldig tot en met (JJJJ-MM-DD). Laat je hem weg, dan geldt de standaardtermijn van het bedrijf. |
intro_text
optioneel
|
string | Inleiding boven de regels. kan leeg zijn (null) · maximaal 20000 tekens |
outro_text
optioneel
|
string | Slottekst onder de regels. kan leeg zijn (null) · maximaal 20000 tekens |
terms_conditions
optioneel
|
string | Voorwaarden op de offerte. kan leeg zijn (null) · maximaal 20000 tekens |
notes
optioneel
|
string | Opmerkingen voor de klant. kan leeg zijn (null) · maximaal 20000 tekens |
hide_line_amounts
optioneel
|
boolean | Verbergt de bedragen per regel op de offerte; de klant ziet alleen het totaal. |
discount_percentage
optioneel
|
number | Korting over het totaal, in procenten. van 0 tot 100 |
discount_amount
optioneel
|
number | Korting over het totaal, als bedrag. van 0 tot 9999999 |
discount_description
optioneel
|
string | Waarom de korting is gegeven; staat op de offerte. kan leeg zijn (null) · maximaal 500 tekens |
Voorbeeldverzoek
curl -X POST "https://app.klantly.com/api/v1/quotes" \
-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 400 × 300",
"items": [
{
"name": "Veranda",
"quantity": 1,
"unit_price": "2450.00",
"tax_rate": "21.00"
}
]
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'quotes', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'customer_id' => '9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70',
'title' => 'Veranda 400 × 300',
'items' => [
0 => [
'name' => 'Veranda',
'quantity' => 1,
'unit_price' => '2450.00',
'tax_rate' => '21.00',
],
],
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/quotes', {
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 400 × 300",
"items": [
{
"name": "Veranda",
"quantity": 1,
"unit_price": "2450.00",
"tax_rate": "21.00"
}
]
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/quotes",
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 400 × 300",
"items": [
{
"name": "Veranda",
"quantity": 1,
"unit_price": "2450.00",
"tax_rate": "21.00"
}
]
},
)
data = response.json()["data"]Antwoord 201
{
"data": {
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig. -
422
unknown_field— De invoer bevat een onbekend veld. -
415
unsupported_media_type— Dit formaat wordt niet ondersteund. -
413
payload_too_large— De body van het verzoek is te groot. -
403
limit_reached— De limiet van het abonnement is bereikt. -
422
idempotency_key_reused— Deze Idempotency-Key is al gebruikt voor een ander verzoek. -
409
idempotency_in_progress— Een verzoek met deze Idempotency-Key is nog bezig.
Offerte bijwerken
/api/v1/quotes/{quote}
Wijzigt alleen de velden die je meestuurt. items vervangt alle regels als geheel; die krijgen daarbij nieuwe id's. Alleen een concept kan nog veranderen: is de offerte verstuurd, dan heeft de klant hem gezien en volgt er een 409.
- Scope
-
quotes.write— Offertes aanmaken en wijzigen (alleen concepten), en accepteren of afwijzen namens de klant - Vereiste functie
quotes
Stuur de ETag mee in If-Match, dan overschrijf je nooit per ongeluk een nieuwere versie.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
quote verplicht |
string (uuid) | De id (UUID) van de offerte. |
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
title
optioneel
|
string | Titel van de offerte. Verplicht bij aanmaken. maximaal 255 tekens |
description
optioneel
|
string | Korte omschrijving; staat boven de regels. kan leeg zijn (null) · maximaal 20000 tekens |
language
optioneel
|
string | Taal van de offerte: nl, en, de of fr. een van: nl, en, de, fr |
quote_date
optioneel
|
string (date) | Offertedatum (JJJJ-MM-DD). |
valid_until
optioneel
|
string (date) | Geldig tot en met (JJJJ-MM-DD). Laat je hem weg, dan geldt de standaardtermijn van het bedrijf. |
intro_text
optioneel
|
string | Inleiding boven de regels. kan leeg zijn (null) · maximaal 20000 tekens |
outro_text
optioneel
|
string | Slottekst onder de regels. kan leeg zijn (null) · maximaal 20000 tekens |
terms_conditions
optioneel
|
string | Voorwaarden op de offerte. kan leeg zijn (null) · maximaal 20000 tekens |
notes
optioneel
|
string | Opmerkingen voor de klant. kan leeg zijn (null) · maximaal 20000 tekens |
hide_line_amounts
optioneel
|
boolean | Verbergt de bedragen per regel op de offerte; de klant ziet alleen het totaal. |
discount_percentage
optioneel
|
number | Korting over het totaal, in procenten. van 0 tot 100 |
discount_amount
optioneel
|
number | Korting over het totaal, als bedrag. van 0 tot 9999999 |
discount_description
optioneel
|
string | Waarom de korting is gegeven; staat op de offerte. kan leeg zijn (null) · maximaal 500 tekens |
items
optioneel
|
array | De regels (maximaal 200). Bij invoer: een lijst met per regel name (verplicht) en optioneel type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate en is_taxable. Ze vervangen alle bestaande regels. |
Voorbeeldverzoek
curl -X PATCH "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Veranda 400 × 300 (herzien)"
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('PATCH', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', [
'json' => [
'title' => 'Veranda 400 × 300 (herzien)',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
"title": "Veranda 400 × 300 (herzien)"
}),
});
const { data } = await response.json();import os
import requests
response = requests.patch(
"https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
json={
"title": "Veranda 400 × 300 (herzien)"
},
)
data = response.json()["data"]Antwoord 200
{
"data": {
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig. -
422
unknown_field— De invoer bevat een onbekend veld. -
415
unsupported_media_type— Dit formaat wordt niet ondersteund. -
413
payload_too_large— De body van het verzoek is te groot. -
404
not_found— Niet gevonden. -
412
precondition_failed— Het record is intussen gewijzigd. -
409
invalid_state_transition— Deze actie kan niet in de huidige status.
Offerte versturen
/api/v1/quotes/{quote}/send
Mailt de offerte naar de klant met het notificatiesjabloon van het bedrijf, zet de status op verstuurd en legt het tijdstip vast. Een eigen bericht komt in de mail te staan. Opnieuw versturen na een afwijzing zet de offerte terug op verstuurd.
- Scope
-
quotes.send— Offertes naar klanten mailen - Vereiste functie
quotes
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
quote verplicht |
string (uuid) | De id (UUID) van de offerte. |
Body (JSON)
| Veld | Type | Omschrijving |
|---|---|---|
message
optioneel
|
string | Persoonlijk bericht in de mail aan de klant. kan leeg zijn (null) · maximaal 5000 tekens |
Voorbeeldverzoek
curl -X POST "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send" \
-H "Authorization: Bearer $KLANTLY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f" \
-d '{
"message": "Bijgaand de offerte die we vanmorgen bespraken."
}'$client = new \GuzzleHttp\Client([
'base_uri' => 'https://app.klantly.com/api/v1/',
'headers' => ['Authorization' => 'Bearer ' . getenv('KLANTLY_API_KEY')],
]);
$response = $client->request('POST', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send', [
'headers' => [
'Idempotency-Key' => '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
],
'json' => [
'message' => 'Bijgaand de offerte die we vanmorgen bespraken.',
],
]);
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': '6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f',
},
body: JSON.stringify({
"message": "Bijgaand de offerte die we vanmorgen bespraken."
}),
});
const { data } = await response.json();import os
import requests
response = requests.post(
"https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/send",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
json={
"message": "Bijgaand de offerte die we vanmorgen bespraken."
},
)
data = response.json()["data"]Antwoord 200
{
"data": {
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
422
validation_failed— De invoer is ongeldig. -
422
unknown_field— De invoer bevat een onbekend veld. -
415
unsupported_media_type— Dit formaat wordt niet ondersteund. -
413
payload_too_large— De body van het verzoek is te groot. -
404
not_found— Niet gevonden. -
403
forbidden— Geen toegang. -
409
invalid_state_transition— Deze actie kan niet in de huidige status. -
422
idempotency_key_reused— Deze Idempotency-Key is al gebruikt voor een ander verzoek. -
409
idempotency_in_progress— Een verzoek met deze Idempotency-Key is nog bezig.
Offerte accepteren
/api/v1/quotes/{quote}/accept
Legt vast dat de klant akkoord is, bijvoorbeeld na een akkoord buiten Klantly om. Een geaccepteerde offerte kan daarna niet meer worden afgewezen.
- Scope
-
quotes.write— Offertes aanmaken en wijzigen (alleen concepten), en accepteren of afwijzen namens de klant - Vereiste functie
quotes
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
quote verplicht |
string (uuid) | De id (UUID) van de offerte. |
Voorbeeldverzoek
curl -X POST "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept" \
-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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept', [
'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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept', {
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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/accept",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Antwoord 200
{
"data": {
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden. -
409
invalid_state_transition— Deze actie kan niet in de huidige status. -
422
idempotency_key_reused— Deze Idempotency-Key is al gebruikt voor een ander verzoek. -
409
idempotency_in_progress— Een verzoek met deze Idempotency-Key is nog bezig.
Offerte afwijzen
/api/v1/quotes/{quote}/reject
Legt vast dat de klant heeft afgewezen. Je kunt daarna een herziene offerte sturen; dat zet hem weer op verstuurd.
- Scope
-
quotes.write— Offertes aanmaken en wijzigen (alleen concepten), en accepteren of afwijzen namens de klant - Vereiste functie
quotes
Stuur een Idempotency-Key mee, dan maakt een nieuwe poging na een time-out geen dubbel record.
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
quote verplicht |
string (uuid) | De id (UUID) van de offerte. |
Voorbeeldverzoek
curl -X POST "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject" \
-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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject', [
'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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject', {
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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/reject",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
"Idempotency-Key": "6f1c2d3e-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
},
)
data = response.json()["data"]Antwoord 200
{
"data": {
"object": "quote",
"id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
"number": "OFF-00042",
"status": "sent",
"title": "Veranda 400 × 300",
"description": null,
"customer_id": "9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
"customer": {
"type": "business",
"name": "De Vries Bouw",
"email": "jan@example.com",
"phone": "+31 6 12345678",
"address": "Dorpsstraat 1",
"postal_code": "3511 AB",
"city": "Utrecht",
"country": "NL",
"company_name": "De Vries Bouw",
"vat_number": null,
"coc_number": null
},
"deal_id": "9d3f7a20-1c5e-4d7b-8a2f-3e4c5d6f7a81",
"pipeline_stage_id": "stg_0q8v4n61h9x0zc",
"language": "nl",
"currency": "EUR",
"quote_date": "2026-09-16",
"valid_until": "2026-09-30",
"intro_text": null,
"outro_text": null,
"terms_conditions": null,
"notes": null,
"hide_line_amounts": false,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"subtotal": "2450.00",
"tax_amount": "514.50",
"total": "2964.50",
"sent_at": null,
"viewed_at": null,
"accepted_at": null,
"rejected_at": null,
"invoice_id": null,
"items": [
{
"object": "quote_item",
"id": "9d3f856a-b0f2-4b7f-8ecd-d2e0f1a2b3cc",
"type": "product",
"name": "Veranda",
"description": null,
"sku": null,
"quantity": "1.00",
"unit": "stuk",
"unit_price": "2450.00",
"unit_price_incl": null,
"discount_percentage": "0.00",
"discount_amount": "0.00",
"discount_description": null,
"line_total": "2450.00",
"line_total_incl": null,
"is_taxable": true,
"tax_rate": "21.00"
}
],
"created_at": "2026-09-14T10:15:00Z",
"updated_at": "2026-09-14T10:15:00Z"
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden. -
409
invalid_state_transition— Deze actie kan niet in de huidige status. -
422
idempotency_key_reused— Deze Idempotency-Key is al gebruikt voor een ander verzoek. -
409
idempotency_in_progress— Een verzoek met deze Idempotency-Key is nog bezig.
Offerte verwijderen
/api/v1/quotes/{quote}
Verwijdert de offerte. Alleen een concept kan weg: een verstuurde offerte blijft staan.
- Scope
-
quotes.delete— Offertes verwijderen - Vereiste functie
quotes
Padparameters
| Naam | Type | Omschrijving |
|---|---|---|
quote verplicht |
string (uuid) | De id (UUID) van de offerte. |
Voorbeeldverzoek
curl -X DELETE "https://app.klantly.com/api/v1/quotes/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('DELETE', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');
$data = json_decode((string) $response->getBody(), true)['data'];const response = await fetch('https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.KLANTLY_API_KEY}`,
},
});
const { data } = await response.json();import os
import requests
response = requests.delete(
"https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70",
headers={
"Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
},
)
data = response.json()["data"]Antwoord 200
{
"data": {
"object": "note",
"id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
"deleted": true
}
}Mogelijke fouten
-
403
insufficient_scope— Deze API-sleutel heeft geen toegang tot deze actie. -
404
not_found— Niet gevonden. -
409
invalid_state_transition— Deze actie kan niet in de huidige status.
Het object
Alle velden zijn altijd aanwezig; een veld zonder waarde is null.
| Veld | Type | Omschrijving |
|---|---|---|
object |
string | Altijd "quote". |
id |
string (uuid) | Unieke id (UUID). |
number |
string | Offertenummer, bijvoorbeeld OFF-00042; geeft Klantly zelf. |
status |
string | draft (concept), pending_review (ter controle), sent (verstuurd), viewed (bekeken), accepted (geaccepteerd), rejected (afgewezen), expired (verlopen) of completed (afgerond). een van: draft, pending_review, sent, viewed, accepted, rejected, expired, completed |
title |
string | Titel van de offerte. Verplicht bij aanmaken. kan leeg zijn (null) |
description |
string | Korte omschrijving; staat boven de regels. kan leeg zijn (null) |
customer_id |
string (uuid) | De klant. Verplicht bij aanmaken; naam, adres en contactgegevens komen van de klant. kan leeg zijn (null) |
customer |
object | De klantgegevens op de offerte, zoals ze bij het aanmaken waren. |
customer.type |
string | individual (particulier) of business (bedrijf). kan leeg zijn (null) |
customer.name |
string | Naam; bij een bedrijf de bedrijfsnaam. kan leeg zijn (null) |
customer.email |
string | E-mailadres. kan leeg zijn (null) |
customer.phone |
string | Telefoonnummer. kan leeg zijn (null) |
customer.address |
string | Straat en huisnummer. kan leeg zijn (null) |
customer.postal_code |
string | Postcode. kan leeg zijn (null) |
customer.city |
string | Plaats. kan leeg zijn (null) |
customer.country |
string | Land. kan leeg zijn (null) |
customer.company_name |
string | Bedrijfsnaam. kan leeg zijn (null) |
customer.vat_number |
string | Btw-nummer. kan leeg zijn (null) |
customer.coc_number |
string | KvK-nummer. kan leeg zijn (null) |
deal_id |
string (uuid) | De deal op het pipelinebord waar deze offerte bij hoort, of null. kan leeg zijn (null) |
pipeline_stage_id |
string | De fase waarin de offerte staat, of null. kan leeg zijn (null) |
language |
string | Taal van de offerte: nl, en, de of fr. een van: nl, en, de, fr |
currency |
string | Altijd "EUR". |
quote_date |
string (date) | Offertedatum (JJJJ-MM-DD). kan leeg zijn (null) |
valid_until |
string (date) | Geldig tot en met (JJJJ-MM-DD). Laat je hem weg, dan geldt de standaardtermijn van het bedrijf. kan leeg zijn (null) |
intro_text |
string | Inleiding boven de regels. kan leeg zijn (null) |
outro_text |
string | Slottekst onder de regels. kan leeg zijn (null) |
terms_conditions |
string | Voorwaarden op de offerte. kan leeg zijn (null) |
notes |
string | Opmerkingen voor de klant. kan leeg zijn (null) |
hide_line_amounts |
boolean | Verbergt de bedragen per regel op de offerte; de klant ziet alleen het totaal. |
discount_percentage |
string | Korting over het totaal, in procenten. |
discount_amount |
string | Korting over het totaal, als bedrag. |
discount_description |
string | Waarom de korting is gegeven; staat op de offerte. kan leeg zijn (null) |
subtotal |
string | Totaal zonder btw, als tekst met twee decimalen. |
tax_amount |
string | Btw-bedrag. |
total |
string | Totaal inclusief btw. |
sent_at |
string (date-time) | Wanneer de offerte naar de klant is gestuurd. kan leeg zijn (null) |
viewed_at |
string (date-time) | Wanneer de klant hem opende. kan leeg zijn (null) |
accepted_at |
string (date-time) | Wanneer de klant akkoord gaf. kan leeg zijn (null) |
rejected_at |
string (date-time) | Wanneer de klant afwees. kan leeg zijn (null) |
invoice_id |
string (uuid) | De factuur die uit deze offerte is gemaakt, of null. kan leeg zijn (null) |
items |
array<object> | De regels (maximaal 200). Bij invoer: een lijst met per regel name (verplicht) en optioneel type, description, sku, quantity, unit, unit_price, unit_price_incl, discount_percentage, discount_amount, discount_description, tax_rate en is_taxable. Ze vervangen alle bestaande regels. |
items.object |
string | Altijd "quote_item". |
items.id |
string (uuid) | Unieke id van de regel (UUID). |
items.type |
string | product, service, discount of fee. een van: product, service, discount, fee |
items.name |
string | Naam van de regel. |
items.description |
string | Omschrijving onder de regel. kan leeg zijn (null) |
items.sku |
string | Artikelnummer. kan leeg zijn (null) |
items.quantity |
string | Aantal. |
items.unit |
string | Eenheid, bijvoorbeeld stuk of uur. kan leeg zijn (null) |
items.unit_price |
string | Prijs per eenheid, zonder btw. |
items.unit_price_incl |
string | Prijs per eenheid inclusief btw, zoals ingevoerd. Is hij gevuld, dan rekent de regel vanaf dit bedrag (unit_price en line_total zijn daarvan afgeleid) en klopt het totaal tot op de cent met de ingevoerde prijs. null = de regel rekent vanaf unit_price. kan leeg zijn (null) |
items.discount_percentage |
string | Korting op deze regel, in procenten. |
items.discount_amount |
string | Korting op deze regel, als bedrag. |
items.discount_description |
string | Waarom de korting op deze regel is gegeven. kan leeg zijn (null) |
items.line_total |
string | Regeltotaal zonder btw, na korting. |
items.line_total_incl |
string | Regeltotaal inclusief btw, na korting. Alleen gevuld als de regel vanaf unit_price_incl rekent; anders null. kan leeg zijn (null) |
items.is_taxable |
boolean | Telt deze regel mee voor de btw. |
items.tax_rate |
string | Btw-tarief van deze regel, in procenten. |
created_at |
string (date-time) | Wanneer de offerte is aangemaakt. |
updated_at |
string (date-time) | Wanneer de offerte voor het laatst is gewijzigd. |