Quotes
4 endpoints
/v1/quotesLister les devis
Retourne la liste paginée des devis du client (cursor v2). Tri stable
created_at DESC, id DESC.
Les devis archivés (archived_at IS NOT NULL) sont exclus par
défaut — l'API publique ne les expose pas. Pour récupérer un devis
archivé, utiliser le dashboard.
Filtres
?status— filtre exact (draft,sent,signed,cancelled)
quotes:readParamètres de requête
| Nom | Type | Obligatoire | Description |
|---|---|---|---|
status | string | optionnel | Filtre exact sur le cycle de vie du devis. |
Responses
| Statut | Description |
|---|---|
200 | Liste paginée des devis. |
400 | La requête est mal formée (header manquant, JSON invalide, paramètre invalide). |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
Exemple de requête
curl -X GET 'https://api.capturia.io/v1/quotes' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'/v1/quotesCréer un devis
Crée un devis en brouillon (status="draft") par le même point de
création que le dashboard : Capturia attribue le numéro
(quote_number, séquence du compte, jamais de doublon) et pose les
défauts du compte (validité, conditions de paiement, profil de taxes,
politique de dépôt). Un quote_number fourni est refusé
(400 validation_error, raison read_only).
contact_id rattache le devis à sa fiche (/v1/contacts) : chaque
champ prospect_* absent est lu de la fiche, chaque champ fourni
prime. line_items écrit les lignes dans la même opération ; les
montants sont calculés par le serveur. Pour envoyer le devis au
prospect, utiliser POST /v1/quotes/{id}/send.
Idempotency
Le header Idempotency-Key est optionnel mais recommandé : une
reprise après un délai dépassé ne crée jamais un second devis. Une
2e requête avec la même clé et le même payload renvoie la réponse
d'origine ; la même clé avec un payload différent retourne
409 idempotency_conflict. Un refus (400, 403, 404) n'est pas
mémorisé : la clé reste utilisable.
quotes:writeCorps de requête
| Propriété | Type | Obligatoire | Description |
|---|---|---|---|
contact_id | string | null | optionnel | Fiche du compte (/v1/contacts) à laquelle rattacher le devis. Chaque champ
prospect_* absent est lu de la fiche ; chaque champ fourni prime. Une fiche
d'un autre compte → 400 validation_error (raison not_found_in_tenant). |
prospect_name | string | null | optionnel | — |
prospect_email | string | null | optionnel | — |
prospect_phone | string | null | optionnel | — |
prospect_company | string | null | optionnel | — |
prospect_address | string | null | optionnel | Adresse postale libre. |
language | string | optionnel | Langue du devis (page publique, PDF, courriel d'envoi). Défaut fr. |
valid_until | string | null | optionnel | Date limite de validité (YYYY-MM-DD). Absente : validité par défaut du compte
(en jours, à partir d'aujourd'hui, heure de Montréal). null : aucune date. |
payment_terms | string | null | optionnel | Modalités de paiement (texte libre). Absentes : celles du compte. null : aucune. |
line_items | object[] | optionnel | Lignes du devis, écrites dans la même opération ; les montants sont calculés par le serveur. |
internal_notes | string | optionnel | Notes internes non visibles par le prospect. |
sales_rep_id | string | null | optionnel | Vendeur assigné ; null ou omis = aucun. |
tax_profile_id | string | null | optionnel | Profil de taxes appliqué (TPS/TVQ) ; null ou omis = profil par défaut du compte. |
signature_payment_order | string | optionnel | Ordre signature ↔ paiement de la page publique. Absent → réglage par défaut du
compte (instantané posé à la création). Valeur hors liste → 400 validation_error. |
tax_exempt_reason | string | null | optionnel | Raison écrite d'une vente hors taxes (voir Quote.tax_exempt_reason). |
Responses
| Statut | Description |
|---|---|
201 | Devis créé. |
400 | Validation des paramètres ou du body a échoué. |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
409 | Une requête précédente avec la même |
415 | Le header |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
Exemple de requête
curl -X POST 'https://api.capturia.io/v1/quotes' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"contact_id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a",
"valid_until": "2026-06-30",
"payment_terms": "Net 30",
"line_items": [
{
"description": "Remplacement de la toiture",
"quantity": 1,
"unit_price": 9500
}
]
}'/v1/quotes/{id}Mettre à jour un devis
Met à jour les champs fournis du devis (champs absents conservés).
Au moins un champ doit être fourni sinon 400 no_fields.
Les montants (subtotal, tps_amount, tvq_amount, total) ne
sont pas éditables via PATCH — calculés serveur-side à partir des
blocks (gérés via le dashboard).
status n'accepte que declined (le prospect a refusé) et
cancelled : un devis passe à sent par POST /v1/quotes/{id}/send
(le courriel part), à viewed et signed par le prospect, à
expired par sa date de validité. Un devis signé, payé ou déjà clos
ne se clôt plus (409 status_locked). La clôture se fait seule :
status combiné à d'autres champs retourne 400 validation_error
(raison exclusive_field).
Les devis archivés (archived_at IS NOT NULL) ne sont pas
modifiables — retourne 404 resource_not_found.
quotes:writeCorps de requête
| Propriété | Type | Obligatoire | Description |
|---|---|---|---|
prospect_name | string | null | optionnel | — |
prospect_email | string | null | optionnel | — |
prospect_phone | string | null | optionnel | — |
prospect_company | string | null | optionnel | — |
prospect_address | string | null | optionnel | — |
valid_until | string | null | optionnel | — |
payment_terms | string | null | optionnel | — |
internal_notes | string | null | optionnel | — |
sales_rep_id | string | null | optionnel | — |
tax_profile_id | string | null | optionnel | — |
payment_plan_type | string | optionnel | Structure du plan de paiement. Les montants de chaque échéance sont dérivés
serveur. Le plan se fige au premier paiement encaissé (409 payment_plan_locked).
La cohérence entre champs (dépôt sans valeur, nombre de versements hors bornes)
est validée serveur → 400 validation_error. |
payment_deposit_type | string | null | optionnel | — |
payment_deposit_value | number | null | optionnel | — |
payment_installments_count | integer | null | optionnel | — |
payment_interval_days | integer | null | optionnel | — |
payment_execution_mode | string | optionnel | automatic = prélèvement carte hors session des échéances ; manual = paiements
reçus (virement, Interac) enregistrés par le vendeur, rien n'est prélevé. Valeur
hors liste → 400 validation_error. |
signature_payment_order | string | optionnel | Ordre signature ↔ paiement de la page publique. Modifiable tant que le devis est
en brouillon ; une fois envoyé → 409 signature_payment_order_locked. Valeur
hors liste → 400 validation_error. |
tax_exempt_reason | string | null | optionnel | Raison écrite d'une vente hors taxes ; null remet le devis taxé. Une
chaîne vide ou de plus de 200 caractères → 400 validation_error. Les
totaux sont recalculés serveur au changement. |
status | string | optionnel | Clôture du devis : declined (le prospect a refusé) ou cancelled. Les autres
statuts ne se posent jamais par PATCH : sent vient de
POST /v1/quotes/{id}/send (le courriel part), viewed et signed du
prospect, expired de la date de validité. Valeur hors liste → 400
validation_error. Un devis signé ou payé ne se clôt plus → 409
status_locked (rien n'est écrit). |
Responses
| Statut | Description |
|---|---|
200 | Devis mis à jour. |
400 | Validation des paramètres ou du body a échoué. |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
404 | La ressource demandée n'existe pas (ou pas dans le scope du tenant). |
409 | La requête est syntaxiquement valide mais viole une règle métier. |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
Exemple de requête
curl -X PATCH 'https://api.capturia.io/v1/quotes/{id}' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json' \
-d '{
"prospect_name": "Alexandre Tremblay",
"valid_until": "2026-07-31",
"payment_terms": "Net 60"
}'/v1/quotes/{id}/sendEnvoyer un devis au prospect
Envoie le devis par courriel au prospect, par le même service que le
bouton « Envoyer » du dashboard : expéditeur du vendeur assigné (sinon
de l'entreprise), lien vers la page publique du devis, puis
status="sent" et sent_at. Seul un brouillon s'envoie ; rien ne
repart d'un devis déjà envoyé.
Un devis sans fiche de contact (prospect saisi sans fiche) est d'abord relié à la fiche du prospect : retrouvée par son courriel ou son numéro, sinon créée — jamais un doublon. Le courriel ouvre alors son fil dans l'Inbox. La fiche créée reste dans le compte de la clé, et sa création déclenche les automatisations et la synchronisation CRM d'une nouvelle fiche.
Refus
409 already_signed: le devis est signé.409 quote_cancelled: le devis a été annulé.409 already_sent: le devis a déjà été envoyé.409 quote_status_changed: le devis n'est plus un brouillon (par exemple expiré).409 send_in_progress: un envoi de ce devis est déjà en cours.409 send_outcome_unknown: un envoi précédent n'a pas pu être confirmé ; il n'est pas refait.409 recipient_unsubscribed: le prospect s'est désabonné.409 sender_unavailable: le vendeur assigné n'a pas d'adresse d'envoi valide.422 no_prospect_email/422 invalid_prospect_email: courriel du prospect absent ou invalide.422 unpriced_lines,422 untaxed_quote,422 deposit_not_payable: garde de contenu.
Aucun body n'est requis.
quotes:writeCorps de requête
Body optionnel — aucun champ utilisé aujourd'hui (réservé pour usage futur).
Responses
| Statut | Description |
|---|---|
200 | Devis envoyé au prospect. |
401 | Clé API inconnue ou format invalide. |
403 | Refus d'autorisation. Deux causes, distinguées par |
404 | La ressource demandée n'existe pas (ou pas dans le scope du tenant). |
409 | La requête est syntaxiquement valide mais viole une règle métier. |
422 | La requête est syntaxiquement valide mais viole une règle métier. |
429 | Le quota de requêtes par fenêtre est dépassé pour cette clé API ou cet endpoint. |
500 | Erreur serveur inattendue. Toujours fournir le |
Exemple de requête
curl -X POST 'https://api.capturia.io/v1/quotes/{id}/send' \
-H 'Authorization: Bearer cap_live_<your-key>' \
-H 'Content-Type: application/json'