Klantly Developers

API-Referenz

Anhänge

Dateien zu einem Angebot und Fotos zu einem Arbeitsauftrag: abrufen, hochladen und löschen. Der Download läuft über einen signierten Link, der fünfzehn Minuten gültig ist und keinen API-Schlüssel braucht.

Endpunkte

Anhänge abrufen

GET /api/v1/quotes/{quote}/attachments

Alle Anhänge dieses Angebots, älteste zuerst, jeweils mit einem signierten Download-Link.

Scope
quotes.read — Angebote lesen, mit Positionen, Beträgen und den Kundendaten darauf
Erforderliche Funktion
quotes

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Beispielanfrage

cURL
curl "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments" \
  -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', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments');

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments', {
  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/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
)
data = response.json()["data"]

Antwort 200

Die Antwort ist eine Liste mit Cursor-Paginierung: data enthält die Objekte, meta die Paginierung.

Beispielantwort
{
  "data": [
    {
      "object": "attachment",
      "id": "9d3f9e56-af10-4123-b345-e6f7a8b9c0d5",
      "quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
      "name": "technische-tekening.pdf",
      "mime_type": "application/pdf",
      "size": 248000,
      "description": "Tekening van de dakopbouw",
      "visible_to_customer": true,
      "download_url": "https://app.klantly.com/api/files/quote-attachment/9d3f9e56-af10-4123-b345-e6f7a8b9c0d5?expires=1789000000&signature=8f1c…",
      "created_at": "2026-09-14T10:15:00Z"
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
    "prev_cursor": null
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Anhang hochladen

POST /api/v1/quotes/{quote}/attachments

Fügt dem Angebot eine Datei hinzu. Senden Sie sie als multipart/form-data im Feld file — zusammen mit dem Arbeitsauftrag-Foto ist das der einzige Endpunkt, der kein JSON erwartet. Erlaubt sind pdf, jpg, jpeg, png, gif, webp, doc, docx, xls und xlsx, bis 10 MB pro Datei und 20 Anhänge pro Angebot; darüber folgt 403 limit_reached. Der Dateityp wird am Inhalt geprüft, nicht am Namen.

Scope
quotes.write — Angebote erstellen und bearbeiten (nur Entwürfe) sowie im Namen des Kunden annehmen oder ablehnen
Erforderliche Funktion
quotes

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.

Body (JSON)

FeldTypBeschreibung
file erforderlich string (binary) Die Datei selbst, als multipart/form-data. Bis 10 MB; erlaubt sind pdf, jpg, jpeg, png, gif, webp, doc, docx, xls und xlsx.
description optional string Eigene Beschreibung des Anhangs. kann leer sein (null) · höchstens 255 Zeichen
visible_to_customer optional boolean True, wenn der Kunde den Anhang mit dem Angebot erhält.

Beispielanfrage

cURL
curl -X POST "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments" \
  -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('POST', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments');

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

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

import requests

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

Antwort 201

Beispielantwort
{
  "data": {
    "object": "attachment",
    "id": "9d3f9e56-af10-4123-b345-e6f7a8b9c0d5",
    "quote_id": "9d3f8449-afe1-4a6e-9dbc-c1dfe0f1a2ba",
    "name": "technische-tekening.pdf",
    "mime_type": "application/pdf",
    "size": 248000,
    "description": "Tekening van de dakopbouw",
    "visible_to_customer": true,
    "download_url": "https://app.klantly.com/api/files/quote-attachment/9d3f9e56-af10-4123-b345-e6f7a8b9c0d5?expires=1789000000&signature=8f1c…",
    "created_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Anhang löschen

DELETE /api/v1/quotes/{quote}/attachments/{attachment}

Löscht den Anhang und die Datei selbst. Das lässt sich nicht rückgängig machen.

Scope
quotes.write — Angebote erstellen und bearbeiten (nur Entwürfe) sowie im Namen des Kunden annehmen oder ablehnen
Erforderliche Funktion
quotes

Pfadparameter

NameTypBeschreibung
quote erforderlich string (uuid) Die id (UUID) des Angebots.
attachment erforderlich string (uuid) Die id (UUID) des Anhangs.

Beispielanfrage

cURL
curl -X DELETE "https://app.klantly.com/api/v1/quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments/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('DELETE', 'quotes/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/attachments/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');

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

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

import requests

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

Antwort 200

Beispielantwort
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Fotos des Arbeitsauftrags abrufen

GET /api/v1/work-orders/{work_order}/photos

Alle Fotos dieses Arbeitsauftrags in der Reihenfolge, in der sie darauf stehen, jeweils mit einem signierten Download-Link.

Scope
work_orders.read — Arbeitsaufträge lesen, mit Name, Adresse und Kontaktdaten des Kunden
Erforderliche Funktion
work_orders

Pfadparameter

NameTypBeschreibung
work_order erforderlich string (uuid) Die ID (UUID) des Arbeitsauftrags.

Beispielanfrage

cURL
curl "https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos" \
  -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', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos');

$data = json_decode((string) $response->getBody(), true)['data'];
JavaScript
const response = await fetch('https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos', {
  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/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos",
    headers={
        "Authorization": f"Bearer {os.environ['KLANTLY_API_KEY']}",
    },
)
data = response.json()["data"]

Antwort 200

Die Antwort ist eine Liste mit Cursor-Paginierung: data enthält die Objekte, meta die Paginierung.

Beispielantwort
{
  "data": [
    {
      "object": "work_order_photo",
      "id": "9d3f9f67-b021-4234-c456-f7a8b9c0d1e6",
      "work_order_id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
      "kind": "after",
      "caption": "Dakgoot na montage",
      "download_url": "https://app.klantly.com/api/files/quote-attachment/9d3f9e56-af10-4123-b345-e6f7a8b9c0d5?expires=1789000000&signature=8f1c…",
      "created_at": "2026-09-14T10:15:00Z"
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": "eyJpZCI6IjlkM2Y2YzFlIn0",
    "prev_cursor": null
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Foto zum Arbeitsauftrag hochladen

POST /api/v1/work-orders/{work_order}/photos

Fügt dem Arbeitsauftrag ein Foto hinzu; es erscheint auch im PDF. Senden Sie es als multipart/form-data im Feld file. Nur Bilder (jpg, jpeg, png, gif, webp), bis 10 MB pro Foto und 30 Fotos pro Arbeitsauftrag. Mit kind geben Sie an, ob das Foto von vor oder nach der Arbeit ist.

Scope
work_orders.write — Arbeitsaufträge erstellen und bearbeiten sowie den Status ändern (Abschließen kann eine Bewertungsanfrage senden)
Erforderliche Funktion
work_orders

Pfadparameter

NameTypBeschreibung
work_order erforderlich string (uuid) Die ID (UUID) des Arbeitsauftrags.

Body (JSON)

FeldTypBeschreibung
file erforderlich string (binary) Das Foto selbst, als multipart/form-data. Bis 10 MB; erlaubt sind jpg, jpeg, png, gif und webp.
kind optional string before (vor der Arbeit), after (danach) oder other. einer von: before, after, other
caption optional string Bildunterschrift zum Foto. kann leer sein (null) · höchstens 255 Zeichen

Beispielanfrage

cURL
curl -X POST "https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos" \
  -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('POST', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos');

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

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

import requests

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

Antwort 201

Beispielantwort
{
  "data": {
    "object": "work_order_photo",
    "id": "9d3f9f67-b021-4234-c456-f7a8b9c0d1e6",
    "work_order_id": "9d3f8328-9ed0-4f5d-8cab-b0cedfe0f1a9",
    "kind": "after",
    "caption": "Dakgoot na montage",
    "download_url": "https://app.klantly.com/api/files/quote-attachment/9d3f9e56-af10-4123-b345-e6f7a8b9c0d5?expires=1789000000&signature=8f1c…",
    "created_at": "2026-09-14T10:15:00Z"
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Foto des Arbeitsauftrags löschen

DELETE /api/v1/work-orders/{work_order}/photos/{photo}

Löscht das Foto und die Datei selbst. Das lässt sich nicht rückgängig machen.

Scope
work_orders.write — Arbeitsaufträge erstellen und bearbeiten sowie den Status ändern (Abschließen kann eine Bewertungsanfrage senden)
Erforderliche Funktion
work_orders

Pfadparameter

NameTypBeschreibung
work_order erforderlich string (uuid) Die ID (UUID) des Arbeitsauftrags.
photo erforderlich string (uuid) Die id (UUID) des Fotos.

Beispielanfrage

cURL
curl -X DELETE "https://app.klantly.com/api/v1/work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos/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('DELETE', 'work-orders/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70/photos/9d3f6c1e-4b2a-4c8e-9f1a-2b3c4d5e6f70');

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

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

import requests

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

Antwort 200

Beispielantwort
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Mögliche Fehler

Zusätzlich kann jeder Endpunkt die allgemeinen Fehler zurückgeben, etwa einen ungültigen Schlüssel oder ein erreichtes Limit. Alle Fehlercodes ansehen.

Das Objekt

Alle Felder sind immer vorhanden; ein Feld ohne Wert ist null.

FeldTypBeschreibung
object string Immer "attachment".
id string (uuid) Id des Anhangs.
quote_id string (uuid) Das Angebot, zu dem der Anhang gehört.
name string Der Dateiname, wie er hochgeladen wurde. kann leer sein (null)
mime_type string Der Dateityp, etwa application/pdf. kann leer sein (null)
size integer Die Größe in Bytes. kann leer sein (null)
description string Eigene Beschreibung des Anhangs. kann leer sein (null)
visible_to_customer boolean True, wenn der Kunde den Anhang mit dem Angebot erhält.
download_url string (uri) Ein signierter Link zur Datei, fünfzehn Minuten gültig und ohne API-Schlüssel zu öffnen. Nach Ablauf einfach neu abrufen.
created_at string (date-time) Wann der Anhang hinzugefügt wurde (UTC).