Capturia mesure ses publicités et la fréquentation du site avec le pixel Meta et Google Analytics. Tu peux refuser en tout temps. En savoir plus

EN

Quotes

4 endpoints

get/v1/quotes

Lister 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)
Scopes requis: quotes:read

Paramètres de requête

NomTypeObligatoireDescription
statusstringoptionnelFiltre exact sur le cycle de vie du devis.

Responses

StatutDescription
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 code : insufficient_scope (la clé ne possède pas le scope requis — details.required_scope) ou feature_not_available (l'API publique ou le module visé n'est pas inclus dans le forfait du compte — details.feature nomme la fonctionnalité, ex. public_api, quotes). Un refus de forfait est définitif pour la clé : inutile de réessayer, le compte doit changer de forfait.

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 request_id au support.

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'
post/v1/quotes

Cré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.

Scopes requis: quotes:write

Corps de requête

PropriétéTypeObligatoireDescription
contact_idstring | nulloptionnelFiche 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_namestring | nulloptionnel—
prospect_emailstring | nulloptionnel—
prospect_phonestring | nulloptionnel—
prospect_companystring | nulloptionnel—
prospect_addressstring | nulloptionnelAdresse postale libre.
languagestringoptionnelLangue du devis (page publique, PDF, courriel d'envoi). Défaut fr.
valid_untilstring | nulloptionnelDate 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_termsstring | nulloptionnelModalités de paiement (texte libre). Absentes : celles du compte. null : aucune.
line_itemsobject[]optionnelLignes du devis, écrites dans la même opération ; les montants sont calculés par le serveur.
internal_notesstringoptionnelNotes internes non visibles par le prospect.
sales_rep_idstring | nulloptionnelVendeur assigné ; null ou omis = aucun.
tax_profile_idstring | nulloptionnelProfil de taxes appliqué (TPS/TVQ) ; null ou omis = profil par défaut du compte.
signature_payment_orderstringoptionnelOrdre 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_reasonstring | nulloptionnelRaison écrite d'une vente hors taxes (voir Quote.tax_exempt_reason).

Responses

StatutDescription
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 code : insufficient_scope (la clé ne possède pas le scope requis — details.required_scope) ou feature_not_available (l'API publique ou le module visé n'est pas inclus dans le forfait du compte — details.feature nomme la fonctionnalité, ex. public_api, quotes). Un refus de forfait est définitif pour la clé : inutile de réessayer, le compte doit changer de forfait.

409

Une requête précédente avec la même Idempotency-Key a utilisé un payload différent, ou est encore en cours de traitement.

415

Le header Content-Type est manquant ou non supporté pour cet endpoint.

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 request_id au support.

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
    }
  ]
}'
patch/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.

Scopes requis: quotes:write

Corps de requête

PropriétéTypeObligatoireDescription
prospect_namestring | nulloptionnel—
prospect_emailstring | nulloptionnel—
prospect_phonestring | nulloptionnel—
prospect_companystring | nulloptionnel—
prospect_addressstring | nulloptionnel—
valid_untilstring | nulloptionnel—
payment_termsstring | nulloptionnel—
internal_notesstring | nulloptionnel—
sales_rep_idstring | nulloptionnel—
tax_profile_idstring | nulloptionnel—
payment_plan_typestringoptionnelStructure 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_typestring | nulloptionnel—
payment_deposit_valuenumber | nulloptionnel—
payment_installments_countinteger | nulloptionnel—
payment_interval_daysinteger | nulloptionnel—
payment_execution_modestringoptionnelautomatic = 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_orderstringoptionnelOrdre 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_reasonstring | nulloptionnelRaison é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.
statusstringoptionnelClô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

StatutDescription
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 code : insufficient_scope (la clé ne possède pas le scope requis — details.required_scope) ou feature_not_available (l'API publique ou le module visé n'est pas inclus dans le forfait du compte — details.feature nomme la fonctionnalité, ex. public_api, quotes). Un refus de forfait est définitif pour la clé : inutile de réessayer, le compte doit changer de forfait.

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 request_id au support.

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"
}'
post/v1/quotes/{id}/send

Envoyer 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.

Scopes requis: quotes:write

Corps de requête

Body optionnel — aucun champ utilisé aujourd'hui (réservé pour usage futur).

Responses

StatutDescription
200

Devis envoyé au prospect.

401

Clé API inconnue ou format invalide.

403

Refus d'autorisation. Deux causes, distinguées par code : insufficient_scope (la clé ne possède pas le scope requis — details.required_scope) ou feature_not_available (l'API publique ou le module visé n'est pas inclus dans le forfait du compte — details.feature nomme la fonctionnalité, ex. public_api, quotes). Un refus de forfait est définitif pour la clé : inutile de réessayer, le compte doit changer de forfait.

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 request_id au support.

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'