# Documentation Tulip (/docs)
Documentation officielle de la plateforme d'assurance Tulip — API, guides et ressources pour les intégrateurs.
Tulip est une plateforme d'assurance qui permet aux partenaires de gérer des contrats de location et des sinistres via une API REST.
## Par où commencer ?
Obtenez votre clé API et créez votre premier contrat en quelques minutes.
Comprenez le cycle de vie des contrats, sinistres, documents et paiements.
Documentation complète de tous les endpoints REST.
Gestion des erreurs, B2B vs B2C, tests et migration.
## Fonctionnalités principales
| Domaine | Description |
|---------|-------------|
| **Contrats** | Créer, modifier et résilier des contrats de location (courte, moyenne, longue durée) |
| **Produits** | Catalogue de produits assurables : vélos, high-tech, sports, événementiel, outillage |
| **Sinistres** | Déclarer des sinistres (vol, casse), uploader des documents justificatifs, suivre le traitement |
| **Webhooks** | Recevoir des notifications en temps réel sur les événements de vos sinistres |
| **Règlements** | Suivre les remboursements et leur statut de paiement |
## Ressources complémentaires
- [Changelog](/docs/resources/changelog) — Historique des versions de l'API
- [SDKs](/docs/resources/sdks) — Clients générés pour TypeScript, PHP et Python
- [Support](/docs/resources/support) — Contact et escalade
---
# Référence API (/docs/api-reference)
Documentation complète des endpoints REST de l'API Tulip v2.
L'API Tulip est une API REST qui utilise JSON pour les requêtes et réponses. Tous les endpoints sont versionnés sous `/v2`.
**Spécification OpenAPI** — Téléchargez la spécification complète au format OpenAPI 3.x pour l'importer dans vos outils (Postman, Insomnia, génération de SDK…) : [openapi.json](/openapi.json)
## Authentification
Toutes les requêtes nécessitent une clé API dans le header `key`. Voir [Authentification](/docs/getting-started/authentication).
## URL de base
```
https://api.mytulip.io/v2
```
## Endpoints par domaine
### Contrats
Gérer les contrats de location (création, modification, résiliation).
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/contracts` | Lister les contrats |
| GET | `/contracts/{id}` | Récupérer un contrat |
| POST | `/contracts` | Créer un contrat |
| PATCH | `/contracts/{id}` | Mettre à jour un contrat |
| DELETE | `/contracts/{id}` | Résilier un contrat |
| POST | `/contracts/{id}/products` | Ajouter un produit |
| DELETE | `/contracts/{id}/products` | Supprimer un produit |
### Sinistres
Gérer les sinistres (déclaration, documents, soumission).
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| POST | `/claims` | Créer une pré-déclaration |
| GET | `/claims` | Lister les sinistres |
| GET | `/claims/{id}` | Récupérer un sinistre |
| PATCH | `/claims/{id}` | Mettre à jour un sinistre |
| DELETE | `/claims/{id}` | Annuler/abandonner |
| POST | `/claims/{id}/submit` | Soumettre un sinistre |
| PUT | `/claims/{id}/documents` | Uploader des documents |
| GET | `/claims/{id}/documents/{docId}` | Télécharger un document |
### Produits
Gérer le catalogue de produits assurables.
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/products` | Lister les produits |
| GET | `/products/{id}` | Récupérer un produit |
| POST | `/products` | Créer un produit |
| PATCH | `/products/{id}` | Mettre à jour un produit |
| DELETE | `/products/{id}` | Supprimer un produit |
### Loueurs
Gérer les loueurs associés à votre clé API.
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/renters` | Lister les loueurs |
| GET | `/renters/{id}` | Récupérer un loueur |
| POST | `/renters` | Ajouter un loueur |
### Géolocalisation
| Méthode | Endpoint | Description |
|---------|----------|-------------|
| GET | `/geo/getCitiesByZipCode/{zipcode}` | Villes par code postal |
| GET | `/geo/getCitiesSuggestions/{country}/{zipcode}` | Recherche de villes par suggestion |
## Conventions
Consultez les [conventions API](/docs/getting-started/conventions) pour les détails sur les formats de dates, la pagination et la structure des réponses.
## Codes d'erreur
Consultez le [guide des erreurs](/docs/guides/error-handling) pour la liste complète des codes d'erreur.
---
# Flux des sinistres (/docs/concepts/claims-workflow)
Les 8 statuts d'un sinistre, les transitions possibles, l'auto-soumission, le système de double identifiant, les catégories de documents, les types de règlement et les codes d'erreur.
## Diagramme des statuts
```
┌─────────┐ ┌───────────┐ ┌───────────────┐ ┌────────┐
│ draft │ ──► │ submitted │ ──► │ in_progress │ ──► │ closed │
└─────────┘ └───────────┘ └───────────────┘ └────────┘
│ ▲ │ │
│ (cancel) (docs fournis) │ │ └──────────┬──────────┐
▼ │ │ │ │
┌──────────┐ │ │(docs requis)│ │
│ archived │ ┌──┴───────────┐ ┌───────────┐ ┌──────────┐
└──────────┘ │ pending_info │ │ abandoned │ │ rejected │
└──────────────┘ └───────────┘ └──────────┘
```
## Statuts
| Statut | Description | Actions possibles |
|--------|-------------|-------------------|
| `draft` | Pré-déclaration créée, en attente de documents | Modifier, uploader docs, soumettre, annuler |
| `submitted` | Soumis, en attente de prise en charge | Lecture seule |
| `in_progress` | En cours de traitement par Tulip | Lecture seule |
| `pending_info` | Documents supplémentaires demandés | Uploader docs |
| `closed` | Traité et indemnisé | Lecture seule |
| `rejected` | Rejeté par l'assureur | Lecture seule |
| `abandoned` | Abandonné par l'assuré ou le partenaire | Lecture seule |
| `archived` | Pré-déclaration annulée (depuis draft uniquement) | Lecture seule |
## Mapping des statuts internes
L'API expose des statuts publics qui sont dérivés des statuts internes. Voici le mapping complet :
### Statut ExternalClaim (interne) vers statut API (public)
| Statut interne ExternalClaim | Statut API |
|------------------------------|------------|
| `unmatched` | `draft` |
| `pending` | `draft` |
| `matched` | Dépend du `userClaimStage` (voir ci-dessous) |
| `abandonned` | `archived` |
### userClaimStage (interne) vers statut API (public)
Lorsque le sinistre est `matched` (soumis et lié à un sinistre interne), le statut API dépend du champ `userClaimStage` :
| userClaimStage | Statut API |
|----------------|------------|
| `waiting_for_tulip` | `in_progress` |
| `document_to_send` | `pending_info` |
| `waiting_for_payment` | `in_progress` |
| `closed_paid` | `closed` |
| `closed_rejected` | `rejected` |
| `abandoned` | `abandoned` |
| `reopened` | `in_progress` |
| _(autre / absent)_ | `submitted` |
## Transitions
| De | Vers | Déclencheur |
|----|------|-------------|
| `draft` | `submitted` | Appel `POST /claims/{id}/submit` ou auto-soumission |
| `draft` | `archived` | Appel `DELETE /claims/{id}` |
| `submitted` | `in_progress` | Prise en charge par Tulip |
| `in_progress` | `pending_info` | Tulip demande des documents supplémentaires |
| `pending_info` | `in_progress` | Documents fournis |
| `in_progress` | `closed` | Sinistre traité et indemnisé |
| `in_progress` | `rejected` | Sinistre rejeté |
| `in_progress` | `abandoned` | Abandon par l'assuré/partenaire (`DELETE /claims/{id}`) |
## Double identifiant
Chaque sinistre possède deux identifiants :
| Champ | Disponible | Exemple |
|-------|-----------|---------|
| `id` | Dès la création | `claim_xyz789` |
| `claimId` | Après soumission | `internal_claim_abc` |
- `claimId` est `null` tant que le sinistre n'est pas soumis
- **Les deux identifiants sont acceptés** pour tous les endpoints GET
- Utilisez `id` si vous stockez la référence dès la création
## Auto-soumission
L'auto-soumission nécessite le query parameter `?autoSubmit=true` sur l'endpoint `PUT /claims/{id}/documents`. Sans ce paramètre, même lorsque tous les documents obligatoires sont uploadés, le sinistre reste en `draft` avec `canSubmit: true`.
Le fonctionnement est le suivant :
1. Le partenaire uploade un document via `PUT /claims/{id}/documents?autoSubmit=true`
2. Après l'upload, l'API vérifie si tous les documents `mandatory` sont présents
3. Si oui **et** que `autoSubmit=true` est passé, le sinistre est automatiquement soumis
4. La réponse contient alors `autoSubmitted: true` et le `claimId` du sinistre interne
### Webhook `v2.claim.draft.ready`
Lorsque tous les documents obligatoires sont uploadés **sans** le paramètre `autoSubmit=true`, l'API émet le webhook `v2.claim.draft.ready`. Cet événement permet au partenaire de savoir que le sinistre est prêt à être soumis manuellement via `POST /claims/{id}/submit`.
## Le champ `canSubmit`
Le champ booléen `canSubmit` indique si le sinistre peut être soumis :
- `true` : Tous les documents obligatoires (`category: "mandatory"`) sont uploadés
- `false` : Il manque des documents obligatoires
Utilisez ce champ pour conditionner l'affichage du bouton de soumission dans votre interface.
## Catégories de documents
Chaque type de document attendu est classé dans une catégorie qui détermine son impact sur la soumission :
| Catégorie | Description | Impact sur la soumission |
|-----------|-------------|-------------------------|
| `mandatory` | Document obligatoire | Bloque la soumission tant qu'il n'est pas uploadé |
| `expected` | Document attendu | Ne bloque pas la soumission mais peut retarder le traitement |
| `optional` | Document complémentaire | Peut être uploadé à tout moment, aucun impact |
Seuls les documents `mandatory` sont pris en compte pour le calcul de `canSubmit`. Les documents `expected` et `optional` n'affectent pas la possibilité de soumettre le sinistre.
Les catégories de documents sont déterminées par la configuration produit en fonction du `subtype` du sinistre et du type de produit assuré.
## Types MIME autorisés et taille maximale
Les fichiers uploadés via `PUT /claims/{id}/documents` doivent respecter les contraintes suivantes :
| Contrainte | Valeur |
|------------|--------|
| Taille maximale | **20 Mo** (20 971 520 octets) |
| Types MIME autorisés | `application/pdf`, `image/jpeg`, `image/png`, `image/webp`, `image/heic`, `image/heif` |
Le type MIME est **auto-détecté** à partir du contenu du fichier (magic bytes). Il n'est pas nécessaire de le spécifier dans la requête. Si le type MIME détecté n'est pas dans la liste autorisée, l'upload est rejeté avec l'erreur `1003 INVALID_MIME_TYPE`.
## Modification d'un sinistre
Un sinistre ne peut être modifié qu'en statut **`draft`**. Seuls les champs `description` et `location` sont modifiables. La description doit contenir au minimum 50 caractères.
Les champs `type`, `subtype`, `claimAt` et `questions` sont figés dès la création et ne peuvent pas être modifiés.
## Annulation
L'endpoint `DELETE /claims/{id}` permet d'annuler un sinistre. Le comportement varie selon le statut :
### Sinistre en `draft`
L'annulation est simple : le sinistre passe en statut `archived`. Le webhook `v2.claim.draft.archived` est émis.
### Sinistre soumis (statut `submitted`, `in_progress`, etc.)
L'annulation d'un sinistre soumis effectue les opérations suivantes :
1. Vérification qu'il n'y a pas de paiement actif (`executing`, `approved`, `pending_approval`)
2. Si un paiement est en cours, l'annulation est bloquée avec l'erreur **3007 `ACTIVE_PAYMENT_BLOCKING`**
3. Sinon, le sinistre interne est abandonné et le statut passe à `abandoned`
4. Le webhook `v2.claim.abandoned` est émis
### Corps de la requête
Le body est optionnel et accepte un champ `reason` :
```json
{
"reason": "Le client a retrouvé son vélo"
}
```
Si aucune raison n'est fournie, la raison par défaut est : _"Demande d'abandon initiée par le client"_.
Un sinistre déjà annulé (`archived` ou `abandoned`) ne peut pas être annulé à nouveau. L'API retourne l'erreur **3003 `CLAIM_STATUS_LOCKED`**.
## Types de règlement (settlements)
Les règlements associés à un sinistre sont exposés dans le champ `settlements` du détail. Il existe 4 types :
| Type | Description |
|------|-------------|
| `refund` | Remboursement effectué à l'assuré |
| `charge` | Prélèvement ou facturation (ex : franchise) |
| `cancel` | Annulation d'un règlement précédent |
| `returned` | Virement retourné (échec du transfert, IBAN invalide, etc.) |
Chaque règlement contient les champs suivants :
| Champ | Type | Description |
|-------|------|-------------|
| `id` | `string` | Identifiant unique du règlement |
| `type` | `string` | Type de règlement (`refund`, `charge`, `cancel`, `returned`) |
| `amount` | `number` | Montant en euros |
| `paymentAt` | `string \| null` | Date de paiement effectif (ISO 8601), `null` si pas encore payé |
| `createdAt` | `string` | Date de création du règlement (ISO 8601) |
## Filtrage dans les listes
L'endpoint `GET /claims` supporte le filtre `status` avec trois valeurs :
| Valeur du filtre | Statuts API retournés | Statut interne correspondant |
|------------------|-----------------------|------------------------------|
| `draft` | `draft` | `unmatched`, `pending` |
| `submitted` | `submitted`, `in_progress`, `pending_info`, `closed`, `rejected`, `abandoned` | `matched` |
| `cancelled` | `archived` | `abandonned` |
Le filtre utilise la valeur **`cancelled`** (et non `archived`). Ce filtre retourne les sinistres dont le statut interne est `abandonned`, qui sont exposés avec le statut API `archived`.
## Codes d'erreur
L'API retourne des codes d'erreur structurés dans le champ `code` de la réponse d'erreur. Les codes sont organisés par plage :
### Erreurs de validation (400 Bad Request)
| Code | Nom | Description |
|------|-----|-------------|
| 1000 | `VALIDATION_ERROR` | Erreur de validation générique |
| 1001 | `QUESTIONS_INCOMPLETE` | Les réponses aux questions sont incomplètes |
| 1002 | `FILE_TOO_LARGE` | Le fichier dépasse la taille maximale de 20 Mo |
| 1003 | `INVALID_MIME_TYPE` | Le type MIME du fichier n'est pas autorisé |
| 1004 | `INVALID_SUBTYPE_FOR_PRODUCT` | Le sous-type n'est pas valide pour ce produit |
| 1005 | `INVALID_BASE64` | Le contenu base64 est invalide |
| 1006 | `UNSUPPORTED_CONTENT_TYPE` | Le Content-Type de la requête n'est pas supporté |
| 1007 | `INVALID_TYPE_FOR_SUBTYPE` | Le type n'est pas valide pour ce sous-type |
| 1008 | `UNSUPPORTED_PRODUCT_TYPE` | Le type de produit n'est pas supporté |
### Erreurs de ressource (404 Not Found)
| Code | Nom | Description |
|------|-----|-------------|
| 2001 | `CLAIM_NOT_FOUND` | Sinistre introuvable |
| 2002 | `CONTRACT_NOT_FOUND` | Contrat introuvable |
| 2003 | `PRODUCT_NOT_FOUND` | Produit introuvable |
| 2004 | `DOCUMENT_NOT_FOUND` | Document introuvable |
### Erreurs de logique métier (409 Conflict)
| Code | Nom | Description |
|------|-----|-------------|
| 3001 | `CLAIM_NOT_EDITABLE` | Le sinistre ne peut pas être modifié dans son état actuel |
| 3002 | `CLAIM_NOT_SUBMITTABLE` | Le sinistre ne peut pas être soumis (documents manquants ou état invalide) |
| 3003 | `CLAIM_STATUS_LOCKED` | Le statut du sinistre ne permet pas cette opération |
| 3004 | `DOCUMENTS_INCOMPLETE` | Tous les documents obligatoires ne sont pas présents |
| 3005 | `DOCUMENT_ALREADY_VALIDATED` | Le document a déjà été validé et ne peut pas être remplacé |
| 3006 | `INVALID_DOCUMENT_TYPE` | Le type de document n'est pas valide |
| 3007 | `ACTIVE_PAYMENT_BLOCKING` | Un paiement est en cours, l'opération est bloquée |
### Erreurs d'authentification (401 Unauthorized)
| Code | Nom | Description |
|------|-----|-------------|
| 9001 | `MISSING_API_KEY` | La clé API est manquante dans la requête |
| 9002 | `INVALID_API_KEY` | La clé API est invalide |
| 9003 | `UNAUTHORIZED_USER` | L'utilisateur n'est pas autorisé pour cette clé API |
---
# Éligibilité et compatibilité (/docs/concepts/contract-eligibility)
Matrices de compatibilité produit/contrat/options, règles de panier multi-produits et seuils critiques.
Cette page de référence regroupe les règles d'éligibilité qui déterminent **quelles combinaisons de produits, types de contrat et options sont valides**. Consultez-la avant de construire vos appels API pour éviter les erreurs de validation.
## Types de contrat par type de produit
Chaque type de produit ne supporte pas tous les types de contrat. Un contrat ne peut être créé que si **tous les produits** du panier supportent le type de contrat sélectionné.
| Type de produit | LCD | LMD | LLD |
|---|:---:|:---:|:---:|
| Vélo (`bike`) | Oui | Oui | Oui |
| High-tech (`high-tech`) | Oui | Oui | Oui |
| Sports nautiques (`watersports`) | Oui | Oui | Oui |
| Sports d'hiver (`wintersports`) | Oui | Non* | Non* |
| Événementiel (`event`) | Oui | Oui | Non* |
| Outillage (`small-tools`) | Oui | Oui | Non* |
| Sports (`sports`) | Oui | Oui | Non* |
*Tarification sur demande, veuillez contacter le support Tulip.
**Sports d'hiver** — Les produits `wintersports` ne peuvent être assurés qu'en LCD. Si vous tentez de créer un contrat LMD ou LLD avec un produit `wintersports`, la requête sera rejetée.
## Options disponibles par type de produit
Toutes les options ne sont pas compatibles avec tous les types de produits :
| Option | Vélo | High-tech | Watersports | Wintersports | Event | Small-tools | Sports |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| `break` | Oui | Oui | Oui | Oui | Oui | Oui | Oui |
| `theft` | Oui | Oui | Oui | Oui | Oui | Oui | Oui |
| `client_theft` | Oui | Non | Non | Non | Non | Non | Non |
| `ia` | Oui | Non | Non | Non | Non | Non | Non |
| `rc` | Oui | Non | Non | Non | Non | Non | Non |
| `assistance_standard` | Oui | Non | Non | Non | Non | Non | Non |
| `assistance_premium` | Oui | Non | Non | Non | Non | Non | Non |
| `no_deductible` | Non | Oui | Non | Non | Non | Non | Non |
Les options `client_theft`, `ia`, `rc`, `assistance_standard`, `assistance_premium` sont **exclusives aux vélos**. L'option `no_deductible` est **exclusive au high-tech**. Les tenter sur d'autres produits déclenchera une erreur de validation.
## Options disponibles par type de contrat
La disponibilité des options dépend aussi du type et de la durée du contrat :
| Option | LCD | LMD < 6 mois | LMD ≥ 6 mois | LLD |
|---|:---:|:---:|:---:|:---:|
| `break` / `theft` | Oui (couplés) | Oui (couplés) | Oui (découplables) | Oui (découplables) |
| `client_theft` | Oui | Oui | Oui | Oui |
| `ia` / `rc` | Non | Oui | Oui | Oui |
| `assistance_standard` | Non | Oui | Oui | Oui |
| `assistance_premium` | Non | Non | Non | Oui |
| `home_to_work` / `pro` / `transporter` | Non | Non | Oui | Oui |
| `sharing` | Non | Non | Non | Oui |
| `loa` | Non | Non | Non | Oui |
| `no_deductible` | Non | Non | Non | Oui |
Pour le détail du comportement de chaque option, consultez [Options de contrat](/docs/concepts/contract-options).
## Seuil LMD 6 mois
**Seuil critique** — La durée de 6 mois est un point d'inflexion pour les contrats LMD. Un contrat LMD de 5 mois et un contrat LMD de 7 mois n'ont **pas les mêmes règles** :
- **Options disponibles** : les options d'usage (`home_to_work`, `pro`, `transporter`) ne sont disponibles qu'à partir de 6 mois
- **Couplage casse/vol** : découplable uniquement à partir de 6 mois
- **Option `individual` / `company`** : doit être présente dans les options **à partir de 6 mois** (interdite avant)
- **Champs obligatoires entreprise** : l'adresse et des champs supplémentaires sont requis à partir de 6 mois
Vérifiez la durée de votre contrat pour déterminer les règles applicables.
## LCD courte durée (≤ 4 heures)
Les contrats LCD d'une durée inférieure ou égale à 4 heures bénéficient d'un tarif réduit (« short LCD »). Ce mode est automatiquement détecté par le système en fonction de la différence entre `start_date` et `end_date`. Aucun paramètre supplémentaire n'est requis.
## Panier multi-produits
Lorsqu'un contrat contient plusieurs produits de types différents, des règles de combinaison s'appliquent :
### Règle 1 — Intersection des types de contrat
Le type de contrat doit être supporté par **tous** les produits du panier. Par exemple :
- Vélo (`bike`) + High-tech → LCD, LMD ou LLD (les trois supportés par les deux)
- Vélo (`bike`) + Ski (`wintersports`) → **LCD uniquement** (le ski ne supporte que LCD)
- High-tech + Événementiel (`event`) → LCD ou LMD (l'événementiel ne supporte pas LLD)
### Règle 2 — Union puis intersection des options
Les options disponibles sont calculées en deux étapes :
1. **Union** des options de chaque type de produit
2. **Intersection** avec les options autorisées par le type de contrat
**Exemple concret** — Un contrat LLD avec uniquement du high-tech ne pourra pas inclure l'option `ia` (individuel accident). Mais un contrat LLD avec un vélo + du high-tech le pourra, car le vélo rend l'option éligible.
## Voir aussi
- [Options de contrat](/docs/concepts/contract-options) — Comportement détaillé de chaque option
- [Catalogue de produits](/docs/concepts/products-catalog) — Types, sous-types et contraintes de valeur
- [Cycle de vie des contrats](/docs/concepts/contract-lifecycle) — Statuts, fenêtres temporelles et modification
- [Gestion des erreurs](/docs/guides/error-handling) — Codes d'erreur et résolution
---
# Cycle de vie des contrats (/docs/concepts/contract-lifecycle)
Types de contrat, statuts, fenêtres de modification, renouvellement automatique et résiliation.
## Types de contrat
| Type | Nom | Durée typique |
|------|-----|---------------|
| **LCD** | Location Courte Durée | Quelques heures à quelques jours |
| **LMD** | Location Moyenne Durée | 1 à 12 mois |
| **LLD** | Location Longue Durée | 12 mois à plusieurs années |
Le type de contrat détermine les options requises, les règles de validation et les fenêtres de modification applicables. Voir [Options de contrat](/docs/concepts/contract-options).
## Statuts
Un contrat passe par un statut initial `open`, puis se termine dans l'un des trois états finaux :
```
open → closed | cancel | terminated
```
| Statut | Description |
|--------|-------------|
| `open` | Le contrat est actif et en cours. |
| `closed` | Fin normale : le contrat est arrivé à sa date de fin (`end_date`) de manière automatique. |
| `cancel` | Annulation : le contrat a été supprimé dans les **4 premières heures** suivant sa date de début. |
| `terminated` | Résiliation anticipée : le contrat a été résilié **après la fenêtre de 4 heures**, avant sa date de fin prévue. |
**Trois états finaux distincts** — `closed` est automatique à l'échéance ; `cancel` ne s'applique que dans la fenêtre de 4 heures ; `terminated` nécessite une raison explicite (voir [Résiliation](#résiliation-terminated)).
## Fenêtres temporelles critiques
### Tolérance à la création
La `start_date` bénéficie d'une tolérance de **±30 minutes** à la création. Une date de début légèrement dans le passé (moins de 30 minutes) sera acceptée.
### Fenêtre de 4 heures — Modification des dates
Après **4 heures** suivant la `start_date`, les champs `start_date` et `end_date` ne peuvent plus être modifiés. Toute tentative renvoie l'erreur **1050**. Dans cette même fenêtre, la suppression du contrat produit une **annulation** (`cancel`), et non une résiliation.
### Gel des informations entreprise et particulier
Les informations `company` (erreur **1150**) et `individual` (erreur **1151**) sont gelées **dès que le contrat a démarré** (la `start_date` est passée). La validation sous-jacente est `contractHasNotStarted` : si la date de début est dans le passé, ces champs deviennent immutables, sans aucun délai supplémentaire.
Assurez-vous que les informations entreprise et particulier sont correctes **avant** la date de début du contrat. Après le démarrage, seule la création d'un nouveau contrat permet de corriger ces données.
### Fenêtre de 2 heures — Modification post-clôture
Un contrat passé au statut `closed` peut encore être modifié via `PATCH` pendant **2 heures** après la clôture. Passé ce délai, le contrat est définitivement verrouillé.
Cette fenêtre permet de corriger d'éventuelles erreurs sur un contrat long terme qui vient tout juste d'arriver à échéance.
## Modification d'un contrat
Les champs modifiables dépendent de l'état du contrat et du temps écoulé :
| Champ | Modifiable si... |
|-------|-----------------|
| `start_date`, `end_date` | Contrat démarré depuis moins de 4 heures |
| `company`, `individual` | Contrat pas encore démarré (`start_date` dans le futur) |
| `products` (remplacement) | Le produit concerné est en statut `open` |
| Champs LLD après clôture | Contrat LLD clôturé depuis moins de 2 heures |
```bash
PATCH /v2/contracts/{cid}
```
### Remplacement de produit
La modification d'un produit dans un contrat **crée un nouveau produit** et passe l'ancien en statut `has_been_replaced` avec un champ `replaced_by` pointant vers le nouveau produit.
## Opérations sur les produits
En plus de la modification globale du contrat, vous pouvez gérer les produits individuellement :
### Ajouter un produit
```bash
POST /v2/contracts/{cid}/products
```
Ajoute un ou plusieurs produits au contrat existant.
### Supprimer un produit
```bash
DELETE /v2/contracts/{cid}/products
```
Le corps de la requête contient les identifiants des produits à supprimer (`cpid`).
```json
{
"products": ["cpid_001", "cpid_002"]
}
```
### Mettre à jour un produit
Utilisez `PATCH /v2/contracts/{cid}` en passant l'objet `products` avec les modifications souhaitées. Le remplacement suit le mécanisme décrit ci-dessus (`has_been_replaced`).
## Prévisualisation
Les endpoints suivants supportent le paramètre `?preview=true` :
```
POST /v2/contracts?preview=true
PATCH /v2/contracts/{cid}?preview=true
DELETE /v2/contracts/{cid}?preview=true
POST /v2/contracts/{cid}/products?preview=true
DELETE /v2/contracts/{cid}/products?preview=true
```
En mode prévisualisation :
- Le contrat n'est **pas créé, modifié, résilié ou altéré** réellement
- La réponse contient les champs calculés (prix, garanties, détails produits) tels qu'ils seraient si l'opération était effectuée
- Le champ `cid` est **`undefined`** dans la réponse (aucun contrat n'a été persisté)
- Utile pour valider les données, vérifier les prix et afficher une confirmation à l'utilisateur avant de créer le contrat
**Simulation de prix (Devis)** — Le mode preview est la méthode standard pour obtenir un devis sans créer de contrat. La requête en preview nécessite moins d'informations qu'une création réelle : seuls le type de produit et sa valeur sont requis, les informations utilisateur détaillées (`company`, `individual`) et les données produit complètes (`user_name`, `product_marked`) peuvent être omises.
## Annulation (`cancel`)
Si le contrat a démarré il y a **moins de 4 heures**, la suppression produit une annulation :
```bash
DELETE /v2/contracts/{cid}
```
Le statut du contrat passe à `cancel`. Aucun paramètre supplémentaire n'est requis.
## Résiliation (`terminated`)
Si le contrat a démarré il y a **plus de 4 heures**, la suppression produit une résiliation anticipée :
```bash
DELETE /v2/contracts/{cid}
Content-Type: application/json
{
"reason": "Le locataire a restitué le véhicule avant terme.",
"end_date": "2024-09-15T00:00:00.000Z"
}
```
**Le champ `reason` est obligatoire** pour toute résiliation. Sans ce champ, la requête est rejetée. Le champ `end_date` est optionnel : sans lui, la résiliation est effective immédiatement.
Contraintes sur `end_date` :
- Ne peut pas être dans le passé
- Ne peut pas dépasser la date de fin originale du contrat
Pour plus de détails sur les codes d'erreur, consultez le [guide de gestion des erreurs](/docs/guides/error-handling).
## Renouvellement automatique (Tacite reconduction)
Le renouvellement automatique permet de prolonger un contrat à son échéance sans intervention manuelle. À la date de fin, un nouveau contrat est automatiquement créé avec les mêmes paramètres.
### Activer le renouvellement
Deux méthodes sont disponibles :
**À la création du contrat** — passez `automatic_renewal: true` dans le corps de la requête :
```json
{
"uid": "user-id",
"contract_type": "LLD",
"automatic_renewal": true,
"options": ["break", "theft", "company"],
...
}
```
**Sur un contrat existant** — utilisez l'endpoint dédié :
```bash
POST /v2/contracts/{cid}/automaticRenewal
```
### Champs modifiables
| Champ | Type | Description |
|-------|------|-------------|
| `use_notification` | `boolean` | Active/désactive la notification avant renouvellement |
| `rejected` | `boolean` | Rejeter (annuler) le renouvellement prévu |
| `duration_months` | `number` | Durée du nouveau contrat en mois |
Ces champs se modifient via :
```bash
PATCH /v2/contracts/{cid}/automaticRenewal
```
### Champs en lecture seule
| Champ | Type | Description |
|-------|------|-------------|
| `applied` | `boolean` | Le renouvellement a été appliqué |
| `applied_at` | `string` | Date à laquelle le renouvellement a été appliqué |
| `renewal_scheduled_for` | `string` | Date prévue pour le renouvellement |
| `notified` | `boolean` | La notification a été envoyée |
| `next_cid` | `string` | Identifiant du nouveau contrat créé par le renouvellement |
### Cycle de vie du renouvellement
```
Création → Notification (si activée) → Rejet ou Application → Nouveau contrat (next_cid)
```
1. **Création** — Le renouvellement est configuré (à la création du contrat ou via POST).
2. **Notification** — Si `use_notification` est `true`, une notification est envoyée avant l'échéance.
3. **Rejet ou application** — Le renouvellement peut être rejeté (`rejected: true`) ou s'applique automatiquement à la date prévue.
4. **Nouveau contrat** — Si appliqué, un nouveau contrat est créé. Son identifiant est disponible dans `next_cid`.
### Consulter le renouvellement
```bash
# Renouvellement du contrat courant
GET /v2/contracts/{cid}/automaticRenewal
# Renouvellement par identifiant
GET /v2/contracts/{cid}/automaticRenewal/{renewalId}
# Lister tous les renouvellements d'un contrat
GET /v2/contracts/{cid}/automaticRenewals
```
### Actions en masse (bulk)
Vous pouvez agir sur les renouvellements de plusieurs contrats en une seule requête :
```bash
POST /v2/contracts/automaticRenewals/bulk
```
Les actions disponibles en masse sont :
- **Ajouter** un renouvellement automatique à plusieurs contrats
- **Rejeter** les renouvellements de plusieurs contrats
- **Activer les notifications** sur plusieurs renouvellements
- **Désactiver les notifications** sur plusieurs renouvellements
### Contraintes
- Le renouvellement ne peut **pas être modifié** après la `end_date` du contrat.
- Le renouvellement ne peut **pas être modifié** s'il a déjà été appliqué (`applied: true`).
- Le contrat doit être en statut **`open`** pour configurer ou modifier un renouvellement.
## Contrats de test
**Configuration de compte requise** — La création de contrats de test nécessite l'activation de cette fonctionnalité sur votre compte Tulip. Les contrats de test se comportent comme des contrats normaux mais sont exclus des flux financiers. Pour plus de détails, contacter Tulip.
```json
{
"uid": "user-id",
"contract_type": "LCD",
"test": true,
"options": ["break", "theft"],
...
}
```
## Récapitulatif des fenêtres temporelles
| Fenêtre | Déclencheur | Effet |
|---------|------------|-------|
| **0 → 4h** après `start_date` | Suppression du contrat | Annulation (`cancel`) |
| **> 4h** après `start_date` | Suppression du contrat | Résiliation (`terminated`, `reason` requis) |
| **Dès que `start_date` est passée** | Modification `company` / `individual` | Bloquée (erreurs 1150/1151) |
| **0 → 4h** après `start_date` | Modification `start_date` / `end_date` | Autorisée |
| **> 4h** après `start_date` | Modification `start_date` / `end_date` | Bloquée (erreur 1050) |
| **0 → 2h** après clôture | `PATCH` sur contrat `closed` | Autorisé |
| **> 2h** après clôture | `PATCH` sur contrat `closed` | Bloqué |
## LCD courte durée (≤ 4 heures)
Les contrats LCD d'une durée inférieure ou égale à 4 heures bénéficient d'un tarif réduit automatique (« short LCD »). Ce mode est détecté par le système en fonction de la différence entre `start_date` et `end_date`. Aucun paramètre supplémentaire n'est requis.
## Voir aussi
- [Éligibilité et compatibilité](/docs/concepts/contract-eligibility) — Matrices produit × contrat × options et règles multi-produits
- [Options de contrat](/docs/concepts/contract-options) — Options obligatoires, facultatives et combinaisons selon l'usage
- [Gestion des erreurs](/docs/guides/error-handling) — Codes d'erreur et résolution
- [Environnement de test](/docs/guides/testing-sandbox) — Contrats de test et sandbox
---
# Options de contrat (/docs/concepts/contract-options)
Options obligatoires et facultatives, différences B2B/B2C et options dépréciées.
Les options définissent les garanties et le contexte d'un contrat. Elles influencent le prix, les validations requises et les informations à fournir.
Pour savoir quelles options sont compatibles avec quel type de produit ou de contrat, consultez la page [Éligibilité et compatibilité](/docs/concepts/contract-eligibility).
## Options obligatoires
Les options **`break`** (casse) et **`theft`** (vol) sont **requises pour tout contrat**, quel que soit le type.
```json
{
"options": ["break", "theft"]
}
```
Sans ces deux options, la création échoue avec l'erreur 1009.
### Couplage casse / vol
En **LCD** et **LMD de moins de 6 mois**, les options `break` et `theft` sont **toujours couplées** : vous devez inclure les deux ou aucune.
En **LMD de 6 mois ou plus** et en **LLD**, il est possible de souscrire `break` seul ou `theft` seul (découplage). Cette possibilité dépend de la configuration de votre compte. Si le découplage n'est pas activé, les deux options restent obligatoirement liées.
## Liste des options
| Option | Description | Obligatoire |
|--------|-------------|-------------|
| `break` | Garantie casse | Oui |
| `theft` | Garantie vol | Oui |
| `company` | Contrat pour une entreprise | Requis si B2B |
| `individual` | Contrat pour un particulier | Requis si B2C (LMD/LLD) |
| `home_to_work` | Trajets domicile-travail | Selon produit |
| `pro` | Usage professionnel (hors transporteur) | Selon produit |
| `transporter` | Usage transporteur | Selon produit |
| `sharing` | Produits partagés entre utilisateurs | Optionnel |
| `rc` | Responsabilité civile | Optionnel |
| `ia` | Individuel accident | Optionnel |
| `loa` | Location avec option d'achat | Optionnel |
| `assistance_premium` | Assistance premium | Optionnel |
| `assistance_standard` | Assistance standard | Optionnel |
| `no_deductible` | Suppression de la franchise | Optionnel |
| `client_theft` | Vol par le client | Automatique |
## B2B vs B2C
Le choix entre `company` et `individual` détermine les informations supplémentaires requises.
**Règle par type de contrat** — L'option `individual` / `company` dans le tableau `options` suit des règles strictes :
- **LCD** et **LMD < 6 mois** : `individual` / `company` **ne doit pas** être envoyé dans les options
- **LMD ≥ 6 mois** et **LLD** : `individual` ou `company` **doit** être présent dans les options
### Contrat entreprise (`company`)
Les champs requis pour l'objet `company` varient selon le type de contrat :
| Champ | LMD < 6 mois | LMD ≥ 6 mois | LLD |
|-------|:---:|:---:|:---:|
| `company_name` | Oui | Oui | Oui |
| `first_name` / `last_name` | Oui | Oui | Oui |
| `address`, `zipcode`, `city`, `country` | Non | Oui | Oui |
| `siren` | Non | Non | Oui |
```json
{
"options": ["break", "theft", "company"],
"company": {
"company_name": "Ma Société",
"siren": "123456789",
"first_name": "Marie",
"last_name": "Martin",
"address": "10 rue de la Paix",
"zipcode": "75002",
"city": "Paris",
"country": "France"
}
}
```
**SIREN** — 9 caractères pour la France, 11 caractères pour l'Italie. Le champ est validé selon le pays. Requis uniquement pour les contrats **LLD**.
### Contrat particulier (`individual`)
```json
{
"options": ["break", "theft", "individual"],
"individual": {
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean@example.com",
"address": "5 avenue des Champs-Élysées",
"zipcode": "75008",
"city": "Paris",
"country": "France"
}
}
```
Pour plus de détails sur les différences B2B/B2C, consultez le [guide B2B vs B2C](/docs/guides/b2b-vs-b2c).
## Comportement détaillé des options
### Vol par le client (`client_theft`)
- Disponible **uniquement pour les vélos** (`bike`)
- S'active **automatiquement** lorsque l'option `theft` est sélectionnée et que votre compte le supporte
- Non modifiable manuellement — l'option est forcée quand elle est disponible
### Assistance (`assistance_standard` / `assistance_premium`)
- Réservée au type de produit **vélo** (`bike`) uniquement
- `assistance_standard` est disponible en **LMD** et **LLD** (pas en LCD), **conditionné par la configuration de votre compte**. Si cette configuration n'est pas activée, l'option standard n'est pas proposée.
- `assistance_premium` est disponible en **LLD** uniquement et est **toujours disponible** (non conditionné par la configuration du compte)
- Les deux sont **mutuellement exclusives** : vous ne pouvez choisir que l'une ou l'autre
- Si l'assistance standard n'est pas activée sur votre compte : seule `assistance_premium` est proposée en LLD
### Options d'usage (`home_to_work` / `pro` / `transporter`)
- Réservées au type de produit **vélo** (`bike`) uniquement — les options d'usage ne s'appliquent **jamais** aux produits non-vélo (watersports, high-tech, event, etc.)
- Disponibles pour les contrats **LMD de 6 mois ou plus** et **LLD**
- Pour les LMD de moins de 6 mois **avec un vélo dans le panier**, l'usage `home_to_work` est appliqué automatiquement
- Pour les LLD, une option d'usage est **requise** en plus de `break`, `theft` et `company`/`individual`. L'absence déclenche l'erreur **1015**.
| Usage | Option |
|-------|--------|
| Domicile-travail | `home_to_work` |
| Professionnel | `pro` |
| Transporteur | `transporter` |
### Suppression de franchise (`no_deductible`)
- Disponible **uniquement pour le high-tech** (`high-tech`) en **LLD**
- Augmente la prime du contrat
### Partage (`sharing`)
- Disponible en **LLD** uniquement, pour les **vélos** (`bike`) uniquement
- Lorsqu'activée, le champ `user_name` des produits est automatiquement rempli avec "Partagé"
- Indique que les vélos sont partagés entre plusieurs utilisateurs (pas d'attribution individuelle)
### Location avec option d'achat (`loa`)
- Disponible en **LLD** uniquement, pour les **vélos** (`bike`) uniquement
- Option activable sur le contrat pour indiquer un contexte de leasing
## Options dépréciées
**`assistance` est dépréciée** — Utilisez `assistance_premium` ou `assistance_standard` à la place. L'option `assistance` reste fonctionnelle pour la compatibilité mais sera retirée dans une version future.
## Voir aussi
- [Éligibilité et compatibilité](/docs/concepts/contract-eligibility) — Matrices complètes produit × contrat × options
- [B2B vs B2C](/docs/guides/b2b-vs-b2c) — Différences entre contrats entreprise et particulier
---
# Gestion des documents (/docs/concepts/documents-management)
Catégories de documents, conditions dynamiques, méthodes d'upload, validation et URLs signées.
Les documents justificatifs sont au coeur du processus de sinistre. Leur gestion suit des règles précises selon le type et le sous-type du sinistre.
## Catégories de documents
Chaque document requis pour un sinistre appartient à une catégorie :
| Catégorie | Icône | Impact sur la soumission |
|-----------|-------|------------------------|
| **mandatory** | 🔴 | **Bloque la soumission** — doit être uploadé avant de soumettre |
| **expected** | 🟡 | Ne bloque pas la soumission, mais peut retarder le traitement |
| **optional** | 🟢 | Peut être uploadé si applicable |
| **conditional** | ⚡ | Devient obligatoire ou attendu selon les réponses aux questions |
## Documents conditionnels
Certains documents changent de catégorie selon les réponses aux questions du sinistre.
**Exemple — Vol total de vélo :**
Si la réponse à "Avez-vous récupéré le dépôt de plainte final ?" est :
- `true` → "Dépôt de plainte" devient 🔴 **mandatory**
- `false` → "Récapitulatif de déclaration de plainte en ligne" devient 🔴 **mandatory**
Si "Le vélo est-il électrique ?" = `true` ET "La batterie était-elle présente sur le vélo ?" = `false` :
- "Photo de la batterie" devient 🔴 **mandatory**
Consultez les [matrices par produit](/docs/concepts/questions-by-product) pour toutes les conditions.
## Statuts d'un document
```
waiting_for → to_verify → accepted | rejected
```
| Statut | Description |
|--------|-------------|
| `waiting_for` | En attente de téléchargement |
| `to_verify` | Téléchargé, en attente de vérification par le gestionnaire |
| `accepted` | Accepté |
| `rejected` | Refusé (voir `refusalReason` pour la raison) |
## Méthodes d'upload
### JSON avec base64
```json
PUT /v2/claims/{id}/documents
Content-Type: application/json
[
{
"type": "Dépôt de plainte",
"title": "Plainte du 15/01/2024",
"filename": "plainte.pdf",
"content": "JVBERi0xLjQKJeLjz9MK..."
}
]
```
### Multipart form-data
```
PUT /v2/claims/{id}/documents
Content-Type: multipart/form-data
--boundary
Content-Disposition: form-data; name="meta"
Content-Type: application/json
{"type": "Dépôt de plainte", "title": "Plainte du 15/01/2024"}
--boundary
Content-Disposition: form-data; name="file"; filename="plainte.pdf"
Content-Type: application/pdf
--boundary--
```
**Détection MIME automatique** — Le type MIME est détecté à partir du contenu du fichier, pas de l'extension ou du Content-Type déclaré. Envoyez le fichier brut.
## Téléchargement de documents
```
GET /v2/claims/{claimId}/documents/{documentId}
```
La réponse contient une **URL signée** (`downloadUrl`) pour télécharger le fichier.
**Expiration de 1 heure** — L'URL de téléchargement expire après 1 heure. Le champ `downloadUrlExpiresAt` indique la date d'expiration. Régénérez l'URL en rappelant l'endpoint si elle a expiré.
## Champs du document
| Champ | Type | Description |
|-------|------|-------------|
| `id` | string | Identifiant unique du document |
| `type` | string | Type de document (ex: "Dépôt de plainte") |
| `title` | string | Titre donné par l'uploloader |
| `mimeType` | string | Type MIME détecté |
| `status` | string | Statut actuel |
| `refusalReason` | string \| null | Raison du refus si rejeté |
| `note` | string \| null | Note du gestionnaire |
| `uploadedAt` | string \| null | Date d'upload |
## Erreurs courantes
| Code | Cause |
|------|-------|
| 1002 | Fichier trop volumineux |
| 1003 | Type MIME invalide (détecté depuis le contenu) |
| 1005 | Encodage base64 invalide |
| 1006 | Content-Type non supporté |
| 3004 | Documents incomplets pour soumission |
| 3005 | Document déjà validé |
| 3006 | Type de document invalide |
---
# Concepts (/docs/concepts)
Les concepts fondamentaux de la plateforme Tulip — contrats, produits, sinistres, documents et paiements.
Cette section explique les concepts métier de la plateforme Tulip. Comprenez ces notions pour intégrer l'API efficacement.
## Domaines
Types de contrat (LCD/LMD/LLD), statuts, fenêtre de modification et résiliation.
Options obligatoires et facultatives, comportement détaillé et dépréciations.
Matrices produit × contrat × options, panier multi-produits et seuils critiques.
6 types de produits, 40+ sous-types, limites de valeur et remplacement.
8 statuts, transitions, auto-soumission et double identifiant.
4 catégories de documents, conditions dynamiques, upload et validation.
Matrices complètes des questions et documents par type/sous-type de sinistre.
Remboursements, paiements retournés et cycle de vie financier.
14 types d'événements, payload, retry et bonnes pratiques d'intégration.
---
# Catalogue de produits (/docs/concepts/products-catalog)
Types de produits, sous-types, catalogue et gestion des produits dans les contrats.
Tulip assure **7 grandes familles de produits**. Chaque famille contient des sous-types avec des règles spécifiques pour les sinistres (questions et documents requis). Consultez les [questions par produit](/docs/concepts/questions-by-product) pour les détails par type de sinistre.
## Types et sous-types
### Vélo (`bike`) — 4 sous-types
| Sous-type | Description |
|-----------|-------------|
| `standard` | Vélo classique |
| `electric` | Vélo à assistance électrique |
| `cargo` | Vélo cargo |
| `remorque` | Remorque vélo |
### High-Tech (`high-tech`) — 11 sous-types
| Sous-type | Description |
|-----------|-------------|
| `action-cam` | Caméra d'action |
| `drone` | Drone |
| `camera` | Appareil photo |
| `video-camera` | Caméra vidéo |
| `stabilizer` | Stabilisateur |
| `phone` | Téléphone |
| `computer` | Ordinateur |
| `tablet` | Tablette |
| `small-appliance` | Petit électroménager |
| `large-appliance` | Gros électroménager |
| `other-electronic-equipment` | Autre équipement électronique |
### Sports nautiques (`watersports`) — 13 sous-types
| Sous-type | Description |
|-----------|-------------|
| `kitesurf` | Kitesurf |
| `foil` | Foil |
| `windsurf` | Planche à voile |
| `sailboat` | Voilier |
| `kayak` | Kayak |
| `canoe` | Canoë |
| `water-ski` | Ski nautique |
| `wakeboard` | Wakeboard |
| `mono-ski` | Mono-ski |
| `buoy` | Bouée tractée |
| `paddle` | Paddle |
| `surf` | Surf |
| `pedalo` | Pédalo |
### Sports d'hiver (`wintersports`) — 3 sous-types
| Sous-type | Description |
|-----------|-------------|
| `ski` | Ski |
| `snowboard` | Snowboard |
| `snowshoe` | Raquettes à neige |
### Événementiel (`event`) — 5 sous-types
| Sous-type | Description |
|-----------|-------------|
| `furniture` | Mobilier |
| `tent` | Tente / chapiteau |
| `decorations` | Décorations |
| `tableware` | Vaisselle / arts de la table |
| `entertainment` | Animation / divertissement |
### Outillage (`small-tools`) — 5 sous-types
| Sous-type | Description |
|-----------|-------------|
| `construction-equipment` | Matériel de chantier |
| `diy-tools` | Outils de bricolage |
| `electric-diy-tools` | Outils de bricolage électriques |
| `gardening-tools` | Outils de jardinage |
| `electric-gardening-tools` | Outils de jardinage électriques |
### Sports (`sports`) — 9 sous-types
| Sous-type | Description |
|-----------|-------------|
| `running-hiking` | Course à pied / randonnée |
| `fishing` | Pêche |
| `golf` | Golf |
| `racket-sports` | Sports de raquette |
| `horseriding` | Équitation |
| `ball-sports` | Sports de ballon |
| `fitness` | Fitness / musculation |
| `water-sports` | Sports aquatiques |
| `other` | Autre sport |
## Tableau récapitulatif
| Type produit | Nombre de sous-types |
|---|---|
| `bike` | 4 |
| `high-tech` | 11 |
| `watersports` | 13 |
| `wintersports` | 3 |
| `event` | 5 |
| `small-tools` | 5 |
| `sports` | 9 |
| **Total** | **50** |
## Compatibilité avec les types de contrat
Tous les produits ne supportent pas tous les types de contrat. Par exemple, les produits `wintersports` ne sont assurables qu'en LCD. Consultez la [matrice de compatibilité complète](/docs/concepts/contract-eligibility#types-de-contrat-par-type-de-produit) pour les détails.
## Panier multi-produits
Lorsqu'un contrat contient des produits de types différents, le type de contrat est limité à l'**intersection** des types supportés par chaque produit. Par exemple, un panier contenant un vélo et un ski sera limité à LCD, car le ski ne supporte que LCD.
Pour les règles complètes (intersection, union des options, exclusion high-tech seul), consultez la page [Éligibilité et compatibilité](/docs/concepts/contract-eligibility#panier-multi-produits).
## Contraintes de valeur
**Valeur maximale : 15 000 EUR** — Le champ `value_excl` (valeur hors taxe) d'un produit ne peut pas dépasser 15 000 €. En cas de dépassement, l'API retourne l'erreur **1230**. Voir la [gestion des erreurs](/docs/guides/error-handling) pour plus de détails.
## Champs d'un produit (catalogue)
Lorsque vous créez un produit dans le catalogue via `POST /products`, les champs suivants sont disponibles :
### Champs obligatoires
| Champ | Type | Description |
|-------|------|-------------|
| `uid` | string | Identifiant unique du produit dans votre système |
| `product_type` | string | Type de produit (`bike`, `high-tech`, etc.) |
| `title` | string | Titre / nom du produit |
| `value_excl` | number | Valeur hors taxe en euros (max 15 000) |
| `data.product_subtype` | string | Sous-type du produit (voir listes ci-dessus) |
| `data.brand` | string | Marque du produit |
| `data.model` | string | Modèle du produit |
### Champs optionnels
| Champ | Type | Description |
|-------|------|-------------|
| `description` | string | Description libre du produit |
| `purchased_date` | string | Date d'achat du produit |
| `data.*` | any | Propriétés personnalisées supplémentaires |
**Propriétés personnalisées** — Le champ `data` peut contenir des propriétés personnalisées en plus des champs obligatoires (`product_subtype`, `brand`, `model`). Utilisez-le pour stocker des métadonnées spécifiques à votre catalogue (référence fournisseur, catégorie interne, etc.).
## Statuts d'un produit (catalogue)
Un produit dans le catalogue possède un statut qui contrôle sa visibilité :
| Statut | Description |
|--------|-------------|
| `show` | Le produit est visible et utilisable |
| `hidden` | Le produit est masqué (soft-delete) |
**Soft-delete** — L'appel `DELETE /products/{id}` ne supprime pas réellement le produit. Il passe son statut à `hidden`. Le produit reste accessible en lecture mais n'apparaît plus dans les listes par défaut.
## Utiliser un produit dans un contrat
Il existe deux manières d'associer un produit à un contrat lors de sa création.
### Depuis le catalogue (v1)
Référencez un produit existant du catalogue par son `product_id` et complétez avec les données spécifiques au contrat :
```json
{
"product_id": "prod_abc123",
"data": {
"user_name": "Jean Dupont",
"product_marked": "MARK-001",
"internal_id": "REF-INTERNE-42"
}
}
```
| Champ | Obligatoire | Description |
|-------|-------------|-------------|
| `product_id` | Oui | Identifiant du produit dans le catalogue Tulip |
| `data.user_name` | Oui | Nom de l'utilisateur du produit |
| `data.product_marked` | Oui | Numéro de marquage du produit |
| `data.internal_id` | Non | Identifiant interne (votre référence) |
### En ligne (v2)
Définissez le produit directement dans le contrat sans passer par le catalogue :
```json
{
"product_type": "bike",
"product_subtype": "electric",
"value_excl": 2500,
"brand": "VanMoof",
"model": "S5",
"data": {
"user_name": "Jean Dupont",
"product_marked": "MARK-002"
}
}
```
| Champ | Obligatoire | Description |
|-------|-------------|-------------|
| `product_type` | Oui | Type de produit |
| `product_subtype` | Oui | Sous-type du produit |
| `value_excl` | Oui | Valeur hors taxe en euros |
| `brand` | Oui | Marque du produit |
| `model` | Oui | Modèle du produit |
| `data` | Oui | Données complémentaires (user_name, product_marked, etc.) |
| `data.*` | Non | Propriétés personnalisées supplémentaires |
**Propriétés personnalisées dans les contrats** — Le champ `data` peut également contenir des propriétés personnalisées spécifiques au contrat en plus des champs obligatoires (`user_name`, `product_marked`). Utilisez-le pour stocker des métadonnées propres à votre gestion contractuelle (référence interne, code magasin, numéro de série, etc.).
## Statuts d'un produit dans un contrat
Un produit associé à un contrat possède son propre cycle de vie :
| Statut | Description |
|--------|-------------|
| `open` | Le produit est actif dans le contrat |
| `closed` | Le contrat est arrivé à échéance |
| `cancel` | Le produit a été annulé |
| `terminated` | Le produit a été résilié |
| `has_been_replaced` | Le produit a été remplacé par un nouveau (voir section suivante) |
## Remplacement de produit
Quand vous modifiez un produit dans un contrat (via `PATCH /contracts/{id}`), le système ne met pas à jour le produit existant. Il applique un mécanisme de remplacement :
1. Un **nouveau produit** est créé avec un nouveau `ContractProductId`
2. L'ancien produit passe en statut `has_been_replaced`
3. Un champ `replaced_by` est ajouté sur l'ancien produit, pointant vers le nouveau
Ce mécanisme crée un historique de versions complet. Le produit remplacé reste accessible en lecture mais ne peut plus être modifié. Vous pouvez remonter la chaîne de remplacements via le champ `replaced_by`.
## Types de sinistres par produit
Chaque type de produit supporte des combinaisons spécifiques de sinistres. Consultez les [questions par produit](/docs/concepts/questions-by-product) pour les matrices complètes de questions et documents requis.
| Type produit | Vol total | Vol partiel | Vandalisme | Casse partielle | Casse totale | Panne |
|---|---|---|---|---|---|---|
| `bike` | Oui | Oui | Oui | Oui | — | — |
| `high-tech` | Oui | Oui | — | Oui | Oui | Oui |
---
# Questions et documents par produit (/docs/concepts/questions-by-product)
Matrices complètes des questions obligatoires et documents requis par type et sous-type de sinistre.
Lors de la création d'un sinistre, les questions à répondre et les documents à fournir dépendent du type de produit (issu du contrat) et du sous-type de sinistre choisi.
## Vélo (`bike`)
### Vol total (`total_theft`)
**Questions :**
| Question | Type |
|----------|------|
| Le vélo était-il attaché à un point fixe ? | boolean |
| Le vélo était-il attaché avec le cadenas fourni ? | boolean |
| Y a-t-il eu une effraction d'un lieu privé ? | boolean |
| Le vélo est-il électrique ? | boolean |
| La batterie était-elle présente sur le vélo ? | boolean |
| Avez-vous récupéré le dépôt de plainte final ? | boolean |
**Documents :**
| Document | Catégorie | Condition |
|----------|-----------|-----------|
| Récapitulatif de déclaration de plainte en ligne | 🔴 Obligatoire | Si dépôt de plainte final = **non** |
| Dépôt de plainte | 🔴 Obligatoire | Si dépôt de plainte final = **oui** |
| Photo des clés de l'antivol et de la batterie | 🔴 Obligatoire | — |
| Photo de la batterie | 🔴 Obligatoire | Si vélo électrique = oui ET batterie présente = **non** |
| Photo de l'effraction | 🔴 Obligatoire | Si effraction lieu privé = **oui** |
| Contrat de location | 🟡 Attendu | — |
| Facture d'achat du vélo | 🟡 Attendu | — |
| Certificat de marquage du vélo | 🟡 Attendu | — |
| Dépôt de plainte | 🟡 Attendu | Si dépôt de plainte final = **non** |
### Vol partiel (`partial_theft`)
**Questions :**
| Question | Type |
|----------|------|
| Qu'est-ce qui a été volé ? | text |
| Avez-vous récupéré le dépôt de plainte final ? | boolean |
**Documents :**
| Document | Catégorie | Condition |
|----------|-----------|-----------|
| Récapitulatif de déclaration de plainte en ligne | 🔴 Obligatoire | Si dépôt de plainte final = **non** |
| Dépôt de plainte | 🔴 Obligatoire | Si dépôt de plainte final = **oui** |
| Photo du vélo dans son ensemble | 🔴 Obligatoire | — |
| Photo de la casse (angle 1) | 🔴 Obligatoire | — |
| Photo de la casse (angle 2) | 🔴 Obligatoire | — |
| Contrat de location | 🟡 Attendu | — |
| Devis de réparation | 🟡 Attendu | — |
| Facture de réparation | 🟡 Attendu | — |
| Facture d'achat des pièces | 🟡 Attendu | — |
### Vandalisme (`vandalism`)
**Questions :**
| Question | Type |
|----------|------|
| Quels éléments du matériel ont été vandalisés ? | text |
**Documents :**
| Document | Catégorie |
|----------|-----------|
| Photo du vélo dans son ensemble | 🔴 Obligatoire |
| Photo de la casse (angle 1) | 🔴 Obligatoire |
| Photo de la casse (angle 2) | 🔴 Obligatoire |
| Dépôt de plainte | 🔴 Obligatoire |
| Contrat de location | 🟡 Attendu |
| Devis de réparation | 🟡 Attendu |
| Facture de réparation | 🟡 Attendu |
| Facture d'achat des pièces | 🟡 Attendu |
### Casse partielle (`partial_break`)
**Questions :**
| Question | Type |
|----------|------|
| Quels éléments du matériel sont cassés ? | text |
| La casse est-elle due à un accident avec un tiers ? | boolean |
| Avez-vous fait un constat avec le tiers ? | boolean |
| La réparation est-elle effectuée en externe ? | boolean |
**Documents :**
| Document | Catégorie | Condition |
|----------|-----------|-----------|
| Photo du vélo dans son ensemble | 🔴 Obligatoire | — |
| Photo de la casse (angle 1) | 🔴 Obligatoire | — |
| Photo de la casse (angle 2) | 🔴 Obligatoire | — |
| Constat amiable | 🔴 Obligatoire | Si constat avec le tiers = **oui** |
| Contrat de location | 🟡 Attendu | — |
| Devis de réparation | 🟡 Attendu | Si réparation externe = **oui** |
| Facture de réparation | 🟡 Attendu | — |
| Facture d'achat des pièces | 🟡 Attendu | Si réparation externe = **oui** |
---
## High-Tech (`high-tech`)
### Casse partielle (`partial_break`)
**Questions :**
| Question | Type |
|----------|------|
| Qu'est-ce qui est cassé ? | text |
| Les dégâts ont-ils été causés par un liquide ? | boolean |
**Documents :**
| Document | Catégorie |
|----------|-----------|
| Photo du matériel vue du dessus | 🔴 Obligatoire |
| Photo du matériel vue du dessous | 🔴 Obligatoire |
| Photo du matériel (angle 1) | 🔴 Obligatoire |
| Photo du matériel (angle 2) | 🔴 Obligatoire |
| Photo du matériel (angle 3) | 🔴 Obligatoire |
| Photo du matériel (angle 4) | 🔴 Obligatoire |
| Photo de l'écran allumé | 🔴 Obligatoire |
| Contrat de location (leasing) | 🟡 Attendu |
| Devis des réparations | 🟡 Attendu |
| Facture des réparations | 🟡 Attendu |
### Casse totale (`total_break`)
Mêmes questions et documents que `partial_break`, **sans** la photo de l'écran allumé.
### Vol partiel (`partial_theft`)
**Questions :**
| Question | Type |
|----------|------|
| Quelles pièces ou accessoires ont été volés ? | text |
| Le reste du matériel est-il encore utilisable ? | boolean |
**Documents :**
| Document | Catégorie |
|----------|-----------|
| Dépôt de plainte final | 🔴 Obligatoire |
| Contrat de location (leasing) | 🟡 Attendu |
| Facture du matériel | 🟡 Attendu |
### Vol total (`total_theft`)
**Questions :**
| Question | Type |
|----------|------|
| Y a-t-il eu une effraction d'un lieu sécurisé ? | boolean |
| S'agit-il d'un vol à la sauvette ? | boolean |
**Documents :** Identiques à `partial_theft`.
### Panne (`failure`)
**Questions :**
| Question | Type |
|----------|------|
| Quel est le problème technique constaté ? | text |
| Les dégâts ont-ils été causés par un liquide ? | boolean |
| Le matériel fonctionne-t-il encore ? | boolean |
**Documents :** Identiques à `partial_break`.
---
**Questions personnalisées** — En plus des questions obligatoires définies par Tulip, vous pouvez ajouter vos propres questions personnalisées lors de la création d'un sinistre en fonction de vos besoins métier.
---
---
# Règlements et paiements (/docs/concepts/settlements-payments)
Cycle de vie des remboursements, types de règlements et suivi des paiements.
Les règlements (settlements) représentent les transactions financières liées à un sinistre. Ils apparaissent dans le champ `settlements` de la réponse d'un sinistre.
## Types de règlement
| Type | Description |
|------|-------------|
| `refund` | **Remboursement** — paiement versé à l'assuré |
| `returned` | **Retour** — remboursement échoué (IBAN invalide, virement rejeté, etc.) |
## Cycle de vie
```
1. Remboursement approuvé
└─► Settlement créé (type: "refund", paymentAt: null)
2. Virement traité
└─► paymentAt mis à jour avec la date de paiement
3. Si le virement échoue
└─► Nouveau settlement créé (type: "returned")
└─► Nouveau settlement "refund" créé pour la nouvelle tentative
```
### Exemple de réponse
```json
{
"settlements": [
{
"id": "stl_001",
"type": "refund",
"amount": 500.00,
"paymentAt": "2024-02-15T14:30:00Z",
"createdAt": "2024-02-10T10:00:00Z"
},
{
"id": "stl_002",
"type": "returned",
"amount": 500.00,
"paymentAt": null,
"createdAt": "2024-02-20T09:00:00Z"
}
]
}
```
Dans cet exemple :
1. Un remboursement de 500 € a été effectué le 15/02
2. Ce remboursement a été retourné le 20/02 (virement échoué)
## Champs
| Champ | Type | Description |
|-------|------|-------------|
| `id` | string | Identifiant unique du règlement |
| `type` | `refund` \| `returned` | Type de transaction |
| `amount` | number | Montant en euros |
| `paymentAt` | string \| null | Date de paiement effectif (null si en attente) |
| `createdAt` | string | Date de création du règlement |
## Points d'attention
**Paiement actif bloque l'abandon** — Un sinistre avec un paiement en cours ne peut pas être abandonné (erreur 3007). Attendez que le paiement soit traité avant de tenter un abandon.
**`paymentAt` null** — Un règlement avec `paymentAt: null` signifie que le paiement est en cours de traitement. Il sera mis à jour automatiquement une fois le virement effectué.
## Webhooks associés
| Événement | Description |
|-----------|-------------|
| `v2.claim.settlement.created` | Nouveau règlement créé |
| `v2.claim.settlement.paid` | Paiement effectué |
| `v2.claim.settlement.returned` | Paiement retourné (échoué) |
Voir [Webhooks](/docs/concepts/webhooks) pour l'intégration.
---
# Webhooks (/docs/concepts/webhooks)
Événements en temps réel, format des payloads, politique de retry et bonnes pratiques d'intégration.
Tulip envoie des notifications en temps réel à votre système lorsque des événements se produisent sur vos contrats et sinistres. Les webhooks sont configurés par clé API.
Tulip supporte **28 types d'événements** couvrant le cycle de vie complet des contrats (création, modification, résiliation, renouvellement) et des sinistres (pré-déclaration, traitement, règlements).
## Configuration
Les webhooks se configurent depuis l'[espace développeur](/docs/getting-started/developer-space) de votre interface Colibri, dans l'onglet **Webhooks**.
Pour chaque webhook, vous pouvez :
- Définir un **nom** et une **URL de callback**
- **Activer ou désactiver** le webhook
- **Sélectionner les événements** auxquels vous souhaitez vous abonner (contrats, sinistres, documents, règlements)
## Sécurité
**Validez la source** — Assurez-vous que votre endpoint webhook n'est accessible que depuis les IPs de Tulip ou utilisez un secret partagé dans l'URL (token query parameter) pour vérifier l'authenticité des appels. Contactez le [support](/docs/resources/support) pour obtenir la liste des IPs sources.
Recommandations de sécurité :
1. **Utilisez HTTPS** — Votre URL de callback doit obligatoirement utiliser HTTPS pour garantir le chiffrement des données en transit
2. **Restreignez l'accès** — Configurez votre pare-feu ou reverse proxy pour n'accepter que les requêtes provenant des IPs Tulip
3. **Token de vérification** — Ajoutez un token secret dans votre URL de callback (ex : `https://api.example.com/webhooks?token=votre_secret`) pour valider l'origine des requêtes
4. **Ne vous fiez pas uniquement au payload** — Pour les opérations critiques, utilisez l'API Tulip pour confirmer l'état de la ressource après réception du webhook
## Format du payload
Tous les payloads suivent cette structure :
```json
{
"event_type": "v2.contract.created",
"data": {
// Objet complet (contrat, sinistre ou renouvellement selon l'événement)
}
}
```
Le contenu de `data` dépend du type d'événement :
- **Événements `v2.contract.*`** — contient le contrat complet (même structure que la réponse de `GET /v2/contracts/{cid}`)
- **Événements `v2.contract.renewal.*`** — contient l'objet renouvellement automatique complet
- **Événements `v2.claim.*`** — contient le sinistre complet (même structure que `ClaimResponse`)
## Événements contrats
### Cycle de vie du contrat
| Événement | Description |
|-----------|-------------|
| `v2.contract.created` | Nouveau contrat créé |
| `v2.contract.updated` | Contrat modifié (changement de champs autre que `updated_at`) |
| `v2.contract.closed` | Contrat arrivé à échéance (passage automatique en `closed`) |
| `v2.contract.canceled` | Contrat annulé (dans la fenêtre de 4 heures) |
| `v2.contract.terminated` | Contrat résilié (après la fenêtre de 4 heures) |
| `v2.contract.reopened` | Contrat rouvert (passage en `open`) |
| `v2.contract.renewed` | Renouvellement automatique appliqué, nouveau contrat créé |
#### Exemple de payload contrat
```json
{
"event_type": "v2.contract.created",
"data": {
"cid": "01JQXK8VN4TZMW3",
"uid": "usr_xyz789",
"status": "open",
"contract_type": "LLD",
"start_date": "2026-04-01T00:00:00.000Z",
"end_date": "2027-04-01T00:00:00.000Z",
"options": ["break", "theft", "company", "home_to_work"],
"products": {
"cpid_8e4f1a2b": {
"product_id": "prod_velo_elec_001",
"status": "open",
"start_date": "2026-04-01T00:00:00.000Z",
"end_date": "2027-04-01T00:00:00.000Z",
"data": {
"user_name": "Marie Martin",
"product_marked": "MARK-001"
}
}
},
"company": {
"company_name": "Transport Express SAS",
"first_name": "Marie",
"last_name": "Martin",
"address": "10 rue de la Paix",
"zipcode": "75002",
"city": "PARIS-2EME-ARRONDISSEMENT",
"country": "FR"
},
"test": false,
"created_date": "2026-04-01T12:00:00.000Z",
"updated_date": "2026-04-01T12:00:00.000Z"
}
}
```
### Renouvellement automatique
| Événement | Description |
|-----------|-------------|
| `v2.contract.renewal.enabled` | Renouvellement automatique activé sur un contrat |
| `v2.contract.renewal.updated` | Configuration du renouvellement modifiée |
| `v2.contract.renewal.rejected` | Renouvellement rejeté par le client |
| `v2.contract.renewal.resumed` | Renouvellement précédemment rejeté réactivé |
| `v2.contract.renewal.notification` | Notification de renouvellement envoyée au client |
#### Exemple de payload renouvellement
```json
{
"event_type": "v2.contract.renewal.rejected",
"data": {
"id": "ar_abc123",
"cid": "01JQXK8VN4TZMW3",
"uid": "usr_xyz789",
"applied": false,
"applied_at": null,
"renewal_scheduled_for": "2027-04-01T00:00:00.000Z",
"use_notification": true,
"notified": true,
"notified_at": "2027-03-01T10:00:00.000Z",
"notification_scheduled_for": "2027-03-01T00:00:00.000Z",
"rejected": true,
"rejected_at": "2027-03-15T14:30:00.000Z",
"next_cid": null,
"created_at": "2026-04-01T12:00:00.000Z",
"updated_at": "2027-03-15T14:30:00.000Z"
}
}
```
## Événements sinistres
### Pré-déclaration (draft)
| Événement | Description |
|-----------|-------------|
| `v2.claim.draft.created` | Nouvelle pré-déclaration créée |
| `v2.claim.draft.ready` | Tous les documents obligatoires uploadés, prête pour soumission |
| `v2.claim.draft.archived` | Pré-déclaration annulée |
### Cycle de vie du sinistre
| Événement | Description |
|-----------|-------------|
| `v2.claim.created` | Sinistre soumis et créé dans le système |
| `v2.claim.closed` | Sinistre clôturé (traité et indemnisé) |
| `v2.claim.reopened` | Sinistre rouvert après clôture |
| `v2.claim.abandoned` | Sinistre abandonné |
| `v2.claim.refused` | Sinistre refusé par l'assureur |
### Statut
| Événement | Description |
|-----------|-------------|
| `v2.claim.status.updated` | Changement de statut du sinistre |
### Documents
| Événement | Description |
|-----------|-------------|
| `v2.claim.document.uploaded` | Document uploadé |
| `v2.claim.document.accepted` | Document accepté par le gestionnaire |
| `v2.claim.document.refused` | Document refusé par le gestionnaire |
| `v2.claim.document.requested` | Document supplémentaire demandé |
### Règlements
| Événement | Description |
|-----------|-------------|
| `v2.claim.settlement.created` | Nouveau règlement créé |
| `v2.claim.settlement.paid` | Paiement effectué |
| `v2.claim.settlement.returned` | Paiement retourné (échoué) |
## Exemple de handler webhook
Voici un exemple de handler en Node.js/Express pour recevoir et traiter les webhooks Tulip :
```js title="webhook-handler.js"
const express = require("express");
const app = express();
app.use(express.json());
app.post("/webhooks/tulip", (req, res) => {
// 1. Répondre immédiatement avec un 200
res.status(200).json({ received: true });
// 2. Traiter l'événement de manière asynchrone
const { event_type, data } = req.body;
switch (event_type) {
// Événements contrats
case "v2.contract.created":
handleContractCreated(data);
break;
case "v2.contract.terminated":
handleContractTerminated(data);
break;
case "v2.contract.renewal.notification":
handleRenewalNotification(data);
break;
// Événements sinistres
case "v2.claim.created":
handleClaimCreated(data);
break;
case "v2.claim.closed":
handleClaimClosed(data);
break;
default:
console.log(`Événement non géré: ${event_type}`);
}
});
async function handleContractCreated(data) {
console.log(`Nouveau contrat créé: ${data.cid}`);
// Ex: synchroniser le contrat dans votre système
}
async function handleContractTerminated(data) {
console.log(`Contrat résilié: ${data.cid}`);
// Ex: mettre à jour le statut dans votre base de données
}
async function handleRenewalNotification(data) {
console.log(`Notification de renouvellement: contrat ${data.cid}`);
// Ex: notifier le client via votre canal préféré
}
async function handleClaimCreated(data) {
console.log(`Nouveau sinistre créé: ${data.claimId}`);
// Ex: créer une entrée dans votre système, notifier une équipe
}
async function handleClaimClosed(data) {
console.log(`Sinistre clôturé: ${data.claimId}`);
// Ex: mettre à jour le statut dans votre base de données
}
app.listen(3000, () => {
console.log("Webhook handler en écoute sur le port 3000");
});
```
**Répondez avant de traiter** — Il est essentiel de retourner un HTTP 200 **avant** de lancer votre logique métier. Si votre traitement prend trop de temps, Tulip considérera la livraison comme échouée et déclenchera un retry.
## Politique de retry
Si votre endpoint retourne un code HTTP non-2xx, Tulip réessaie avec un backoff exponentiel :
| Tentative | Délai |
|-----------|-------|
| 1ère | 1 minute |
| 2ème | 5 minutes |
| 3ème | 30 minutes |
Après 3 échecs, le webhook est marqué comme échoué.
**Livraisons échouées** — Si un webhook est marqué comme échoué après 3 tentatives, l'événement n'est pas perdu. Depuis l'[espace développeur](/docs/getting-started/developer-space#consulter-les-logs-de-livraison), vous pouvez consulter l'historique des livraisons, **rejouer** un webhook échoué ou **annuler** une tentative en attente.
## Bonnes pratiques
1. **Répondez rapidement** — Retournez un 200 dès réception, traitez de manière asynchrone
2. **Idempotence** — Votre handler doit supporter les doublons (même événement reçu plusieurs fois)
3. **Vérifiez le `event_type`** — Ignorez les événements non gérés plutôt que de retourner une erreur
4. **Loggez les payloads** — Conservez les payloads bruts pour le debugging
5. **Gérez les retries** — Un même événement peut arriver plusieurs fois en cas de timeout
## Espace développeur
La configuration des webhooks, la consultation des logs de livraison et le replay des webhooks échoués se font depuis l'[espace développeur](/docs/getting-started/developer-space) de Colibri.
---
# Authentification (/docs/getting-started/authentication)
Comment obtenir et utiliser votre clé API pour accéder à l'API Tulip.
Tous les endpoints de l'API Tulip requièrent une authentification par clé API.
## Obtenir votre clé API
Votre clé API est fournie par Tulip lors de la mise en place de votre partenariat. Si vous n'en avez pas encore, contactez votre interlocuteur commercial ou le [support](/docs/resources/support).
Vous recevrez deux clés :
- **Clé développement** — pour les tests et le développement (aucun impact financier)
- **Clé production** — pour les contrats et sinistres réels
Les deux clés utilisent la même URL : `https://api.mytulip.io/v2`
Vous pouvez retrouver votre clé API à tout moment depuis l'interface [Colibri](app.mytulip.io) : **Profil > Paramètres > Intégration API** ou dans l'[espace développeur](/docs/getting-started/developer-space) (menu latéral > Développeur). L'espace développeur vous donne également accès aux logs d'appels API et à la gestion des webhooks.
## Utilisation
Incluez votre clé API dans le header `key` de chaque requête :
```bash
curl -X GET https://api.mytulip.io/v2/contracts \
-H "key: votre-cle-api"
```
```typescript
const response = await fetch('https://api.mytulip.io/v2/contracts', {
headers: {
'key': 'votre-cle-api',
'Content-Type': 'application/json',
},
});
```
## Erreurs d'authentification
| Code | Message | Cause |
|------|---------|-------|
| 9001 | Missing API key | Le header `key` est absent de la requête |
| 9002 | Invalid API key | La clé API fournie n'est pas valide |
| 9003 | Unauthorized user | La clé est valide mais n'a pas accès à cette ressource |
Exemple de réponse d'erreur :
```json
{
"success": false,
"error": {
"code": 9001,
"message": "Missing API key"
}
}
```
## Bonnes pratiques
- **Ne partagez jamais** votre clé API dans du code client ou des dépôts publics
- **Utilisez des variables d'environnement** pour stocker vos clés
- **Utilisez la clé développement** pour le développement et les tests
- En cas de compromission, contactez immédiatement le [support](/docs/resources/support) pour révoquer et régénérer votre clé
---
# Conventions API (/docs/getting-started/conventions)
Formats de données, authentification, géolocalisation, pagination, structure des réponses et codes HTTP.
## Authentification
Toutes les requêtes vers l'API Tulip nécessitent une clé API transmise via le header `key`.
```bash
curl -X GET https://api.mytulip.io/v2/contracts \
-H "key: YOUR_API_KEY" \
-H "Content-Type: application/json"
```
Il n'y a pas de token Bearer. L'authentification repose uniquement sur le header `key`. Consultez la page [Authentification](/docs/getting-started/authentication) pour obtenir vos clés sandbox et production.
## URL de base
Toutes les requêtes sont faites sur :
```
https://api.mytulip.io/v2
```
## Format des dates
Toutes les dates sont au format **ISO 8601** en UTC :
```
2024-01-15T14:30:00.000Z
```
Le suffixe `Z` indique UTC. Toutes les dates retournées par l'API sont en UTC.
## Pays supportés
L'API Tulip supporte les pays suivants, identifiés par leur code **ISO 3166-1 alpha-2** (2 lettres) :
| Code | Pays |
|------|------|
| `FR` | France métropolitaine |
| `BE` | Belgique |
| `IT` | Italie |
| `RE` | La Réunion |
Le champ `country` utilise ces codes dans les objets `company`, `individual` et les requêtes Géo.
Le code `RE` désigne La Réunion. Les codes postaux réunionnais commencent par `974`.
## Normalisation des villes
Les noms de villes doivent être transmis dans un **format normalisé en majuscules**, tel que retourné par l'API Géo.
| Format | Exemple |
|--------|---------|
| Correct | `PARIS-1ER-ARRONDISSEMENT` |
| Correct | `LYON-2EME-ARRONDISSEMENT` |
| Correct | `SAINT-DENIS` |
| Incorrect | `Paris` |
| Incorrect | `paris 1er` |
| Incorrect | `Saint Denis` |
L'API **rejette les noms de villes non normalisés**. Utilisez toujours l'API Géo pour récupérer le nom normalisé avant de l'envoyer dans vos requêtes.
### Validation ville / code postal
La validation des couples ville + code postal est effectuée contre la base de données Géo de Tulip. En cas de valeur invalide :
**Entreprise (company) :**
| Code | Description |
|------|-------------|
| 1106 | Code postal manquant ou invalide |
| 1107 | Ville manquante ou invalide |
| 1108 | Pays manquant ou invalide |
**Particulier (individual) :**
| Code | Description |
|------|-------------|
| 1403 | Code postal manquant |
| 1407 | Code postal invalide |
| 1404 | Ville manquante |
| 1408 | Ville invalide |
| 1406 | Pays manquant |
| 1409 | Pays invalide |
## API Géo
L'API Géo permet de récupérer les noms de villes normalisés et de valider les codes postaux. Deux endpoints sont disponibles — **sans authentification**.
### Recherche par pays et code postal
```bash
curl -X GET https://api.mytulip.io/v2/geo/FR/75001
```
Retourne les villes correspondant à un code postal dans un pays donné :
```json
{
"success": true,
"data": [
{
"city": "PARIS-1ER-ARRONDISSEMENT",
"zipcode": "75001",
"country": "FR"
}
]
}
```
### Suggestions de villes
```bash
curl -X GET "https://api.mytulip.io/v2/geo/suggestions?query=saint-den"
```
Retourne des suggestions avec un score de confiance, utile pour l'auto-complétion :
```json
{
"success": true,
"data": {
"cities": [
{
"ville": "SAINT-DENIS",
"zipcode": "93200",
"confidence": 0.95
},
{
"ville": "SAINT-DENIS",
"zipcode": "97400",
"confidence": 0.90
}
]
}
}
```
L'endpoint de suggestions est idéal pour implémenter une auto-complétion côté client. Le champ `confidence` (de 0 à 1) indique la pertinence du résultat.
## Structure des réponses
### Succès (format standard)
```json
{
"success": true,
"data": {
"id": "claim_abc123",
"status": "open",
"created_at": "2024-01-15T14:30:00.000Z"
}
}
```
### Succès (format contrats)
Les endpoints de contrats utilisent un format spécifique avec `status` et `execution_id` :
```json
{
"status": "success",
"execution_id": "exec_7f3a2b9e",
"contract": {
"cid": "01CFV26E8TS0U",
"uid": "1b0b2b3b4b5b",
"status": "open",
"contract_type": "LCD"
}
}
```
L'`execution_id` est un identifiant unique généré pour chaque appel API. Communiquez-le au support Tulip pour faciliter le diagnostic d'un problème.
### Erreur
```json
{
"success": false,
"error": {
"code": 1050,
"message": "The contract started more than 4 hours ago."
}
}
```
Les messages d'erreur sont toujours en **anglais**. Consultez le [guide des erreurs](/docs/guides/error-handling) pour la liste complète.
## Pagination
Les endpoints de liste supportent la pagination par offset :
| Paramètre | Type | Description |
|-----------|------|-------------|
| `limit` | number | Nombre de résultats par page (défaut : 20, max : 100) |
| `offset` ou `skip` | number | Nombre de résultats à ignorer |
```bash
curl -X GET "https://api.mytulip.io/v2/claims?userId=user_123&limit=10&offset=20" \
-H "key: YOUR_API_KEY"
```
La réponse inclut les métadonnées de pagination :
```json
{
"success": true,
"data": [...],
"pagination": {
"total": 45,
"offset": 20,
"limit": 10
}
}
```
## Types de contenu
| Direction | Content-Type |
|-----------|-------------|
| Requêtes (JSON) | `application/json` |
| Upload de documents | `multipart/form-data` |
| Réponses | toujours `application/json` |
## Codes HTTP
| Code | Signification | Action recommandée |
|------|---------------|-------------------|
| 200 | Succès | — |
| 201 | Ressource créée | — |
| 400 | Erreur de validation | Consultez `error.code` et `error.message` |
| 401 | Clé API manquante ou invalide | Vérifiez le header `key` |
| 403 | Permissions insuffisantes (code 98888) | Vérifiez les droits de votre clé API |
| 404 | Ressource non trouvée | Vérifiez l'identifiant |
| 429 | Rate limit atteint | Réessayez avec backoff exponentiel |
| 500 | Erreur serveur | Réessayez avec backoff ; contactez le support avec l'`execution_id` |
---
# Espace développeur (/docs/getting-started/developer-space)
Gérer vos clés API, consulter les logs d'appels et configurer vos webhooks depuis l'interface Colibri.
L'interface [Colibri](app.mytulip.io) met à disposition un **espace développeur** accessible depuis le menu latéral (section **Développeur**). Cet espace vous permet de gérer votre intégration API en autonomie.
L'espace développeur n'est visible que pour les utilisateurs ayant le rôle **propriétaire d'API**. Si vous ne voyez pas la section « Développeur » dans le menu, contactez votre administrateur de compte.
## Clés API
La page principale de l'espace développeur affiche vos clés API :
- **Identifiant** de chaque clé (partiellement masqué pour la sécurité)
- **Nom** et **date de dernière mise à jour**
- Accès direct aux **logs** de chaque clé
Votre clé API est également accessible depuis **Profil > Paramètres > Intégration API** avec un bouton de copie rapide.
**Votre clé API est confidentielle** — Ne la partagez jamais dans du code client ou des dépôts publics. Utilisez des variables d'environnement pour la stocker. En cas de compromission, contactez immédiatement le [support](/docs/resources/support).
## Logs des appels API
L'onglet **Appels** vous donne une vue complète de toutes les requêtes effectuées vers l'API Tulip :
### Filtrage
- **Méthode HTTP** : GET, POST, PATCH, DELETE
- **Code de statut** HTTP
- **Code d'erreur** Tulip
- **Plage de dates** (du / au)
- **Recherche libre** par identifiant
### Détail d'un appel
Pour chaque appel, vous pouvez consulter :
| Champ | Description |
|-------|-------------|
| Statut HTTP | Code de retour avec indicateur visuel (succès/erreur) |
| Méthode | GET, POST, PATCH ou DELETE |
| Chemin | URL de l'endpoint appelé |
| Durée | Temps de réponse en millisecondes |
| Horodatage | Date et heure de la requête |
| Identifiant d'exécution | Référence unique pour le support |
| Paramètres de requête | Query parameters envoyés (JSON) |
| Corps de la requête | Body envoyé (JSON) |
| Réponse | Corps de la réponse retournée (JSON) |
L'identifiant d'exécution (`execution_id`) est retourné dans chaque réponse API. Communiquez-le au [support](/docs/resources/support) pour faciliter le diagnostic en cas de problème.
## Gestion des webhooks
L'onglet **Webhooks** vous permet de créer et gérer vos webhooks en autonomie, sans contacter le support.
### Créer un webhook
Pour chaque webhook, configurez :
- **Nom** : identifiant libre pour reconnaître le webhook
- **URL de callback** : l'endpoint HTTPS qui recevra les notifications
- **Événements** : sélectionnez les événements souhaités parmi 5 catégories (contrats, pré-déclarations, sinistres, documents, règlements)
- **Statut** : actif ou inactif (vous pouvez désactiver un webhook sans le supprimer)
Pour la liste complète des événements disponibles, consultez la page [Webhooks](/docs/concepts/webhooks).
### Consulter les logs de livraison
Pour chaque webhook, un historique détaillé des livraisons est disponible :
| Champ | Description |
|-------|-------------|
| Statut | Succès, erreur ou en attente de retry |
| Événement | Type d'événement (`v2.contract.created`, etc.) |
| Code de réponse | Code HTTP retourné par votre endpoint |
| Durée | Temps de réponse de votre endpoint |
| Tentative | Numéro de la tentative (1ère, 2ème, 3ème) |
| Prochaine tentative | Date du prochain retry (si en attente) |
| Payload | Corps envoyé à votre endpoint (JSON) |
| Réponse | Corps retourné par votre endpoint (JSON) |
### Rejouer ou annuler
- **Rejouer** : relancer la livraison d'un webhook échoué
- **Annuler** : abandonner une tentative de retry en attente
Ces actions sont disponibles directement depuis le détail de chaque livraison.
## Voir aussi
- [Authentification](/docs/getting-started/authentication) — Obtenir et utiliser votre clé API
- [Webhooks](/docs/concepts/webhooks) — Liste des événements, format des payloads et bonnes pratiques
- [Environnement de test](/docs/guides/testing-sandbox) — Tester votre intégration en sandbox
---
# Environnements (/docs/getting-started/environments)
URL de l'API, clés de développement et de production, et contrats de test.
## URL de l'API
Tulip expose une **URL unique** pour tous les appels :
```
https://api.mytulip.io/v2
```
La distinction entre développement et production se fait par la **clé API** utilisée, pas par l'URL.
## Clés API
Vous disposez de deux clés API distinctes :
| Clé | Usage |
|-----|-------|
| **Clé développement** | Tests et développement — les contrats créés n'ont aucun impact financier |
| **Clé production** | Contrats et sinistres réels avec facturation active |
Les deux clés utilisent la **même URL**. C'est la clé qui détermine le contexte (développement ou production). Retrouvez vos clés depuis l'[espace développeur](/docs/getting-started/developer-space).
## Contrats de test
En complément de la clé développement, vous pouvez créer des **contrats de test** avec n'importe quelle clé en passant le paramètre `test: true` lors de la création :
```json
{
"uid": "user_123",
"contract_type": "LCD",
"test": true,
"options": ["break", "theft"],
"start_date": "2026-04-01T08:00:00.000Z",
"end_date": "2026-04-02T18:00:00.000Z",
"products": [...]
}
```
Les contrats de test :
- Sont distingués des contrats réels dans les listes (filtrable via `?test=true`)
- N'ont aucun impact financier
- Permettent de valider votre intégration
Pour plus de détails sur les stratégies de test, consultez le [guide de test](/docs/guides/testing-sandbox).
---
# Déclarer votre premier sinistre (/docs/getting-started/first-claim)
Tutoriel pas à pas pour créer un sinistre, uploader des documents et soumettre la déclaration.
Ce tutoriel vous guide à travers le flux complet d'un sinistre : création, upload de documents, soumission et annulation.
## Prérequis
- Un [contrat actif](/docs/getting-started/first-contract) avec son `contractId` et `contractProductId`
- Votre clé API
- Les réponses aux questions liées au type de sinistre (voir [questions par produit](/docs/concepts/questions-by-product))
## Étape 1 : Créer la pré-déclaration
```bash
curl -X POST https://api.mytulip.io/v2/claims \
-H "key: votre-cle-api" \
-H "Content-Type: application/json" \
-d '{
"userId": "votre-user-id",
"contractId": "votre-contract-id",
"contractProductId": "votre-contract-product-id",
"type": "theft",
"subtype": "total_theft",
"claimAt": "2024-01-15T14:30:00Z",
"description": "Mon vélo a été volé le 15 janvier 2024 vers 14h30 devant la gare de Lyon. Il était attaché avec le cadenas fourni à un poteau fixe.",
"questions": {
"Le vélo était-il attaché à un point fixe ?": true,
"Le vélo était-il attaché avec le cadenas fourni ?": true,
"Y a-t-il eu une effraction d'\''un lieu privé ?": false,
"Le vélo est-il électrique ?": true,
"La batterie était-elle présente sur le vélo ?": false,
"Avez-vous récupéré le dépôt de plainte final ?": true
},
"insured": {
"firstName": "Jean",
"lastName": "Dupont",
"phoneNumber": "+33612345678",
"email": "jean.dupont@example.com"
},
"location": {
"address": "Gare de Lyon, Place Louis-Armand",
"city": "Paris",
"zipcode": "75012",
"country": "France"
}
}'
```
### Réponse
```json
{
"success": true,
"data": {
"claim": {
"id": "claim_xyz789",
"claimId": null,
"status": "draft",
"canSubmit": false,
"type": "theft",
"subtype": "total_theft",
"documentEntries": [
{
"type": "Dépôt de plainte",
"category": "mandatory",
"status": "waiting_for",
"document": null
},
{
"type": "Photo des clés de l'antivol et de la batterie",
"category": "mandatory",
"status": "waiting_for",
"document": null
}
]
}
}
}
```
**Double identifiant** — `id` est l'identifiant de la pré-déclaration, disponible immédiatement. `claimId` sera attribué après soumission. Les deux sont acceptés par tous les endpoints GET.
Notez le `id` et vérifiez les `documentEntries` pour savoir quels documents télécharger.
## Étape 2 : Uploader les documents
L'endpoint `PUT /v2/claims/{id}/documents` accepte **un seul document par requête**. Deux méthodes sont disponibles : JSON avec base64 ou multipart/form-data.
### Formats acceptés et limites
| Contrainte | Valeur |
|------------|--------|
| **Nombre de fichiers** | 1 par requête |
| **Taille maximale** | 20 Mo (20 971 520 octets) |
| **Types MIME autorisés** | `application/pdf`, `image/jpeg`, `image/png`, `image/webp`, `image/heic`, `image/heif` |
**Détection MIME automatique** — Le type MIME est détecté à partir du contenu du fichier (magic bytes), pas de l'extension ni du Content-Type déclaré. Envoyez le fichier brut sans modifier son contenu.
### Méthode 1 : JSON avec base64
```bash
curl -X PUT "https://api.mytulip.io/v2/claims/claim_xyz789/documents?autoSubmit=true" \
-H "key: votre-cle-api" \
-H "Content-Type: application/json" \
-d '{
"type": "Dépôt de plainte",
"title": "Plainte du 15/01/2024",
"filename": "plainte.pdf",
"content": "JVBERi0xLjQKJeLjz9MK...",
"comment": "Dépôt de plainte final récupéré au commissariat"
}'
```
| Champ | Type | Requis | Description |
|-------|------|--------|-------------|
| `type` | string | oui | Type de document (doit correspondre à un `documentEntries[].type`) |
| `title` | string | oui | Titre libre du document |
| `filename` | string | oui | Nom du fichier avec extension |
| `content` | string | oui | Contenu du fichier encodé en base64 |
| `comment` | string | non | Commentaire optionnel |
### Méthode 2 : Multipart form-data
```bash
curl -X PUT "https://api.mytulip.io/v2/claims/claim_xyz789/documents?autoSubmit=true" \
-H "key: votre-cle-api" \
-F 'meta={"type":"Dépôt de plainte","title":"Plainte du 15/01/2024","comment":"Dépôt de plainte final"}' \
-F "file=@plainte.pdf"
```
| Champ | Type | Requis | Description |
|-------|------|--------|-------------|
| `meta` | JSON string | oui | Objet JSON stringifié contenant `type`, `title`, et optionnellement `comment` |
| `file` | binary | oui | Le fichier binaire à uploader |
### Réponse
```json
{
"success": true,
"data": {
"document": {
"id": "doc_abc123",
"type": "Dépôt de plainte",
"title": "Plainte du 15/01/2024",
"mimeType": "application/pdf",
"status": "to_verify",
"refusalReason": null,
"note": null,
"createdAt": "2024-01-16T10:00:00Z",
"updatedAt": "2024-01-16T10:00:00Z",
"uploadedAt": "2024-01-16T10:00:00Z",
"acceptedAt": null,
"refusedAt": null
},
"isRequiredDocument": true
}
}
```
### Auto-soumission avec `?autoSubmit=true`
Ajoutez le paramètre `?autoSubmit=true` à l'URL de l'upload. Si, après cet upload, tous les documents obligatoires sont présents, le sinistre est automatiquement soumis. Dans ce cas, la réponse inclut également les informations de soumission :
```json
{
"success": true,
"data": {
"document": {
"id": "doc_abc123",
"type": "Photo des clés de l'antivol et de la batterie",
"title": "Photo clés",
"mimeType": "image/jpeg",
"status": "to_verify"
},
"isRequiredDocument": true,
"claim": {
"id": "claim_xyz789",
"claimId": "internal_claim_abc",
"status": "submitted",
"canSubmit": false,
"autoSubmitted": true,
"submittedAt": "2024-01-16T12:00:00Z"
}
}
}
```
**Quand utiliser `autoSubmit`** — Utilisez `autoSubmit=true` sur le dernier document uploadé pour éviter un appel supplémentaire à `/submit`. Si les documents obligatoires ne sont pas encore tous présents, le paramètre est ignoré et seul le document est uploadé.
## Étape 3 : Récupérer un document
Utilisez cet endpoint pour obtenir une URL signée de téléchargement d'un document uploadé :
```bash
curl -X GET https://api.mytulip.io/v2/claims/claim_xyz789/documents/doc_abc123 \
-H "key: votre-cle-api"
```
### Réponse
```json
{
"success": true,
"data": {
"document": {
"id": "doc_abc123",
"type": "Dépôt de plainte",
"title": "Plainte du 15/01/2024",
"mimeType": "application/pdf",
"status": "to_verify",
"refusalReason": null,
"note": null,
"createdAt": "2024-01-16T10:00:00Z",
"updatedAt": "2024-01-16T10:00:00Z",
"uploadedAt": "2024-01-16T10:00:00Z",
"acceptedAt": null,
"refusedAt": null,
"downloadUrl": "https://storage.googleapis.com/...",
"downloadUrlExpiresAt": "2024-01-16T11:00:00Z"
}
}
}
```
**Expiration de 1 heure** — L'URL signée expire après 1 heure. Le champ `downloadUrlExpiresAt` indique la date d'expiration exacte. Rappelez l'endpoint pour générer une nouvelle URL si nécessaire.
## Étape 4 : Vérifier la soumissibilité
Récupérez le sinistre pour vérifier si tous les documents obligatoires sont uploadés :
```bash
curl -X GET https://api.mytulip.io/v2/claims/claim_xyz789 \
-H "key: votre-cle-api"
```
Vérifiez le champ `canSubmit` dans `data.claim` :
- `true` — tous les documents obligatoires sont fournis, vous pouvez soumettre
- `false` — il manque des documents obligatoires (consultez `documentEntries` pour identifier les manquants)
## Étape 5 : Soumettre le sinistre
**Étape optionnelle si vous utilisez `autoSubmit`** — Si vous avez utilisé `?autoSubmit=true` lors de l'upload du dernier document et que la réponse contenait un objet `claim` avec `autoSubmitted: true`, le sinistre est déjà soumis. Vous pouvez passer cette étape.
```bash
curl -X POST https://api.mytulip.io/v2/claims/claim_xyz789/submit \
-H "key: votre-cle-api"
```
### Réponse
```json
{
"success": true,
"data": {
"claim": {
"id": "claim_xyz789",
"claimId": "internal_claim_abc",
"status": "submitted",
"canSubmit": false,
"autoSubmitted": false,
"submittedAt": "2024-01-16T12:00:00Z"
}
}
}
```
## Étape 6 : Annuler un sinistre
Vous pouvez annuler un sinistre à tout moment (tant qu'il n'a pas de paiement en cours) via `DELETE /v2/claims/{id}` :
```bash
curl -X DELETE https://api.mytulip.io/v2/claims/claim_xyz789 \
-H "key: votre-cle-api" \
-H "Content-Type: application/json" \
-d '{
"reason": "Le vélo a été retrouvé"
}'
```
Le champ `reason` est optionnel. Si omis, la raison par défaut est "Demande d'abandon initiée par le client".
### Réponse
```json
{
"success": true,
"data": {
"claim": {
"id": "claim_xyz789",
"status": "archived"
}
}
}
```
**Paiement en cours** — Si un paiement est en cours de traitement sur le sinistre, l'annulation sera refusée avec le code d'erreur `3007`. Attendez la fin du paiement avant de réessayer.
## Codes d'erreur sinistres
Les erreurs sont retournées au format `{ "success": false, "error": { "code": 1000, "message": "..." } }`.
### Erreurs de validation (400 Bad Request)
| Code | Nom | Description |
|------|-----|-------------|
| 1000 | Validation error | Erreur générique de validation (champ manquant, format invalide) |
| 1001 | Questions incomplete | Réponses aux questions manquantes ou invalides |
| 1002 | File too large | Le fichier dépasse la limite de 20 Mo |
| 1003 | Invalid MIME type | Type MIME détecté non autorisé (seuls pdf, jpeg, png, webp, heic, heif sont acceptés) |
| 1004 | Invalid subtype for product | Le sous-type de sinistre n'est pas valide pour ce type de produit |
| 1005 | Invalid base64 content | Le contenu base64 est mal encodé ou vide |
| 1006 | Unsupported content type | Le Content-Type de la requête n'est ni `multipart/form-data` ni `application/json` |
| 1007 | Invalid type for subtype | Le type de sinistre n'est pas compatible avec le sous-type |
| 1008 | Unsupported product type | Le type de produit n'est pas supporté pour la déclaration de sinistre |
### Erreurs de ressource (404 Not Found)
| Code | Nom | Description |
|------|-----|-------------|
| 2001 | Claim not found | Sinistre introuvable (ou non autorisé pour votre clé API) |
| 2002 | Contract not found | Contrat introuvable |
| 2003 | Product not found | Produit introuvable dans le contrat |
| 2004 | Document not found | Document introuvable |
### Erreurs métier (409 Conflict)
| Code | Nom | Description |
|------|-----|-------------|
| 3001 | Claim not editable | Le sinistre n'est pas modifiable (pas en statut draft) |
| 3002 | Claim not submittable | Le sinistre a déjà été soumis ou annulé |
| 3003 | Claim status locked | Le statut du sinistre est verrouillé (ex: déjà annulé) |
| 3004 | Documents incomplete | Des documents obligatoires sont manquants pour la soumission |
| 3005 | Document already validated | Le document a déjà été accepté et ne peut pas être remplacé |
| 3007 | Active payment blocking | Un paiement est en cours, impossible d'annuler le sinistre |
Pour la stratégie de retry et le format général des erreurs, consultez le guide [gestion des erreurs](/docs/guides/error-handling).
## Flux complet résumé
```
POST /claims — Créer la pré-déclaration (status: draft)
PUT /claims/{id}/documents — Uploader un document (1 par requête)
PUT /claims/{id}/documents?autoSubmit=true — Uploader + auto-soumission si complet
GET /claims/{id} — Vérifier canSubmit = true
POST /claims/{id}/submit — Soumettre (status: submitted)
GET /claims/{claimId}/documents/{docId} — Obtenir l'URL signée d'un document
DELETE /claims/{id} — Annuler le sinistre
```
## Prochaine étape
Votre sinistre est soumis. Consultez le [flux des sinistres](/docs/concepts/claims-workflow) pour comprendre les statuts suivants et les [webhooks](/docs/concepts/webhooks) pour recevoir des notifications de progression.
---
# Créer votre premier contrat (/docs/getting-started/first-contract)
Tutoriel pas à pas pour créer un contrat de location courte durée via l'API Tulip.
Ce tutoriel vous guide pas à pas pour créer votre premier contrat via l'API Tulip. Nous commençons par un contrat de location courte durée (LCD) pour un vélo, puis nous montrons un exemple LLD plus complet.
## Prérequis
- Votre [clé API](/docs/getting-started/authentication) sandbox ou production
- Un `uid` (identifiant utilisateur dans votre système)
- Un `product_id` si vous référencez un produit existant dans votre catalogue Tulip, **ou** les informations produit complètes pour une définition en ligne
## Types de contrat et durées
| Type | Nom | Durée autorisée |
|------|-----|-----------------|
| **LCD** | Location Courte Durée | 1 à 31 jours |
| **LMD** | Location Moyenne Durée | 1 à 12 mois |
| **LLD** | Location Longue Durée | 12 à 60 mois |
La durée entre `start_date` et `end_date` doit respecter les bornes ci-dessus. En dehors de ces limites, l'API rejettera la requête.
## Options disponibles
Les options définissent les garanties et le contexte du contrat. Voici la liste complète des valeurs acceptées :
`break`, `theft`, `assistance`, `home_to_work`, `pro`, `rc`, `ia`, `sharing`, `transportation`, `individual`, `company`, `loa`, `no_deductible`, `client_theft`
Les options **`break`** et **`theft`** sont **obligatoires** pour tout contrat. Sans elles, la création échoue avec l'erreur 1009.
Pour le détail de chaque option, consultez [Options de contrat](/docs/concepts/contract-options).
## Étape 1 : Vérifier vos produits disponibles
Si vous utilisez le catalogue Tulip, récupérez vos produits pour noter le `product_id` :
```bash
curl -X GET https://api.mytulip.io/v2/products \
-H "key: YOUR_API_KEY"
```
Si vous préférez définir le produit directement dans le contrat (style v2, sans catalogue), vous pouvez passer cette étape. Voir l'[Exemple 2](#exemple-2--lcd-avec-produit-en-ligne-v2) ci-dessous.
## Étape 2 : Créer le contrat
### Exemple 1 : LCD avec produit du catalogue
Ce premier exemple référence un produit existant via son `product_id` :
```bash
curl -X POST https://api.mytulip.io/v2/contracts \
-H "key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uid": "usr_jean_dupont_01",
"contract_type": "LCD",
"options": ["break", "theft"],
"start_date": "2026-04-01T08:00:00.000Z",
"end_date": "2026-04-03T18:00:00.000Z",
"products": [
{
"product_id": "prod_velo_elec_001",
"data": {
"user_name": "Jean Dupont",
"product_marked": "MARK-VE-2026-001"
}
}
]
}'
```
### Exemple 2 : LCD avec produit en ligne (v2)
Ce deuxième exemple définit le produit directement dans la requête, sans passer par le catalogue. Vous devez fournir `product_type`, `product_subtype`, `value_excl`, `brand` et `model` :
```bash
curl -X POST https://api.mytulip.io/v2/contracts \
-H "key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uid": "usr_jean_dupont_01",
"contract_type": "LCD",
"options": ["break", "theft"],
"start_date": "2026-04-10T09:00:00.000Z",
"end_date": "2026-04-12T18:00:00.000Z",
"products": [
{
"product_type": "bike",
"product_subtype": "electric",
"value_excl": 2500,
"brand": "VanMoof",
"model": "S5",
"data": {
"user_name": "Jean Dupont",
"product_marked": "MARK-VM-S5-042"
}
}
]
}'
```
### Paramètres obligatoires
| Paramètre | Description |
|-----------|-------------|
| `uid` | Identifiant unique de l'utilisateur dans votre système |
| `contract_type` | Type de contrat : `LCD`, `LMD` ou `LLD` |
| `options` | Options du contrat — `break` et `theft` sont **obligatoires** |
| `start_date` | Date de début au format ISO 8601 (ne peut pas être dans le passé) |
| `end_date` | Date de fin (ne peut pas être antérieure à `start_date`, doit respecter les contraintes de durée du type) |
| `products` | Au moins un produit : soit avec `product_id` (catalogue), soit avec `product_type`, `product_subtype`, `value_excl`, `brand`, `model` (en ligne) |
### Paramètres dans `products[].data`
| Champ | LCD | LMD / LLD | Description |
|-------|-----|-----------|-------------|
| `user_name` | Non | **Oui** | Nom de l'utilisateur du produit |
| `product_marked` | **Oui** | **Oui** | Numéro de marquage / identification du produit |
| `internal_id` | Non | Non | Identifiant interne (votre référence) |
En **LCD**, seul le numéro de marquage (`product_marked`) est obligatoire. À partir du **LMD**, le nom de l'utilisateur du matériel (`user_name`) devient également requis.
## Étape 3 : Vérifier la réponse
Une création réussie retourne un objet contrat avec un `cid` (identifiant unique du contrat) :
```json
{
"status": "success",
"execution_id": "exec_7f2a9b3e",
"contract": {
"cid": "01JQXK8VN4TZMW3",
"uid": "usr_jean_dupont_01",
"status": "open",
"contract_type": "LCD",
"start_date": "2026-04-01T08:00:00.000Z",
"end_date": "2026-04-03T18:00:00.000Z",
"options": ["break", "theft"],
"products": [
{
"cpid": "cpid_8e4f1a2b",
"product_id": "prod_velo_elec_001",
"status": "open",
"data": {
"user_name": "Jean Dupont",
"product_marked": "MARK-VE-2026-001"
}
}
],
"price": {
"total_excl": 4.50,
"total_incl": 5.40,
"currency": "EUR"
}
}
}
```
Conservez le `cid` — vous en aurez besoin pour modifier le contrat ou [déclarer des sinistres](/docs/getting-started/first-claim).
## Règle `individual` / `company` selon le type de contrat
L'option `individual` ou `company` dans le tableau `options` est **soumise à des règles strictes** selon le type de contrat :
| Type de contrat | Règle |
|-----------------|-------|
| **LCD** | `individual` / `company` **ne doit pas** être envoyé dans les options |
| **LMD < 6 mois** | `individual` / `company` **ne doit pas** être envoyé dans les options |
| **LMD ≥ 6 mois** | `individual` ou `company` **doit** être présent dans les options |
| **LLD** | `individual` ou `company` **doit** être présent dans les options |
## Exemple avancé : LLD avec informations particulier
Pour un contrat longue durée (LLD) destiné à un particulier, ajoutez l'option `individual` et l'objet `individual` avec les coordonnées complètes :
```bash
curl -X POST https://api.mytulip.io/v2/contracts \
-H "key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uid": "usr_marie_martin_07",
"contract_type": "LLD",
"options": ["break", "theft", "individual"],
"start_date": "2026-05-01T00:00:00.000Z",
"end_date": "2028-04-30T23:59:59.000Z",
"individual": {
"first_name": "Marie",
"last_name": "Martin",
"phone_number": "+33612345678",
"email": "marie.martin@example.com",
"address": "15 rue de Rivoli",
"zipcode": "75001",
"city": "PARIS-1ER-ARRONDISSEMENT",
"country": "FR"
},
"products": [
{
"product_type": "bike",
"product_subtype": "electric",
"value_excl": 3200,
"brand": "Moustache",
"model": "Samedi 27 Xroad 3",
"data": {
"user_name": "Marie Martin",
"product_marked": "MARK-MOU-2026-107"
}
}
]
}'
```
**Format de ville normalisé** — Le champ `city` doit utiliser le format normalisé retourné par l'endpoint [GET /geo/cities](/docs/api-reference/geo/getCitiesByZipcode). Par exemple : `PARIS-1ER-ARRONDISSEMENT`, `LYON-2EME-ARRONDISSEMENT`, `MARSEILLE-1ER-ARRONDISSEMENT`. Utilisez cet endpoint pour obtenir la valeur exacte à partir du code postal.
## Prévisualisation
Ajoutez `?preview=true` pour simuler la création sans réellement créer le contrat. La réponse contient les champs calculés (prix, garanties, détails produits) tels qu'ils seraient si l'opération était effectuée. Le champ `cid` sera `undefined` dans la réponse.
```bash
curl -X POST "https://api.mytulip.io/v2/contracts?preview=true" \
-H "key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uid": "usr_jean_dupont_01",
"contract_type": "LCD",
"options": ["break", "theft"],
"start_date": "2026-04-01T08:00:00.000Z",
"end_date": "2026-04-03T18:00:00.000Z",
"products": [
{
"product_type": "bike",
"product_subtype": "electric",
"value_excl": 2500,
"brand": "VanMoof",
"model": "S5",
"data": {
"user_name": "Jean Dupont",
"product_marked": "MARK-VM-S5-042"
}
}
]
}'
```
Utile pour valider les données et vérifier le prix avant de confirmer.
## Contrats de test
Ajoutez `"test": true` dans le corps de la requête pour créer un **contrat de test**. Les contrats de test se comportent normalement mais sont exclus des flux financiers et de la facturation. Ils sont filtrables via `?test=true` dans les endpoints de liste. Pour plus de détails, consultez le [guide d'environnement de test](/docs/guides/testing-sandbox).
```json
{
"uid": "usr_test_01",
"contract_type": "LCD",
"test": true,
"options": ["break", "theft"],
"start_date": "2026-04-01T08:00:00.000Z",
"end_date": "2026-04-02T18:00:00.000Z",
"products": [...]
}
```
## Points d'attention
**Fenêtre de modification de 4 heures** — Après 4 heures suivant la `start_date`, les champs `start_date` et `end_date` ne peuvent plus être modifiés. Dans cette même fenêtre, la suppression du contrat produit une annulation (`cancel`). Après, elle produit une résiliation (`terminated`) qui nécessite un champ `reason`. Assurez-vous que les informations sont correctes dès la création.
**Gel des informations entreprise/particulier** — Les objets `company` et `individual` sont gelés dès que la `start_date` est passée. Vérifiez ces informations avant le démarrage du contrat.
## Prochaine étape
Votre contrat est créé. Vous pouvez maintenant [déclarer votre premier sinistre](/docs/getting-started/first-claim).
---
# Démarrage rapide (/docs/getting-started)
Tout ce dont vous avez besoin pour commencer à utiliser l'API Tulip en quelques minutes.
Ce guide vous accompagne de l'obtention de votre clé API jusqu'à la création de votre premier contrat et sinistre.
## Étapes
1. **[Authentification](/docs/getting-started/authentication)** — Obtenir et configurer votre clé API
2. **[Environnements](/docs/getting-started/environments)** — Comprendre sandbox et production
3. **[Espace développeur](/docs/getting-started/developer-space)** — Gérer clés API, logs et webhooks depuis Colibri
4. **[Conventions API](/docs/getting-started/conventions)** — Formats, pagination, structure des réponses
5. **[Premier contrat](/docs/getting-started/first-contract)** — Tutoriel pas à pas pour créer un contrat
6. **[Premier sinistre](/docs/getting-started/first-claim)** — Déclarer un sinistre, uploader des documents et soumettre
## Prérequis
- Une clé API Tulip (fournie par votre contact commercial)
- Un accès à l'interface [Colibri](app.mytulip.io) avec le rôle propriétaire d'API
- Un outil pour effectuer des requêtes HTTP (curl, Postman, ou votre langage de programmation)
---
# B2B vs B2C (/docs/guides/b2b-vs-b2c)
Différences entre contrats entreprise et contrats particulier — options, validations et informations requises.
Les contrats Tulip distinguent deux contextes : les contrats pour **entreprises** (B2B) et pour **particuliers** (B2C). Cette distinction impacte les options requises, les informations à fournir et certaines règles de validation.
## Quand utiliser quoi ?
| Contexte | Option | Cas d'usage |
|----------|--------|-------------|
| **Entreprise** | `company` | Le locataire est une entreprise (location de flotte, équipement pro) |
| **Particulier** | `individual` | Le locataire est un particulier |
Pour les contrats **LCD** (courte durée), ces options sont souvent optionnelles. Pour les **LMD** et **LLD**, l'une des deux est généralement requise.
## Liste complète des options
### Options obligatoires pour tous les contrats
| Option | Description |
|--------|-------------|
| `break` | Garantie casse |
| `theft` | Garantie vol |
Pour la liste complète des options et leurs règles de compatibilité (couplage casse/vol, seuil LMD 6 mois, options par type de produit), consultez la page [Options de contrat](/docs/concepts/contract-options).
### Options de contexte (LMD / LLD)
| Option | Description | Contexte |
|--------|-------------|----------|
| `company` | Contrat pour une entreprise | Requis pour LMD/LLD avec entreprise (B2B) |
| `individual` | Contrat pour un particulier | Requis pour LMD/LLD avec particulier (B2C) |
### Options d'usage (LMD ≥ 6 mois et LLD)
| Option | Description |
|--------|-------------|
| `home_to_work` | Trajets domicile-travail |
| `pro` | Usage professionnel (hors transporteur) |
| `transporter` | Usage transporteur |
**Option d'usage requise en LLD** -- Pour les contrats LLD, une option d'usage doit accompagner `company` ou `individual`. Son absence déclenche l'erreur **1015**. Pour les contrats LMD de 6 mois ou plus, les options d'usage sont disponibles mais pas obligatoires. Pour les LMD de moins de 6 mois, l'usage `home_to_work` est appliqué automatiquement.
Les options d'usage sont réservées aux produits **vélo** (`bike`). Consultez la page [Éligibilité et compatibilité](/docs/concepts/contract-eligibility) pour les matrices complètes.
### Options facultatives
| Option | Description |
|--------|-------------|
| `assistance` | Assistance (dépréciée -- préférez `assistance_premium` ou `assistance_standard`) |
| `rc` | Responsabilité civile |
| `ia` | Individuel accident |
| `sharing` | Produits partagés entre utilisateurs |
| `transportation` | Transport |
| `loa` | Location avec option d'achat |
| `no_deductible` | Suppression de la franchise |
| `client_theft` | Vol par le client |
## Informations entreprise
```json
{
"company": {
"company_name": "Location Vélos Paris",
"siren": "123456789",
"first_name": "Marie",
"last_name": "Martin",
"address": "10 rue de la Paix",
"zipcode": "75002",
"city": "PARIS-2EME-ARRONDISSEMENT",
"country": "FR"
}
}
```
| Champ | Obligatoire | Notes |
|-------|-------------|-------|
| `company_name` | Oui | Raison sociale |
| `siren` | Recommandé | Exactement 9 caractères pour la France, exactement 11 caractères pour l'Italie |
| `first_name` | Oui (LLD) | Contact principal |
| `last_name` | Oui (LLD) | Contact principal |
| `address` | Oui | Adresse postale |
| `zipcode` | Oui | Code postal |
| `city` | Oui | Ville au format normalisé (voir [API Geo](/docs/api-reference/geo/getCitiesByZipcode)) |
| `country` | Oui | Code pays : `"FR"` pour la France, `"IT"` pour l'Italie |
## Informations particulier
```json
{
"individual": {
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean@example.com",
"address": "5 avenue des Champs-Élysées",
"zipcode": "75001",
"city": "PARIS-1ER-ARRONDISSEMENT",
"country": "FR"
}
}
```
| Champ | Obligatoire | Notes |
|-------|-------------|-------|
| `first_name` | Oui | -- |
| `last_name` | Oui | -- |
| `phone_number` | Oui | Format international recommandé |
| `email` | Recommandé | -- |
| `address` | Oui | Adresse postale |
| `zipcode` | Oui | Code postal |
| `city` | Oui | Ville au format normalisé (voir [API Geo](/docs/api-reference/geo/getCitiesByZipcode)) |
| `country` | Oui | Code pays : `"FR"` pour la France, `"IT"` pour l'Italie |
**Valeurs du champ `country`** -- Utilisez `"FR"` pour la France et `"IT"` pour l'Italie. Ce sont les seules valeurs acceptées.
## Gel des informations
**Gel dès le démarrage du contrat** -- Les informations `company` (erreur **1150**) et `individual` (erreur **1151**) sont gelées **dès que le contrat a démarré** (la `start_date` est passée). La validation sous-jacente est `contractHasNotStarted` : si la date de début est dans le passé, ces champs deviennent immutables, sans aucun délai supplémentaire. Vérifiez les données **avant** la date de début du contrat.
## Validation SIREN
Le numéro SIREN est validé selon le pays :
- **France (`FR`)** : exactement **9 caractères**
- **Italie (`IT`)** : exactement **11 caractères**
Un SIREN invalide déclenche l'erreur **1102**.
## Exemple complet B2B -- Contrat LLD avec entreprise
```bash
curl -X POST https://api.mytulip.io/v2/contracts \
-H "key: votre-cle-api" \
-H "Content-Type: application/json" \
-d '{
"uid": "votre-user-id",
"contract_type": "LLD",
"options": ["break", "theft", "company", "home_to_work"],
"start_date": "2026-04-01T00:00:00.000Z",
"end_date": "2027-04-01T00:00:00.000Z",
"company": {
"company_name": "Transport Express SAS",
"siren": "987654321",
"first_name": "Marie",
"last_name": "Martin",
"address": "10 rue de la Paix",
"zipcode": "75002",
"city": "PARIS-2EME-ARRONDISSEMENT",
"country": "FR"
},
"products": [
{
"product_id": "votre-product-id",
"data": {
"product_marked": "FLEET001",
"user_name": "Marie Martin"
}
}
]
}'
```
**Format de ville normalisé** -- La valeur de `city` doit correspondre au format retourné par l'[API Geo](/docs/api-reference/geo/getCitiesByZipcode) (par exemple `"PARIS-2EME-ARRONDISSEMENT"` et non `"Paris"`).
## Exemple complet B2C -- Contrat LLD avec particulier
```bash
curl -X POST https://api.mytulip.io/v2/contracts \
-H "key: votre-cle-api" \
-H "Content-Type: application/json" \
-d '{
"uid": "votre-user-id",
"contract_type": "LLD",
"options": ["break", "theft", "individual", "home_to_work"],
"start_date": "2026-04-01T00:00:00.000Z",
"end_date": "2027-04-01T00:00:00.000Z",
"individual": {
"first_name": "Jean",
"last_name": "Dupont",
"phone_number": "+33612345678",
"email": "jean@example.com",
"address": "12 rue de Rivoli",
"zipcode": "75001",
"city": "PARIS-1ER-ARRONDISSEMENT",
"country": "FR"
},
"products": [
{
"product_id": "votre-product-id",
"data": {
"product_marked": "VELO001",
"user_name": "Jean Dupont"
}
}
]
}'
```
---
# Gestion des erreurs (/docs/guides/error-handling)
Codes d'erreur de l'API, messages et stratégies de résolution.
L'API Tulip retourne des codes d'erreur numériques avec des messages en **anglais**. Chaque erreur appartient à un domaine fonctionnel identifiable par la plage de codes.
## Format des erreurs
Toutes les réponses d'erreur suivent le même format JSON :
```json
{
"success": false,
"error": {
"code": 1050,
"message": "The contract started more than 4 hours ago."
}
}
```
Les messages d'erreur sont retournés en **anglais** par l'API, quel que soit le contexte d'appel.
## Codes d'erreur par domaine
---
### Création de contrat (1000–1022)
Ces erreurs surviennent lors de l'appel de création d'un contrat.
| Code | Message API | Description |
|------|------------|-------------|
| 1000 | `The start_date cannot be in the past.` | La date de début doit être dans le futur. |
| 1001 | `The end_date cannot be earlier than the start_date.` | La date de fin doit être postérieure à la date de début. |
| 1002 | `The duration of the contract is invalid.` | Vérifiez que la durée respecte les limites du type de contrat. |
| 1003 | `You do not have permissions to create this type of contract.` | Votre compte n'a pas les droits pour ce type de contrat. |
| 1004 | `The contract type is invalid.` | Le `contract_type` fourni n'existe pas. |
| 1005 | `An end_date is required.` | Le champ `end_date` est obligatoire. |
| 1006 | `A contract_type is required.` | Le champ `contract_type` est obligatoire. |
| 1007 | `At least one product is required to create the contract.` | Ajoutez au moins un produit dans le tableau `products`. |
| 1008 | `Options are required to create a contract.` | Le champ `options` est obligatoire. |
| 1009 | `Options for breakage and theft are required to create a contract, including whether the insured is a company or an individual.` | Spécifiez les options casse/vol et le type d'assuré (entreprise ou particulier). |
| 1010 | `The options for the contract are invalid.` | Vérifiez la structure et les valeurs du champ `options`. |
| 1011 | `The document does not exist.` | Le document référencé est introuvable. |
| 1012 | `A uid is required to create a contract.` | Le champ `uid` est obligatoire. |
| 1013 | `A company is required to create this contract.` | Ce type de contrat nécessite un objet `company`. |
| 1014 | `An individual is required to create this contract.` | Ce type de contrat nécessite un objet `individual`. |
| 1015 | `Options for breakage and theft are required to create a contract, specifying whether the insured is a company or an individual, and defining the use case of the product(s): home_to_work, professional, or transporter.` | Spécifiez les options casse/vol, le type d'assuré et le cas d'usage (`home_to_work`, `professional` ou `transporter`). |
| 1016 | `An ia is required to create this contract.` | Le champ `ia` (intermédiaire d'assurance) est obligatoire. |
| 1017 | `The ia is invalid.` | L'identifiant `ia` fourni n'est pas reconnu. |
| 1018 | `Options for breakage and theft are required to create a contract.` | Les options casse/vol sont manquantes. |
| 1019 | `Options for breakage and theft are required to create a contract.` | Les options casse/vol sont manquantes (variante). |
| 1020 | `Options for breakage and theft are required to create a contract, including whether the insured is a company or an individual.` | Spécifiez les options casse/vol et le type d'assuré. |
| 1021 | `The end_date cannot be in the past.` | La date de fin doit être dans le futur. |
| 1022 | `For special cases, please contact Tulip.` | Ce cas particulier nécessite une intervention manuelle de Tulip. |
---
### Modification de contrat (1050–1052)
Ces erreurs surviennent lors de la mise à jour d'un contrat existant.
| Code | Message API | Description |
|------|------------|-------------|
| 1050 | `The contract started more than 4 hours ago.` | Un contrat ne peut plus être modifié 4 heures après son début. |
| 1051 | `The contract does not exist.` | L'identifiant de contrat fourni est introuvable. |
| 1052 | `The contract is not open.` | Seuls les contrats au statut `open` peuvent être modifiés. |
La fenêtre de modification d'un contrat est de **4 heures** après la date de début. Passé ce délai, le code 1050 sera retourné.
---
### Entreprise (1100–1109)
Validation des informations entreprise lors de la création d'un contrat B2B.
| Code | Message API | Description |
|------|------------|-------------|
| 1100 | `A company name is required to create this contract.` | Le champ `name` de l'entreprise est obligatoire. |
| 1101 | `A SIREN number is required to create this contract.` | Le champ `siren` est obligatoire. |
| 1102 | `The SIREN must be 9 characters for France and 11 characters for Italy.` | 9 caractères pour la France, 11 pour l'Italie. |
| 1103 | `A first name is required to create this contract.` | Le prénom du contact est obligatoire. |
| 1104 | `A last name is required to create this contract.` | Le nom du contact est obligatoire. |
| 1105 | `An address is required to create this contract.` | L'adresse est obligatoire. |
| 1106 | `A zipcode is required to create this contract.` / `The zipcode provided is invalid.` | Code postal manquant ou invalide. |
| 1107 | `A city is required to create this contract.` / `The city provided is invalid.` | Ville manquante ou invalide. |
| 1108 | `A country is required to create this contract.` / `The country provided is invalid.` | Pays manquant ou invalide. |
| 1109 | `The company details are invalid.` | Vérifiez l'ensemble des informations entreprise. |
### Gel d'entreprise (1150)
| Code | Message API | Description |
|------|------------|-------------|
| 1150 | `The company informations is frozen.` | Les informations entreprise sont gelées et ne peuvent plus être modifiées. |
### Gel de particulier (1151)
| Code | Message API | Description |
|------|------------|-------------|
| 1151 | `The individual account is frozen.` | Le compte particulier est gelé et ne peut plus être modifié. |
Les codes 1150 et 1151 indiquent un gel administratif. Contactez le support Tulip pour débloquer la situation.
---
### Produits dans un contrat — Création (1200–1230)
Erreurs lors de l'ajout de produits à un contrat.
| Code | Message API | Description |
|------|------------|-------------|
| 1200 | `product_id is required to assign a product to the contract` | Le champ `product_id` est obligatoire. |
| 1201 | `product_id must exist to assign a product to the contract` | Le `product_id` fourni n'existe pas dans le catalogue. |
| 1202 | `You do not have permission to create a contract with this type of product` | Votre compte n'a pas les droits pour ce type de produit. |
| 1203 | `The document does not exist` | Le document référencé est introuvable. |
| 1220 | `data is required when the product is a bike/winter sports/water sports/event/small tool` | Le champ `data` est obligatoire pour ce type de produit. Le message précise le type concerné. |
| 1221 | `The products are invalid` | Vérifiez la structure du tableau `products`. |
| 1222 | `product_type is required to create this contract` | Le champ `product_type` est obligatoire (définition inline). |
| 1223 | `product_type is invalid` | Le `product_type` fourni n'est pas reconnu. |
| 1224 | `product_subtype is required to create this contract` | Le champ `product_subtype` est obligatoire (définition inline). |
| 1225 | `product_subtype is invalid` | Le `product_subtype` fourni n'est pas reconnu. |
| 1226 | `value_excl is required to create this contract` | Le champ `value_excl` (valeur HT) est obligatoire. |
| 1227 | `value_excl must be positive` | La valeur HT doit être strictement positive. |
| 1228 | `brand is required to create this contract` | Le champ `brand` (marque) est obligatoire. |
| 1229 | `model is required to create this contract` | Le champ `model` (modèle) est obligatoire. |
| 1230 | `value_excl must be less or equal to 15000` | La valeur HT ne doit pas dépasser 15 000. |
Le code **1220** est partagé par plusieurs types de produits (vélo, sports d'hiver, sports nautiques, événement, petit outillage). Le message retourné précise le type concerné.
---
### Produits dans un contrat — Données vélo (1300–1301)
| Code | Message API | Description |
|------|------------|-------------|
| 1300 | `user_name is required to create this contract` | Le nom de l'utilisateur du vélo est obligatoire dans `data`. |
| 1301 | `product_marked is required to create this contract` | Le marquage du produit est obligatoire dans `data`. |
### Produits dans un contrat — Données sports d'hiver (1310–1311)
| Code | Message API | Description |
|------|------------|-------------|
| 1310 | `user_name is required to create this contract` | Le nom de l'utilisateur est obligatoire dans `data`. |
| 1311 | `product_marked is required to create this contract` | Le marquage du produit est obligatoire dans `data`. |
### Produits dans un contrat — Données sports nautiques (1320–1321)
| Code | Message API | Description |
|------|------------|-------------|
| 1320 | `user_name is required to create this contract` | Le nom de l'utilisateur est obligatoire dans `data`. |
| 1321 | `product_marked is required to create this contract` | Le marquage du produit est obligatoire dans `data`. |
---
### Produits dans un contrat — Modification (1330–1343)
Erreurs lors de la mise à jour d'un produit rattaché à un contrat.
| Code | Message API | Description |
|------|------------|-------------|
| 1330 | `The cpid does not exist` | L'identifiant produit-contrat (`cpid`) est introuvable. |
| 1331 | `The product status is not allowed` | Le statut actuel du produit ne permet pas cette modification. |
| 1332 | `user_name cannot be empty` | Le champ `user_name` ne peut pas être vide. |
| 1333 | `internal_id cannot be empty` | Le champ `internal_id` ne peut pas être vide. |
| 1334 | `product_marked cannot be empty` | Le champ `product_marked` ne peut pas être vide. |
| 1335 | `product_marked must be a string` | Le champ `product_marked` doit être une chaîne de caractères. |
| 1336 | `user_name must be a string` | Le champ `user_name` doit être une chaîne de caractères. |
| 1337 | `internal_id must be a string` | Le champ `internal_id` doit être une chaîne de caractères. |
| 1338 | `You are not authorized to update the start_date or end_date` | Votre compte n'a pas les droits pour modifier les dates du produit. |
| 1339 | `The start_date cannot be earlier than the contract's start_date` | La date de début du produit ne peut pas précéder celle du contrat. |
| 1340 | `The start_date cannot be later than the contract's end_date` | La date de début du produit ne peut pas dépasser la date de fin du contrat. |
| 1341 | `The end_date cannot be earlier than the contract's start_date` | La date de fin du produit ne peut pas précéder la date de début du contrat. |
| 1342 | `The end_date cannot be later than the contract's end_date` | La date de fin du produit ne peut pas dépasser celle du contrat. |
| 1343 | `The start_date cannot be later than the product's start_date` | La nouvelle date de début ne peut pas être postérieure à la date de début actuelle du produit. |
---
### Produits dans un contrat — Suppression (1350–1352)
| Code | Message API | Description |
|------|------------|-------------|
| 1350 | `The product does not exist, or is closed, or has been terminated` | Le produit est introuvable, déjà fermé ou résilié. |
| 1351 | `The end_date cannot be in the past` | La date de fin de résiliation doit être dans le futur. |
| 1352 | `The end_date cannot be in the future of the contract` | La date de fin du produit ne peut pas dépasser celle du contrat. |
---
### Particulier (1400–1410)
Validation des informations du particulier lors de la création d'un contrat B2C.
| Code | Message API | Description |
|------|------------|-------------|
| 1400 | `A first_name is required to create this contract.` | Le prénom est obligatoire. |
| 1401 | `A last_name is required to create this contract.` | Le nom est obligatoire. |
| 1402 | `An address is required to create this contract.` | L'adresse est obligatoire. |
| 1403 | `A zipcode is required to create this contract.` | Le code postal est obligatoire. |
| 1404 | `A city is required to create this contract.` | La ville est obligatoire. |
| 1405 | `A phone_number is required to create this contract.` | Le numéro de téléphone est obligatoire. |
| 1406 | `A country is required to create this contract.` | Le pays est obligatoire. |
| 1407 | `The zipcode is invalid.` | Le code postal fourni n'est pas valide. |
| 1408 | `The city is invalid.` | La ville fournie n'est pas valide. |
| 1409 | `The country is invalid.` | Le pays fourni n'est pas valide. |
| 1410 | `The individual details are invalid.` | Vérifiez l'ensemble des informations du particulier. |
---
### Locataires (2000–2002)
Erreurs liées à l'ajout d'un locataire.
| Code | Message API | Description |
|------|------------|-------------|
| 2000 | `The renter_id is required.` | Le champ `renter_id` est obligatoire. |
| 2001 | `Invalid renter_id.` | L'identifiant du locataire n'est pas valide. |
| 2002 | `The renter_id is already registered.` | Ce locataire est déjà enregistré sur le contrat. |
---
### Produits — CRUD (4000–4999)
Erreurs liées aux opérations sur l'endpoint `/products` (création, lecture, mise à jour, suppression de produits dans le catalogue).
#### Création de produit (4000–4022)
| Code | Message API | Description |
|------|------------|-------------|
| 4000 | `A uid is required to create a product.` | Le champ `uid` est obligatoire. |
| 4001 | `A product_type is required to create a product.` | Le champ `product_type` est obligatoire. |
| 4002 | `A title is required to create a product.` | Le champ `title` est obligatoire. |
| 4003 | `Data is required to create a product.` | Le champ `data` est obligatoire. |
| 4004 | `A value_excl is required to create a product.` | Le champ `value_excl` (valeur HT) est obligatoire. |
| 4005 | `A product_subtype is required to create a product.` | Le champ `product_subtype` est obligatoire. |
| 4006 | `A brand is required to create a product.` | Le champ `brand` est obligatoire. |
| 4007 | `A model is required to create a product.` | Le champ `model` est obligatoire. |
| 4008 | `The provided product_type is invalid.` | Le `product_type` fourni n'est pas reconnu. |
| 4009 | `The provided product_subtype is invalid.` | Le `product_subtype` fourni n'est pas reconnu. |
| 4011 | `The uid should be a string.` | Le champ `uid` doit être une chaîne de caractères. |
| 4012 | `The title should be a string.` | Le champ `title` doit être une chaîne de caractères. |
| 4013 | `The value_excl should be a number.` | Le champ `value_excl` doit être un nombre. |
| 4014 | `The description should be a string.` | Le champ `description` doit être une chaîne de caractères. |
| 4015 | `The brand should be a string.` | Le champ `brand` doit être une chaîne de caractères. |
| 4016 | `The model should be a string.` | Le champ `model` doit être une chaîne de caractères. |
| 4017 | `The data should be an object.` | Le champ `data` doit être un objet JSON. |
| 4018 | `The product_type should be a string.` | Le champ `product_type` doit être une chaîne de caractères. |
| 4019 | `The product_subtype should be a string.` | Le champ `product_subtype` doit être une chaîne de caractères. |
| 4020 | `You do not have permission to create this product_type.` | Votre compte n'a pas les droits pour ce type de produit. |
| 4021 | `The value_excl must be positive.` | La valeur HT doit être strictement positive. |
| 4022 | `The purchased_date must be a date.` | Le champ `purchased_date` doit être une date valide. |
#### Mise à jour de produit (4100–4104)
| Code | Message API | Description |
|------|------------|-------------|
| 4100 | `A product_id is required to update a product.` | Le champ `product_id` est obligatoire pour la mise à jour. |
| 4101 | `The value_excl must be a number.` | Le champ `value_excl` doit être un nombre. |
| 4102 | `The description must be a string.` | Le champ `description` doit être une chaîne de caractères. |
| 4103 | `The title must be a string.` | Le champ `title` doit être une chaîne de caractères. |
| 4104 | `The purchased_date must be a date.` | Le champ `purchased_date` doit être une date valide. |
#### Lecture de produit (4200)
| Code | Message API | Description |
|------|------------|-------------|
| 4200 | `A product_id is required to read a product.` | Le champ `product_id` est obligatoire pour la lecture. |
#### Suppression de produit (4300)
| Code | Message API | Description |
|------|------------|-------------|
| 4300 | `A product_id is required to delete a product.` | Le champ `product_id` est obligatoire pour la suppression. |
#### Produit non trouvé (4999)
| Code | Message API | Description |
|------|------------|-------------|
| 4999 | `The product was not found.` | Le produit référencé n'existe pas dans le catalogue. |
---
### Renouvellement automatique (5000–5114)
Erreurs liées à la gestion du renouvellement automatique des contrats.
| Code | Message API | Description |
|------|------------|-------------|
| 5000 | `Automatic renewal not found.` | Le renouvellement automatique n'existe pas pour ce contrat. |
| 5050 | `This contract is locked.` | Le contrat est verrouillé et ne peut pas être modifié. |
| 5100 | `The default application is not modifiable.` | L'application par défaut ne peut pas être modifiée. |
| 5101 | `Applied status is not modifiable.` | Le statut d'application ne peut pas être modifié. |
| 5102 | `Applied date is not modifiable.` | La date d'application ne peut pas être modifiée. |
| 5103 | `Renewal scheduled date is not modifiable.` | La date de renouvellement planifiée ne peut pas être modifiée. |
| 5104 | `Notified status is not modifiable.` | Le statut de notification ne peut pas être modifié. |
| 5105 | `Notification date is not modifiable.` | La date de notification ne peut pas être modifiée. |
| 5106 | `Notification scheduled date is not modifiable.` | La date de notification planifiée ne peut pas être modifiée. |
| 5107 | `Rejection date is not modifiable.` | La date de rejet ne peut pas être modifiée. |
| 5108 | `Extension is not modifiable.` | L'extension ne peut pas être modifiée. |
| 5109 | `Next CID is not modifiable.` | L'identifiant du prochain contrat ne peut pas être modifié. |
| 5110 | `Rejection status is not modifiable.` | Le statut de rejet ne peut pas être modifié. |
| 5112 | `Not modifiable because the contract's end date is in the past.` | Le contrat est expiré, aucune modification n'est possible. |
| 5113 | `Not modifiable because the automatic renewal has already been applied.` | Le renouvellement a déjà été appliqué. |
| 5114 | `This contract is locked.` | Le contrat est verrouillé (variante). |
Les champs du renouvellement automatique (5100–5110) sont en lecture seule une fois le processus enclenché. Seul le champ `rejected` peut encore être modifié tant que le renouvellement n'a pas été appliqué.
---
### Sinistres — Validation (1000–1008)
Ces erreurs sont dans le contexte de l'API Claims (différent des codes contrats dans la même plage).
| Code | Message API | Description |
|------|------------|-------------|
| 1000 | `Validation error` | Erreur générique de validation du corps de la requête. |
| 1001 | `Questions validation failed. Missing: ... Invalid: ...` | Les réponses aux questions obligatoires sont incomplètes ou invalides. |
| 1002 | `File too large` | Le fichier dépasse la taille maximale de 20 Mo. |
| 1003 | `Invalid MIME type` | Le type de fichier n'est pas supporté. Types acceptés : PDF, JPEG, PNG, WebP, HEIC, HEIF. |
| 1004 | `Invalid subtype for product type` | Le sous-type de sinistre n'est pas valide pour ce type de produit. |
| 1005 | `Invalid base64 content` | Le contenu base64 fourni est invalide. |
| 1006 | `Unsupported content type` | Le Content-Type de la requête n'est pas supporté. |
| 1007 | `Invalid type for subtype` | Le type de sinistre n'est pas compatible avec le sous-type. |
| 1008 | `Unsupported product type` | Le type de produit n'est pas supporté pour les sinistres. |
### Sinistres — Ressources introuvables (2001–2004)
| Code | Message API | Description |
|------|------------|-------------|
| 2001 | `Claim not found` | Le sinistre n'existe pas ou vous n'avez pas les droits d'accès. |
| 2002 | `Contract not found` | Le contrat référencé n'existe pas. |
| 2003 | `Product not found in contract` | Le produit n'existe pas dans le contrat. |
| 2004 | `Document not found` | Le document n'existe pas. |
### Sinistres — Logique métier (3001–3007)
| Code | Message API | Description |
|------|------------|-------------|
| 3001 | `Claim can only be modified in draft status` | Le sinistre ne peut être modifié qu'en statut `draft`. |
| 3002 | `Claim has already been submitted or cancelled` | Le sinistre a déjà été soumis ou annulé. |
| 3003 | `Claim is already abandoned` | Le sinistre est déjà abandonné/archivé. |
| 3004 | `Missing required documents: ...` | Des documents obligatoires manquent pour soumettre. |
| 3005 | `This document has already been validated and cannot be replaced` | Le document a été accepté et ne peut plus être remplacé. |
| 3007 | `Cannot abandon claim: payment in progress` | Un paiement est en cours, impossible d'abandonner. |
### Sinistres — Authentification (9001–9003)
| Code | Message API | Description |
|------|------------|-------------|
| 9001 | `API key is required` | Le header `key` est absent. |
| 9002 | `Invalid API key` | La clé API n'est pas valide. |
| 9003 | `User is not authorized for this API key` | L'utilisateur (`userId`) n'est pas autorisé pour cette clé API. |
Les codes d'erreur **sinistres** (1000–9003) sont dans un namespace distinct des codes contrats (1000–1052). Le contexte d'appel (endpoint `/claims` vs `/contracts`) détermine quelle plage s'applique.
---
### Permissions et limites (98888, 99888)
| Code | Message API | Description |
|------|------------|-------------|
| 98888 | `You do not have permissions.` | Votre compte n'a pas les droits nécessaires pour cette action. Vérifiez les permissions associées à votre clé API. |
| 99888 | `The limit has been reached. You cannot perform this action. Please contact support.` | Vous avez atteint la limite autorisée (rate limit ou quota). Contactez le support Tulip. |
Le code **99888** indique un dépassement de quota. Ce n'est pas une erreur transitoire : il faut contacter le support pour augmenter vos limites.
---
## Stratégie de retry
Toutes les erreurs ne doivent pas être réessayées. Voici les recommandations par code HTTP :
| Code HTTP | Type | Action recommandée |
|-----------|------|-------------------|
| **400** | Erreur de validation | **Ne pas réessayer.** Corrigez le corps de la requête en vous appuyant sur le code d'erreur et le message retournés. |
| **401** | Authentification | **Ne pas réessayer.** Vérifiez votre clé API et ses permissions. |
| **403** | Autorisation | **Ne pas réessayer.** Votre compte n'a pas les droits nécessaires (code 98888). |
| **404** | Ressource introuvable | **Ne pas réessayer.** Vérifiez l'identifiant de la ressource (contrat, produit, etc.). |
| **429** | Rate limit | **Réessayer** avec un backoff exponentiel. Respectez le header `Retry-After` si présent. |
| **500** | Erreur serveur | **Réessayer** avec un backoff exponentiel : 1s, 2s, 4s, 8s (maximum 3 tentatives). |
| **502/503** | Service indisponible | **Réessayer** avec un backoff exponentiel. Attendez au moins 5 secondes avant la première tentative. |
| **Timeout** | Pas de réponse | **Réessayer** avec précaution. Vérifiez d'abord via un appel `GET` si la ressource a été créée pour éviter les doublons. |
### Exemple d'implémentation
```typescript
async function callWithRetry(
fn: () => Promise,
maxRetries = 3
): Promise {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
const status = error?.response?.status;
// Ne pas réessayer les erreurs client
if (status && status >= 400 && status < 500) {
throw error;
}
// Dernière tentative, propager l'erreur
if (attempt === maxRetries) {
throw error;
}
// Backoff exponentiel
const delay = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, delay));
}
}
throw new Error("Unreachable");
}
```
Pour les opérations de création (POST), vérifiez toujours via un GET que la ressource n'a pas été créée avant de réessayer, afin d'éviter les doublons.
---
# Guides d'intégration (/docs/guides)
Guides avancés pour la gestion des erreurs, les tests, les différences B2B/B2C et la migration.
Ces guides couvrent les sujets avancés pour une intégration robuste et complète.
Tous les codes d'erreur, stratégies de retry et debugging.
Différences entre contrats entreprise et particulier.
Stratégies de test, données de simulation et validation.
Changements de version et options dépréciées.
---
# Guide de migration (/docs/guides/migration-guide)
Changements de version, options dépréciées et recommandations de migration.
## Options dépréciées
### `assistance` → `assistance_premium`
L'option `assistance` est **dépréciée**. Utilisez `assistance_premium` ou `assistance_standard` à la place.
| Ancien | Nouveau | Impact |
|--------|---------|--------|
| `assistance` | `assistance_premium` | Couverture premium |
| — | `assistance_standard` | Couverture standard (nouvelle option) |
**Action requise** : Remplacez `assistance` par `assistance_premium` dans vos appels de création de contrat. L'ancienne option reste fonctionnelle pour la compatibilité.
```diff
{
"options": [
"break",
"theft",
- "assistance",
+ "assistance_premium"
]
}
```
## Versioning de l'API
L'API Tulip est actuellement en version **v2**. La version est incluse dans l'URL de base :
```
https://api.mytulip.io/v2
```
## Changements récents
Consultez le [changelog](/docs/resources/changelog) pour l'historique détaillé des modifications.
---
# Tests et validation (/docs/guides/testing-sandbox)
Stratégies de test, contrats de test, prévisualisation et checklist d'intégration.
## Clé de développement
Votre clé de développement utilise la **même URL** que la clé de production (`https://api.mytulip.io/v2`). Les contrats créés avec cette clé n'ont aucun impact financier. Voir [Environnements](/docs/getting-started/environments) pour les détails.
## Contrats de test
Le mode test est un **paramétrage de compte** configuré par Tulip. Lorsqu'il est activé sur votre compte, tous les contrats créés sont automatiquement marqués comme contrats de test — aucun paramètre supplémentaire n'est nécessaire dans vos requêtes.
Contactez Tulip pour activer ou désactiver le mode test sur votre compte.
Les contrats de test sont filtrables via `?test=true` dans les listes.
## Prévisualisation
Utilisez `?preview=true` sur les endpoints de création, modification et résiliation pour simuler l'opération sans l'exécuter :
```bash
# Simuler la création d'un contrat
POST /v2/contracts?preview=true
# Simuler une résiliation
DELETE /v2/contracts/{id}?preview=true
```
La réponse contient le résultat tel qu'il serait, y compris le prix calculé.
## Checklist d'intégration
### Contrats
- [ ] Créer un contrat LCD avec les options minimales (`break`, `theft`)
- [ ] Créer un contrat LMD avec option `company`
- [ ] Créer un contrat LLD avec option `individual`
- [ ] Modifier un contrat (dans la fenêtre de 4h)
- [ ] Résilier un contrat
- [ ] Ajouter/supprimer un produit
- [ ] Tester le mode `preview`
### Sinistres
- [ ] Créer une pré-déclaration (vol total vélo)
- [ ] Uploader des documents (JSON base64)
- [ ] Vérifier `canSubmit` avant soumission
- [ ] Soumettre le sinistre
- [ ] Tester l'annulation (DELETE depuis draft)
- [ ] Récupérer un document avec URL signée
### Erreurs
- [ ] Requête sans clé API → erreur 9001
- [ ] Clé API invalide → erreur 9002
- [ ] Contrat non trouvé → erreur 1051
- [ ] Modification après 4h → erreur 1050
- [ ] Documents incomplets à la soumission → erreur 3004
### Webhooks
- [ ] Configurer un webhook depuis l'[espace développeur](/docs/getting-started/developer-space)
- [ ] Vérifier la réception des événements
- [ ] Tester l'idempotence de votre handler
---
# Développer avec les LLMs (/docs/resources/ai-assistant)
Utilisez la documentation et les APIs Tulip avec vos assistants IA et LLMs.
Vous pouvez utiliser des modèles de langage (LLMs) pour vous aider à développer vos intégrations Tulip. Cette page explique comment accéder à notre documentation dans des formats adaptés aux LLMs et comment connecter vos assistants IA.
## Utiliser la documentation avec les LLMs
### llms.txt
Le fichier [llms.txt](https://llmstxt.org) est un standard qui aide les LLMs à indexer du contenu efficacement, similaire à un sitemap pour les moteurs de recherche. Les outils IA utilisent ce fichier pour comprendre la structure de la documentation et trouver le contenu pertinent.
Nous hébergeons deux fichiers :
- [docs.mytulip.io/llms.txt](https://docs.mytulip.io/llms.txt) — index des pages disponibles
- [docs.mytulip.io/llms-full.txt](https://docs.mytulip.io/llms-full.txt) — contenu complet de la documentation en un seul fichier
### skill.md
La [spécification skill.md](https://agentskills.io/specification) est un format structuré et lisible par les machines qui décrit ce que les agents IA peuvent accomplir avec Tulip. Là où `llms.txt` indique aux agents où trouver l'information, `skill.md` leur indique quelles capacités sont disponibles, quels inputs sont requis et quelles contraintes s'appliquent.
Les agents peuvent installer ce skill via le [CLI skills](https://www.npmjs.com/package/skills) :
```bash
npx skills add @mytulip/tulip-api-skills
```
## Voir aussi
- [Spécification OpenAPI](/openapi.json) — Spec complète de l'API
- [Gestion des erreurs](/docs/guides/error-handling) — Catalogue des codes d'erreur
- [Webhooks](/docs/concepts/webhooks) — Configuration des webhooks
---
# Changelog (/docs/resources/changelog)
Historique des changements de l'API Tulip.
## v2 (version actuelle)
Aucun changement récent à signaler. Cette page sera mise à jour lors des prochaines évolutions de l'API.
---
# Limites de requêtes (/docs/resources/rate-limits)
Limites de taux, quotas et gestion de l'erreur 99888.
## Limites par défaut
Les limites de requêtes sont configurées par clé API. Si vous atteignez une limite, l'API retourne l'erreur **99888** :
```json
{
"success": false,
"error": {
"code": 99888,
"message": "La limite a été atteinte. Vous ne pouvez pas effectuer cette action. Veuillez contacter le support."
}
}
```
## Augmentation des limites
Si vos besoins dépassent les limites par défaut, contactez le [support](/docs/resources/support) pour demander une augmentation.
## Bonnes pratiques
- **Implémentez un backoff exponentiel** pour les erreurs 429 et 99888
- **Mettez en cache** les réponses GET quand c'est possible (produits, loueurs)
- **Utilisez la pagination** pour les listes volumineuses au lieu de tout charger
- **Regroupez les uploads** de documents en une seule requête PUT
---
# SDKs et clients API (/docs/resources/sdks)
Générer un client API à partir de la spécification OpenAPI de Tulip.
Tulip ne fournit pas de SDK officiel pour le moment. L'API REST est conçue pour être appelée directement depuis n'importe quel langage via des requêtes HTTP standard.
## Appels directs
Aucune dépendance n'est nécessaire. Utilisez le client HTTP de votre langage :
```typescript title="TypeScript (fetch)"
const response = await fetch('https://api.mytulip.io/v2/contracts', {
method: 'POST',
headers: {
'key': process.env.TULIP_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
uid: 'user_123',
contract_type: 'LCD',
options: ['break', 'theft'],
start_date: '2026-04-01T08:00:00.000Z',
end_date: '2026-04-03T18:00:00.000Z',
products: [{ product_id: 'prod_001', data: { user_name: 'Jean Dupont', product_marked: 'MARK-001' } }],
}),
});
const { contract } = await response.json();
```
```python title="Python (requests)"
import requests
import os
response = requests.post(
'https://api.mytulip.io/v2/contracts',
headers={'key': os.environ['TULIP_API_KEY']},
json={
'uid': 'user_123',
'contract_type': 'LCD',
'options': ['break', 'theft'],
'start_date': '2026-04-01T08:00:00.000Z',
'end_date': '2026-04-03T18:00:00.000Z',
'products': [{'product_id': 'prod_001', 'data': {'user_name': 'Jean Dupont', 'product_marked': 'MARK-001'}}],
}
)
contract = response.json()['contract']
```
```php title="PHP (cURL)"
$ch = curl_init('https://api.mytulip.io/v2/contracts');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'key: ' . getenv('TULIP_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'uid' => 'user_123',
'contract_type' => 'LCD',
'options' => ['break', 'theft'],
'start_date' => '2026-04-01T08:00:00.000Z',
'end_date' => '2026-04-03T18:00:00.000Z',
'products' => [['product_id' => 'prod_001', 'data' => ['user_name' => 'Jean Dupont', 'product_marked' => 'MARK-001']]],
]),
]);
$response = json_decode(curl_exec($ch), true);
$contract = $response['contract'];
```
## Génération d'un client typé
Si vous souhaitez bénéficier de types et d'autocomplétion, vous pouvez générer un client à partir de la spécification OpenAPI de Tulip :
```
https://docs.mytulip.io/openapi.json
```
### Outils recommandés
| Outil | Langages | Installation |
|-------|----------|-------------|
| [OpenAPI Generator](https://openapi-generator.tech/) | 50+ langages (TypeScript, Python, PHP, Java, Go, etc.) | `npm install @openapitools/openapi-generator-cli -g` |
| [openapi-typescript](https://github.com/drwpow/openapi-typescript) | TypeScript (types uniquement) | `npx openapi-typescript https://docs.mytulip.io/openapi.json -o tulip-api.d.ts` |
| [openapi-fetch](https://openapi-ts.dev/openapi-fetch/) | TypeScript (client typé + fetch) | `npm install openapi-fetch` |
### Exemple avec openapi-typescript + openapi-fetch
```bash
# 1. Générer les types
npx openapi-typescript https://docs.mytulip.io/openapi.json -o src/tulip-api.d.ts
# 2. Installer le client
npm install openapi-fetch
```
```typescript
import createClient from 'openapi-fetch';
import type { paths } from './tulip-api';
const client = createClient({
baseUrl: 'https://api.mytulip.io/v2',
headers: { key: process.env.TULIP_API_KEY },
});
// Autocomplétion et validation de types
const { data, error } = await client.GET('/contracts/{contractId}', {
params: { path: { contractId: '01JQXK8VN4TZMW3' } },
});
```
La combinaison `openapi-typescript` + `openapi-fetch` offre une expérience proche d'un SDK officiel : types générés automatiquement, autocomplétion dans l'IDE, et validation à la compilation — sans dépendance tierce spécifique à Tulip.
---
# Support (/docs/resources/support)
Comment obtenir de l'aide, contacter le support et escalader un problème.
## Contact
| Canal | Coordonnées | Usage |
|-------|-------------|-------|
| **Email** | api-support@mytulip.io | Questions techniques, bugs, demandes de fonctionnalités |
| **Documentation** | Ce portail | Référence API, guides, tutoriels |
## Quand contacter le support
- **Clé API** — Demande, régénération ou révocation
- **Webhooks** — Configuration de votre URL de callback
- **Limites** — Augmentation des quotas de requêtes
- **Erreur 1022** — Cas spéciaux nécessitant une intervention manuelle
- **Bugs** — Comportement inattendu de l'API
## Informations à fournir
Pour accélérer le traitement de votre demande, incluez :
1. **Votre identifiant partenaire** (clé API ou nom du partenaire)
2. **L'endpoint concerné** (méthode + URL)
3. **Le code d'erreur** reçu
4. **Le payload de la requête** (en masquant les données sensibles)
5. **La réponse complète** de l'API
6. **La date et l'heure** de l'incident
## FAQ
### Ma clé API ne fonctionne plus
Vérifiez que vous utilisez la bonne clé pour le bon environnement (sandbox vs production). Si le problème persiste, contactez le support pour vérification.
### J'obtiens l'erreur 99888
Vous avez atteint la limite de requêtes. Voir [Limites de requêtes](/docs/resources/rate-limits).
### Comment tester les webhooks ?
Utilisez un service comme [webhook.site](https://webhook.site) pour recevoir et inspecter les payloads. Voir [Webhooks](/docs/concepts/webhooks).
---
# Annuler ou abandonner un sinistre (/docs/api-reference/claims/cancelClaim)
## DELETE `/claims/{id}`
Annuler ou abandonner un sinistre
Ce endpoint permet d'annuler une pré-déclaration (passage en archived depuis draft) ou d'abandonner un sinistre en cours de traitement.
### Parameters
- `id` (path, string) (required)
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `message`: string (required) — Message de confirmation
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **404**: The server cannot find the requested resource.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Créer un sinistre (pré-déclaration) (/docs/api-reference/claims/createClaim)
## POST `/claims`
Créer un sinistre (pré-déclaration)
Ce endpoint permet de créer une nouvelle pré-déclaration de sinistre en statut brouillon. Les questions obligatoires doivent être renseignées selon le type et sous-type du sinistre.
### Request Body
- `userId`: unknown (required) — L'identifiant de l'utilisateur
- `contractId`: unknown (required) — L'identifiant du contrat associé
- `contractProductId`: string (required) — L'identifiant du produit dans le contrat
- `type`: unknown (required) — Le type de sinistre
- `subtype`: unknown (required) — Le sous-type de sinistre
- `claimAt`: unknown (required) — La date de l'incident
- `description`: string (required) — La description détaillée de l'incident (minimum 50 caractères)
- `questions`: object (required) — Les réponses aux questions obligatoires selon le type/sous-type de sinistre. Les clés sont les questions et les valeurs sont les réponses (texte ou booléen).
- `insured`: unknown (required) — Les informations de l'assuré
- `location`: unknown (required) — Le lieu de l'incident
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: unknown (required) — Le sinistre créé
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Récupérer un sinistre (/docs/api-reference/claims/getClaim)
## GET `/claims/{id}`
Récupérer un sinistre
Ce endpoint permet de récupérer les détails complets d'un sinistre. L'identifiant peut être celui de la pré-déclaration ou du sinistre interne.
### Parameters
- `id` (path, string) (required)
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: unknown (required) — Le sinistre
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **404**: The server cannot find the requested resource.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Récupérer un document (/docs/api-reference/claims/getDocument)
## GET `/claims/{claimId}/documents/{documentId}`
Récupérer un document
Ce endpoint permet de récupérer les détails d'un document avec une URL de téléchargement signée. L'URL expire après 1 heure.
### Parameters
- `claimId` (path, string) (required)
- `documentId` (path, string) (required)
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: unknown (required) — Le document avec URL de téléchargement signée
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **404**: The server cannot find the requested resource.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Lister les sinistres (/docs/api-reference/claims/listClaims)
## GET `/claims`
Lister les sinistres
Ce endpoint permet de récupérer la liste des sinistres d'un utilisateur avec pagination.
### Parameters
- `userId` (query, string) (required)
- `status` (query, string)
- `limit` (query, string)
- `offset` (query, string)
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: array (required) — La liste des sinistres
- `pagination`: object (required) — Les métadonnées de pagination
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Soumettre un sinistre (/docs/api-reference/claims/submitClaim)
## POST `/claims/{id}/submit`
Soumettre un sinistre
Ce endpoint permet de soumettre un sinistre pour traitement. Tous les documents obligatoires doivent avoir été téléchargés. Le sinistre peut aussi être soumis automatiquement lorsque tous les documents obligatoires sont fournis.
### Parameters
- `id` (path, string) (required)
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: unknown (required) — Les détails de la soumission
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **404**: The server cannot find the requested resource.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Mettre à jour un sinistre (/docs/api-reference/claims/updateClaim)
## PATCH `/claims/{id}`
Mettre à jour un sinistre
Ce endpoint permet de mettre à jour la description et/ou le lieu d'un sinistre. La modification n'est possible qu'en statut draft.
### Parameters
- `id` (path, string) (required)
### Request Body
- `description`: string — La description mise à jour de l'incident (minimum 50 caractères)
- `location`: unknown — Le lieu mis à jour de l'incident
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: unknown (required) — Le sinistre mis à jour
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **404**: The server cannot find the requested resource.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Télécharger des documents (/docs/api-reference/claims/uploadDocuments)
## PUT `/claims/{id}/documents`
Télécharger des documents
Ce endpoint permet de télécharger des documents pour un sinistre. Supporte l'upload multipart (formulaire avec fichier) ou JSON (contenu en base64). Le type MIME est détecté automatiquement à partir du contenu du fichier.
### Parameters
- `id` (path, string) (required)
### Request Body
Array< - `type`: string (required) — Le type de document
- `title`: string (required) — Le titre du document
- `filename`: string (required) — Le nom du fichier
- `content`: string (required) — Le contenu du fichier encodé en base64 (base64 brut, sans préfixe data URL)
- `comment`: string — Un commentaire optionnel>
### Responses
- **200**: The request has succeeded.
- `success`: boolean (required) — Indique si l'opération a réussi
- `data`: array (required) — La liste des documents après upload
- **400**: The server could not understand the request due to invalid syntax.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **401**: Access is unauthorized.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
- **404**: The server cannot find the requested resource.
- `success`: boolean (required) — Indique que l'opération a échoué
- `error`: object (required) — Les détails de l'erreur
---
# Actions en masse sur les renouvellements automatiques (/docs/api-reference/contracts/bulkAutomaticRenewals)
## POST `/contracts/action/automaticRenewals`
Actions en masse sur les renouvellements automatiques
Ce endpoint permet d'effectuer des actions en masse sur les renouvellements automatiques de tous vos contrats : ajouter, refuser, activer/désactiver les notifications.
### Request Body
- `add_to_all_contracts`: boolean — Ajouter un renouvellement automatique à tous les contrats
- `reject_on_all_contracts`: boolean — Refuser le renouvellement sur tous les contrats
- `enable_notification_on_all_contracts`: boolean — Activer les notifications sur tous les contrats
- `disable_notification_on_all_contracts`: boolean — Désactiver les notifications sur tous les contrats
### Responses
- **200**: Réponse lors d'actions en masse sur les renouvellements automatiques
- `automatic_renewals`: array (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors d'actions en masse sur les renouvellements automatiques
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Résiler un contrat (/docs/api-reference/contracts/cancelContract)
## DELETE `/contracts/{contractId}`
Résiler un contrat
Ce endpoint permet de résilier ou annuler un contrat. Si le paramètre preview est à true, ceci ne résiliera pas le contrat mais retournera un contrat prévisualisé.
Si le contrat a démarré depuis peu, il sera annulé.
### Parameters
- `preview` (query, string)
- `contractId` (path, string) (required)
### Request Body
- `reason`: TerminateContractReason (required)
- `end_date`: unknown — La date de fin du contrat. Si non fournie, le contrat sera résilié au moment de la résiliation.
Vous pouvez également fournir une date de fin pour le contrat, ce qui permet de résiler le contrat à une date future.
La date ne peut être supérieure à la date de fin du contrat.
### Responses
- **200**: Réponse lors de la résiliation d'un contrat
- `contract`: Contract (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la résiliation d'un contrat
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Créer un renouvellement automatique (/docs/api-reference/contracts/createAutomaticRenewal)
## POST `/contracts/{contractId}/automaticRenewal`
Créer un renouvellement automatique
Ce endpoint permet de créer ou configurer un renouvellement automatique (tacite reconduction) pour un contrat. Le contrat doit être ouvert.
### Parameters
- `contractId` (path, string) (required)
### Request Body
- `use_notification`: boolean — Activer ou désactiver les notifications de renouvellement
- `duration_months`: integer — La durée du renouvellement en mois
### Responses
- **200**: Réponse lors de la création d'un renouvellement automatique
- `automatic_renewal`: AutomaticRenewal (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la création d'un renouvellement automatique
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Créer un contrat (/docs/api-reference/contracts/createContract)
## POST `/contracts`
Créer un contrat
Ce endpoint permet de créer un contrat. Si le paramètre preview est à true, ceci ne créera pas le contrat mais retournera un contrat prévisualisé.
### Parameters
- `preview` (query, string)
### Request Body
- `uid`: UserId (required)
- `start_date`: Date
- `end_date`: Date (required)
- `contract_type`: ContractType (required)
- `options`: array (required)
- `company`: ContractCompany
- `individual`: ContractIndividual
- `test`: ContractTest
- `products`: array (required)
### Responses
- **200**: Réponse lors de la création d'un contrat
- `contract`: Contract (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la création d'un contrat
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Ajouter un produit à un contrat (/docs/api-reference/contracts/createProductFromContract)
## POST `/contracts/{contractId}/products`
Ajouter un produit à un contrat
Ce endpoint permet d'ajouter un produit à un contrat.
### Parameters
- `preview` (query, string)
- `contractId` (path, string) (required)
### Request Body
- `start_date`: unknown (required) — La date de début du nouveau produit.
La date ne peut être supérieure à la date de fin du contrat.
La date ne peut être inférieure à la date de début du contrat ou dans le passé.
- `products`: array (required)
### Responses
- **200**: La réponse de l'ajout d'un produit à un contrat
- `contract`: Contract (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Les erreurs de l'ajout d'un produit à un contrat
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Supprimer un produit d'un contrat (/docs/api-reference/contracts/deleteProductFromContract)
## DELETE `/contracts/{contractId}/products`
Supprimer un produit d'un contrat
Ce endpoint permet de supprimer un produit d'un contrat.
### Parameters
- `preview` (query, string)
- `contractId` (path, string) (required)
### Request Body
- `reason`: TerminateContractReason (required)
- `end_date`: unknown — La date de fin du contrat. Si non fournie, le contrat sera résilié au moment de la résiliation.
Vous pouvez également fournir une date de fin pour le contrat, ce qui permet de résiler le contrat à une date future.
La date ne peut être supérieure à la date de fin du contrat.
- `products`: array (required)
### Responses
- **200**: La réponse de la résiliation d'un produit
- `contract`: Contract (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Les erreurs de la résiliation d'un produit
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Récupérer un renouvellement automatique (/docs/api-reference/contracts/getAutomaticRenewalById)
## GET `/contracts/{contractId}/automaticRenewal/{arid}`
Récupérer un renouvellement automatique
Ce endpoint permet de récupérer un renouvellement automatique spécifique par son identifiant.
### Parameters
- `contractId` (path, string) (required)
- `arid` (path, string) (required)
### Responses
- **200**: Réponse lors de la récupération d'un renouvellement automatique spécifique
- `automatic_renewal`: AutomaticRenewal (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la récupération d'un renouvellement automatique spécifique
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Récupérer le renouvellement automatique actif (/docs/api-reference/contracts/getCurrentAutomaticRenewal)
## GET `/contracts/{contractId}/automaticRenewal/current`
Récupérer le renouvellement automatique actif
Ce endpoint permet de récupérer le renouvellement automatique actuellement actif pour un contrat.
### Parameters
- `contractId` (path, string) (required)
### Responses
- **200**: Réponse lors de la récupération du renouvellement automatique actif
- `automatic_renewal`: AutomaticRenewal (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la récupération du renouvellement automatique actif
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Lister les renouvellements automatiques (/docs/api-reference/contracts/listAutomaticRenewals)
## GET `/contracts/{contractId}/automaticRenewals`
Lister les renouvellements automatiques
Ce endpoint permet de récupérer l'historique de tous les renouvellements automatiques d'un contrat.
### Parameters
- `contractId` (path, string) (required)
### Responses
- **200**: Réponse lors de la récupération des renouvellements automatiques
- `automatic_renewals`: array (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la récupération des renouvellements automatiques
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Récupérer un contrat (/docs/api-reference/contracts/retrieveContract)
## GET `/contracts/{contractId}`
Récupérer un contrat
Ce endpoint permet de récupérer un contrat
### Parameters
- `contractId` (path, string) (required)
### Responses
- **200**: Le contrat
- `contract`: Contract (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la récupération d'un contrat
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: Error1051 (required)
---
# Récupérer un ensemble de contrats (/docs/api-reference/contracts/retrieveContracts)
## GET `/contracts`
Récupérer un ensemble de contrats
Ce endpoint permet de récupérer un ensemble de contrats
### Parameters
- `uid` (query, string) (required)
- `skip` (query, string)
- `limit` (query, string)
- `test` (query, string)
### Responses
- **200**: Les contrats
- `contracts`: array (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la récupération des contrats
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: Error1051 (required)
---
# Mettre à jour un renouvellement automatique (/docs/api-reference/contracts/updateAutomaticRenewal)
## PATCH `/contracts/{contractId}/automaticRenewal/{arid}`
Mettre à jour un renouvellement automatique
Ce endpoint permet de modifier un renouvellement automatique. Seuls les champs use_notification, rejected et duration_months sont modifiables. Le contrat doit être ouvert et le renouvellement ne doit pas avoir été déjà appliqué.
### Parameters
- `contractId` (path, string) (required)
- `arid` (path, string) (required)
### Request Body
- `use_notification`: boolean — Activer ou désactiver les notifications de renouvellement
- `rejected`: boolean — Refuser le renouvellement automatique
- `duration_months`: integer — La durée du renouvellement en mois
### Responses
- **200**: Réponse lors de la mise à jour d'un renouvellement automatique
- `automatic_renewal`: AutomaticRenewal (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la mise à jour d'un renouvellement automatique
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Mettre à jour un contrat (/docs/api-reference/contracts/updateContract)
## PATCH `/contracts/{contractId}`
Mettre à jour un contrat
Ce endpoint permet de mettre à jour un contrat. Si le paramètre preview est à true, ceci ne mettra pas à jour le contrat mais retournera un contrat prévisualisé.
### Parameters
- `preview` (query, string)
- `contractId` (path, string) (required)
### Request Body
- `start_date`: unknown — La date de début du contrat. Si non fournie, la date de début du contrat sera inchangée.
La date ne peut être supérieure à la date de fin du contrat.
Si le contrat a démarré depuis plus de 1h, la date de début ne peut être modifiée.
- `end_date`: unknown — La date de fin du contrat. Si non fournie, la date de fin du contrat sera inchangée.
La date de fin ne peut pas être inférieure ou supérieure à la durée du contrat type.
- `products`: object — Les produits du contrat. Si non fourni, les produits du contrat seront inchangés.
La modification d'un produit crée un nouveau produit, l'ancien produit aura sont statut **has_been_replaced** et contiendra l'identifiant (ContractProductId) dans la propriété **replaced_by** du nouveau produit.
- `company`: unknown — Les informations de l'entreprise. Si non fourni, les informations de l'entreprise seront inchangées.
Si le contrat a commencé depuis plus de 1h, cette propriété ne peuvent être modifiées.
- `individual`: unknown — Les informations du particulier. Si non fourni, les informations du particulier seront inchangées.
Si le contrat a commencé depuis plus de 1h, cette propriété ne peuvent être modifiées.
### Responses
- **200**: La réponse de la mise à jour d'un contrat
- `contract`: Contract (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Les erreurs de la mise à jour d'un contrat
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `error`: unknown (required)
---
# Récupérer les villes par code postal (/docs/api-reference/geo/getCitiesByZipcode)
## GET `/geo/getCitiesByZipcode/{country}/{zipcode}`
Récupérer les villes par code postal
Ce endpoint permet de récupérer les villes correspondant à un code postal pour un pays donné. Les pays supportés sont la France (FR), l'Italie (IT) et la Réunion (incluse dans FR).
### Parameters
- `country` (path, string) (required)
- `zipcode` (path, string) (required)
### Responses
- **200**: La réponse de la récupération des villes par code postal
- `cities`: array (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la récupération des villes par code postal
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
---
# Rechercher des villes par suggestion (/docs/api-reference/geo/getCitiesSuggestions)
## GET `/geo/getCitiesSuggestions/{country}/{zipcode}`
Rechercher des villes par suggestion
Ce endpoint permet de rechercher des villes et codes postaux par recherche partielle (fuzzy search). Utile pour l'auto-complétion d'adresses. Retourne jusqu'à 30 suggestions triées par score de confiance.
### Parameters
- `country` (path, string) (required)
- `zipcode` (path, string) (required)
### Responses
- **200**: La réponse de la recherche de suggestions de villes
- `cities`: array (required)
- `status`: string (required)
- `execution_id`: ApiExecutionId (required)
- **400**: Erreurs lors de la recherche de suggestions de villes
- `execution_id`: ApiExecutionId (required)
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
---
# Créer un produit (/docs/api-reference/products/createProduct)
## POST `/products`
Créer un produit
Ce endpoint permet de créer un nouveau produit.
### Request Body
- `uid`: string (required) — Unique identifier for the product
- `product_type`: ProductType (required)
- `title`: string (required)
- `description`: string
- `data`: object (required)
- `purchased_date`: string
- `value_excl`: ProductValueExcl (required)
### Responses
- **201**: The request has succeeded and a new resource has been created as a result.
- `status`: string (required)
- `product`: Product (required)
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Supprimer un produit (/docs/api-reference/products/deleteProduct)
## DELETE `/products/{productId}`
Supprimer un produit
Ce endpoint permet de supprimer un produit.
### Parameters
- `productId` (path, string) (required)
### Responses
- **204**: There is no content to send for this request, but the headers may be useful.
- `status`: string (required)
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Récupérer un produit (/docs/api-reference/products/retrieveProduct)
## GET `/products/{productId}`
Récupérer un produit
Ce endpoint permet de récupérer les informations d’un produit spécifique.
### Parameters
- `productId` (path, string) (required)
### Responses
- **200**: The request has succeeded.
- `status`: string (required)
- `product`: Product (required)
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Lister les produits (/docs/api-reference/products/retrieveProducts)
## GET `/products`
Lister les produits
Ce endpoint permet de récupérer la liste des produits disponibles.
### Responses
- **200**: The request has succeeded.
Array< - `status`: string (required)
- `products`: array (required)>
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Mettre à jour un produit (/docs/api-reference/products/updateProduct)
## PATCH `/products/{productId}`
Mettre à jour un produit
Ce endpoint permet de mettre à jour un produit existant.
### Parameters
- `productId` (path, string) (required)
### Request Body
- `uid`: string — Unique identifier for the product
- `product_type`: ProductType
- `title`: string
- `description`: string
- `data`: object
- `purchased_date`: string
- `value_excl`: ProductValueExcl
### Responses
- **200**: The request has succeeded.
- `status`: string (required)
- `product`: Product (required)
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Ajouter un loueur (/docs/api-reference/renters/addRenter)
## POST `/renters`
Ajouter un loueur
Ce endpoint permet d’ajouter un loueur à une clé API existante.
### Request Body
string
### Responses
- **201**: The request has succeeded and a new resource has been created as a result.
- `status`: string (required)
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Récupérer un loueur (/docs/api-reference/renters/retrieveRenter)
## GET `/renters/{renterId}`
Récupérer un loueur
Ce endpoint permet de récupérer les informations d'un loueur spécifique.
### Parameters
- `renterId` (path, string) (required)
### Responses
- **200**: The request has succeeded.
- `status`: string (required)
- `renter`: object (required)
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
---
# Lister les loueurs (/docs/api-reference/renters/retrieveRenters)
## GET `/renters`
Lister les loueurs
Ce endpoint permet de récupérer la liste des loueurs disponibles.
### Responses
- **200**: The request has succeeded.
Array< - `status`: string (required)
- `renters`: object (required)>
- **400**: The server could not understand the request due to invalid syntax.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **401**: Access is unauthorized.
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)
- **500**: Server error
- `status`: string (required)
- `type`: string (required)
- `message`: string (required)
- `execution_id`: string (required)