Klantly Developers

Référence de l'API

Pièces jointes

Fichiers liés à un devis et photos liées à un bon de travail : consulter, téléverser et supprimer. Le téléchargement passe par un lien signé, valable un quart d'heure et sans clé API.

Endpoints

Consulter les pièces jointes

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

Toutes les pièces jointes de ce devis, les plus anciennes d’abord, chacune avec un lien de téléchargement signé.

Scope
quotes.read — Lire les devis, avec leurs lignes, leurs montants et les coordonnées du client
Fonctionnalité requise
quotes

Paramètres de chemin

NomTypeDescription
quote obligatoire string (uuid) L'id (UUID) du devis.

Exemple de requête

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"]

Réponse 200

La réponse est une liste avec pagination par curseur : data contient les objets, meta la pagination.

Exemple de réponse
{
  "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
  }
}

Erreurs possibles

En outre, chaque endpoint peut renvoyer les erreurs générales, comme une clé invalide ou une limite atteinte. Voir tous les codes d'erreur.

Téléverser une pièce jointe

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

Ajoute un fichier au devis. Envoyez-le en multipart/form-data dans le champ file — avec la photo de bon de travail, c'est le seul endpoint qui n'attend pas du JSON. Sont autorisés pdf, jpg, jpeg, png, gif, webp, doc, docx, xls et xlsx, jusqu'à 10 Mo par fichier et 20 pièces jointes par devis ; au-delà, vous recevez 403 limit_reached. Le type de fichier est vérifié d'après le contenu, pas d'après le nom.

Scope
quotes.write — Créer et modifier des devis (brouillons uniquement), et les accepter ou les refuser au nom du client
Fonctionnalité requise
quotes

Paramètres de chemin

NomTypeDescription
quote obligatoire string (uuid) L'id (UUID) du devis.

Corps (JSON)

ChampTypeDescription
file obligatoire string (binary) Le fichier lui-même, en multipart/form-data. 10 Mo maximum ; sont autorisés pdf, jpg, jpeg, png, gif, webp, doc, docx, xls et xlsx.
description facultatif string Votre description de la pièce jointe. peut être vide (null) · au maximum 255 caractères
visible_to_customer facultatif boolean True si le client reçoit la pièce jointe avec le devis.

Exemple de requête

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"]

Réponse 201

Exemple de réponse
{
  "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"
  }
}

Erreurs possibles

En outre, chaque endpoint peut renvoyer les erreurs générales, comme une clé invalide ou une limite atteinte. Voir tous les codes d'erreur.

Supprimer une pièce jointe

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

Supprime la pièce jointe et le fichier lui-même. Cette action est irréversible.

Scope
quotes.write — Créer et modifier des devis (brouillons uniquement), et les accepter ou les refuser au nom du client
Fonctionnalité requise
quotes

Paramètres de chemin

NomTypeDescription
quote obligatoire string (uuid) L'id (UUID) du devis.
attachment obligatoire string (uuid) L’id (UUID) de la pièce jointe.

Exemple de requête

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"]

Réponse 200

Exemple de réponse
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Erreurs possibles

En outre, chaque endpoint peut renvoyer les erreurs générales, comme une clé invalide ou une limite atteinte. Voir tous les codes d'erreur.

Consulter les photos du bon de travail

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

Toutes les photos de ce bon de travail, dans l’ordre où elles y figurent, chacune avec un lien de téléchargement signé.

Scope
work_orders.read — Lire les bons d'intervention, avec le nom, l'adresse et les coordonnées du client
Fonctionnalité requise
work_orders

Paramètres de chemin

NomTypeDescription
work_order obligatoire string (uuid) L’id (UUID) du bon d’intervention.

Exemple de requête

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"]

Réponse 200

La réponse est une liste avec pagination par curseur : data contient les objets, meta la pagination.

Exemple de réponse
{
  "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
  }
}

Erreurs possibles

En outre, chaque endpoint peut renvoyer les erreurs générales, comme une clé invalide ou une limite atteinte. Voir tous les codes d'erreur.

Téléverser une photo de bon de travail

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

Ajoute une photo au bon de travail ; elle figure aussi dans le PDF. Envoyez-la en multipart/form-data dans le champ file. Images uniquement (jpg, jpeg, png, gif, webp), jusqu'à 10 Mo par photo et 30 photos par bon de travail. Avec kind, indiquez si la photo date d'avant ou d'après l'intervention.

Scope
work_orders.write — Créer et modifier des bons d'intervention, et changer leur statut (terminer peut envoyer une demande d'avis)
Fonctionnalité requise
work_orders

Paramètres de chemin

NomTypeDescription
work_order obligatoire string (uuid) L’id (UUID) du bon d’intervention.

Corps (JSON)

ChampTypeDescription
file obligatoire string (binary) La photo elle-même, en multipart/form-data. 10 Mo maximum ; sont autorisés jpg, jpeg, png, gif et webp.
kind facultatif string before (avant l'intervention), after (après) ou other. l'une des valeurs : before, after, other
caption facultatif string Légende de la photo. peut être vide (null) · au maximum 255 caractères

Exemple de requête

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"]

Réponse 201

Exemple de réponse
{
  "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"
  }
}

Erreurs possibles

En outre, chaque endpoint peut renvoyer les erreurs générales, comme une clé invalide ou une limite atteinte. Voir tous les codes d'erreur.

Supprimer une photo de bon de travail

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

Supprime la photo et le fichier lui-même. Cette action est irréversible.

Scope
work_orders.write — Créer et modifier des bons d'intervention, et changer leur statut (terminer peut envoyer une demande d'avis)
Fonctionnalité requise
work_orders

Paramètres de chemin

NomTypeDescription
work_order obligatoire string (uuid) L’id (UUID) du bon d’intervention.
photo obligatoire string (uuid) L’id (UUID) de la photo.

Exemple de requête

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"]

Réponse 200

Exemple de réponse
{
  "data": {
    "object": "note",
    "id": "9d3f7b41-2d6f-4e8c-9b3a-4f5d6e7a8b92",
    "deleted": true
  }
}

Erreurs possibles

En outre, chaque endpoint peut renvoyer les erreurs générales, comme une clé invalide ou une limite atteinte. Voir tous les codes d'erreur.

L'objet

Tous les champs sont toujours présents ; un champ sans valeur vaut null.

ChampTypeDescription
object string Toujours "attachment".
id string (uuid) Id de la pièce jointe.
quote_id string (uuid) Le devis auquel la pièce jointe appartient.
name string Le nom du fichier tel qu’il a été téléversé. peut être vide (null)
mime_type string Le type de fichier, par exemple application/pdf. peut être vide (null)
size integer La taille en octets. peut être vide (null)
description string Votre description de la pièce jointe. peut être vide (null)
visible_to_customer boolean True si le client reçoit la pièce jointe avec le devis.
download_url string (uri) Un lien signé vers le fichier, valable un quart d'heure et ouvrable sans clé API. Redemandez-le une fois expiré.
created_at string (date-time) Quand la pièce jointe a été ajoutée (UTC).