Facturino / Documentation / Devis

Devis

L'objet quote représente une proposition commerciale envoyée au client avant émission de la facture définitive. À la différence des factures, les devis n'ont pas de numérotation opposable et peuvent être modifiés tant qu'ils ne sont pas acceptés.

Endpoints

MéthodeCheminDescription
POST/v1/quotesCréer un devis
GET/v1/quotesLister les devis
GET/v1/quotes/:idRécupérer un devis
PATCH/v1/quotes/:idModifier (drafts uniquement)
DELETE/v1/quotes/:idSupprimer un brouillon
POST/v1/quotes/:id/sendPasser en sent (attribue le numéro, planifie le rappel J-7)
POST/v1/quotes/:id/emailRenvoyer par email
POST/v1/quotes/:id/acceptMarquer comme accepté
POST/v1/quotes/:id/refuseMarquer comme refusé
POST/v1/quotes/:id/convertConvertir en brouillon commercial (TVA non décidée)
POST/v1/quotes/:id/cloneDupliquer en brouillon
GET/v1/quotes/:id/pdfTélécharger le PDF

Créer un devis

$ curl -X POST https://facturino.com/api/v1/quotes \
  -H "Authorization: Bearer fac_test_..." \
  -H "Idempotency-Key: quote-create-opportunity-1842" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "cus_8f2k4m9n",
    "lines": [
      {
        "description": "Refonte du site corporate",
        "quantity": "1", "unit": "flat_rate",
        "unitPrice": 1250000, "vatRate": 2000, "vatCode": "S"
      }
    ],
    "dates": { "issued": "2026-04-22", "validUntil": "2026-05-22" },
    "notes": "Devis valable 30 jours, signature électronique acceptée."
  }'

La structure des lignes (lines), des totaux (totals) et des montants suit exactement la même convention que les factures — des centimes entiers aussi bien en requête qu'en réponse (symétrie entrée/sortie).

La fenêtre de validité est obligatoire et se fournit de deux façons exclusives : soit une date absolue dates.validUntil, soit validityDays (entier 1–365) — un nombre de jours à partir de la date d'émission. Fournir l'un ou l'autre (mais pas les deux). Si dates.issued est omis, il prend par défaut la date du jour.

Cycle de vie

StatutDescriptionTransitions
draftBrouillon éditablesent, suppression
sentEnvoyé au clientviewed, accepted, refused, expired
viewedConsulté par le clientaccepted, refused, expired
acceptedAccepté par le clientconverted
refusedRefusé par le clientdraft (correction)
expiredDate de validité dépasséesent (renvoi)
convertedConverti en facture(terminal)

Le suivi viewed repose sur le lien public envoyé par email — il s'active dès la première consultation, idempotent ensuite. Un rappel automatique est envoyé avant la date de validité (J-7 avant dates.validUntil), tant que le devis reste en attente (sent ou viewed).

Envoyer au client

$ curl -X POST https://facturino.com/api/v1/quotes/quo_a740665b/email \
  -H "Authorization: Bearer fac_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "recipientEmail": "marie@durand.fr",
    "customMessage": "Bonjour Marie, voici notre proposition comme convenu."
  }'

L'envoi joint le PDF en pièce attachée et inclut un lien public unique (token opaque, sans compte) permettant au client de consulter le devis, puis de l'accepter ou le refuser en ligne — l'action déclenche la transition de statut (accepted / refused) et l'événement webhook correspondant. Si le PDF n'a pas encore été généré (création récente), la réponse retourne 202 Accepted avec un identifiant de job (id) à poller — relancez l'envoi quand le PDF est prêt.

Convertir, décider, adosser, finaliser

Le cycle du devis se déroule sur un seul document : convertirdécideradosserfinaliser. Ne créez jamais une seconde facture : cela abandonnerait le brouillon converti et romprait la filiation devis → facture.

1. Convertir

Quand le client accepte, la conversion produit un brouillon commercial : il énonce l'opération du devis et aucune conclusion fiscale. La TVA du devis était indicative ; le brouillon porte donc taxSource: null, un items vide et des totals à zéro — parce que rien n'est décidé, jamais parce que la TVA serait nulle. L'opération se lit dans commercialDraft.

$ curl -X POST https://facturino.com/api/v1/quotes/quo_a740665b/convert \
  -H "Authorization: Bearer fac_test_..."

# Réponse — brouillon COMMERCIAL créé à partir du devis
# (la facture seule est retournée, pas le couple { quote, invoice })
# taxSource null : l'opération est énoncée, aucune TVA n'est encore décidée.
{
  "id": "inv_new_draft",
  "object": "invoice",
  "status": "draft",
  "taxSource": null,
  "items": [],
  "commercialDraft": {
    "priceMode": "tax_exclusive",
    "totalCents": 80000,
    "lines": [{ "reference": "li_a1", "unitPrice": 80000, "quantity": "1", ... }]
  },
  "sourceQuoteId": "quo_a740665b"
}

2. Décider

Prenez une décision fiscale sur exactement l'opération que le brouillon énonce : reprenez ses commercialDraft.lines — mêmes reference, mêmes montants — et donnez à effectiveAt la date d'émission du brouillon. Les références de ligne sont attribuées côté serveur à la conversion : relisez-les, ne les inventez pas. Voir Décisions fiscales.

3. Adosser la décision au brouillon

POST /v1/invoices/:id/bind-tax-decision fige la décision sur ce même brouillon. L'appel est idempotent sur la décision : le rejouer renvoie la même facture. Adosser une autre décision à une facture déjà adossée, ou la même décision à une seconde facture, répond 409.

$ curl -X POST https://facturino.com/api/v1/invoices/inv_new_draft/bind-tax-decision \
  -H "Authorization: Bearer fac_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "taxDecisionId": "taxdec_9c1f",
    "decisionLines": [{ "taxLineRef": "li_a1", "unit": "hour" }]
  }'

# Réponse — LE MÊME brouillon, désormais adossé à sa décision.
# Il reste en statut draft : la finalisation est un appel distinct.
{
  "id": "inv_new_draft",
  "status": "draft",
  "taxSource": "facturino",
  "taxDecisionId": "taxdec_9c1f",
  "sourceQuoteId": "quo_a740665b"
}

4. Finaliser

Adosser fige la TVA ; POST /v1/invoices/:id/finalize émet la facture — numéro, mentions légales, cycle de dépôt. La facture conserve sourceQuoteId=quo_... ; retrouvez les factures issues d'un devis via GET /v1/invoices?convertedFrom=quo_....

Acceptation explicite ou implicite ? L'endpoint convert exige que le devis soit en statut accepted. Pour passer en accepted sans interaction client (cas d'usage interne, importation de données existantes), appelez POST /v1/quotes/:id/accept (sans corps de requête) — la réponse est { id, object: "quote", status: "accepted" } et l'événement quote.accepted est émis.

Lister et filtrer

Filtres supportés :

ParamètreDescription
statusFiltrer par statut (draft, sent, accepted…)
date_from / date_toPlage de dates ISO (sur created par défaut, ou updated selon sort)
sortcreated (défaut) ou updated
limit, starting_afterPagination cursor-based (défaut 25, max 100)

Conformité

Les devis ne sont pas soumis à la facturation électronique obligatoire (article 289 bis du CGI vise uniquement les factures). Ils restent toutefois soumis à l'article L.441-9 du Code de commerce sur les mentions obligatoires entre professionnels :

  • Identité et SIRET du vendeur
  • Désignation des prestations / produits
  • Quantités et prix unitaires HT
  • Date d'émission et durée de validité
  • Modalités de paiement (acompte, échelonnement)
  • Conditions générales de vente (annexées ou référencées)

Le générateur PDF Facturino applique automatiquement ces mentions à partir des données de l'entreprise (paramétrables depuis Paramètres → Facturation).

Étapes suivantes