Objets (Livre de police)

L'API Objets permet de gérer les entrées du livre de police numérique : enregistrement d'objets mobiliers, suivi du stock et des ventes, gestion des photos et documents vendeur.

Chaque objet reçoit automatiquement un numéro d'ordre séquentiel, conforme aux exigences réglementaires. La numérotation est propre à chaque établissement : si vous gérez plusieurs établissements, chacun a sa séquence indépendante (1, 2, 3... ou 2026-1, 2026-2... en mode annuel). Les informations du vendeur sont intégrées directement dans l'objet.

Scope — La clé API utilisée détermine l'établissement ciblé. Toutes les opérations (création, lecture, audit) sont scopées au livre de police de cet établissement.

Régimes juridiques

Chaque objet porte un ou plusieurs régimes juridiques qui déterminent les règles applicables (mentions obligatoires, seuils de paiement, obligations déclaratives, etc.). Les régimes disponibles dépendent de l'activité configurée sur l'établissement de la clé API utilisée.

ValeurRégime correspondant
OBJETS_OCCASIONLivre de police — art. 321-7 du Code pénal
METAUX_PRECIEUXRégime de garantie — art. L834-6 du Code de commerce (douanes)
METAUX_FERREUX_ET_NON_FERREUXInterdiction espèces L112-6 CMF + déclaration annuelle 1649 bis CGI

Règles métier :

  • Si le champ regimes est absent ou vide à la création, l'objet reprend automatiquement les regimesActifs configurés sur l'établissement de la clé API.
  • Une valeur qui ne figure pas dans les regimesActifs de l'établissement est refusée : 400 REGIME_NON_ACTIF, avec la liste des régimes actifs dans le champ regimesActifs de la réponse. Un régime détermine des obligations propres (prix d'achat obligatoire, plafonds de paiement en espèces, déclaration annuelle) : l'ignorer silencieusement laisserait croire que l'entrée y est soumise alors qu'elle ne le sera jamais.
  • Les régimes disponibles dépendent de l'activité — un professionnel de l'automobile ne peut pas déclarer METAUX_PRECIEUX. Voir la page Modèles pour la correspondance activité / régimes.
  • En modification (PUT), les régimes déjà portés par l'entrée restent acceptés, même s'ils ont été désactivés sur l'établissement depuis — sans quoi les entrées anciennes deviendraient inéditables.
  • Le régime détermine la validation du prix (voir ci-dessous) et les warnings affichés dans l'UI.

Lister les objets

GET /api/objets

Réponse 200

Retourne un tableau d'objets. Voir le type ObjetMobilier pour la structure complète.


Créer un objet

Enregistre un nouvel objet dans le livre de police. Le numero est attribué automatiquement.

POST /api/objets

Header obligatoire : X-Agent

X-Agent: Marie Dupont

L'arrêté du 15 mai 2020 impose que toute mutation du livre de police (création, modification, suppression, mouvement) soit attribuée à une personne physique identifiable. Comme votre clé API est partagée entre toutes les opérations de votre établissement, vous devez transmettre l'identité de l'agent sur chaque appel d'écriture :

  • POST /api/objets (création d'entrée)
  • PUT /api/objets/:id (modification)
  • DELETE /api/objets/:id (suppression motivée)
  • DELETE /api/objets/:id/photos (retrait d'une photo)
  • POST /api/objets/:id/mouvements (sortie, vente, mise en réparation, etc.)
  • PUT /api/objets/:id/mouvements/:id/resolve (annulation d'un mouvement)

La valeur doit correspondre au prénom et au nom d'un utilisateur de votre établissement, tel qu'enregistré dans son compte Registeo (Prénom Nom, ex. Marie Dupont). Ni identifiant interne, ni pseudo, ni valeur générique du type API ou système : un humain identifiable et joignable en cas de contrôle par les forces de l'ordre.

La comparaison ignore la casse, les accents et les espaces superflus — marie dupont fonctionne. En revanche, le registre enregistre toujours l'orthographe du compte, afin qu'une même personne n'apparaisse jamais sous plusieurs graphies.

SituationRéponse
Header absent400 MISSING_AGENT
Nom ne correspondant à aucun utilisateur de l'établissement400 AGENT_INTROUVABLE

La réponse AGENT_INTROUVABLE liste les valeurs acceptées, dans son message et dans un champ agentsAutorises :

{
  "success": false,
  "code": "AGENT_INTROUVABLE",
  "message": "X-Agent « API » ne correspond à aucun utilisateur de l'établissement. Valeurs acceptées : Marie Dupont, Paul Bernard.",
  "agentsAutorises": ["Marie Dupont", "Paul Bernard"]
}

Pour qu'un nouvel opérateur puisse être désigné, ajoutez-le comme utilisateur de l'établissement depuis « Mon compte », en renseignant son prénom et son nom.

Ces contrôles sont délibérés : votre intégration ne peut pas s'en exonérer. La traçabilité légale repose dessus, et chaque entrée du journal d'audit porte le nom de l'agent ayant effectué la modification.

Champs principaux

ChampTypeRequisDescription
typeOperationstringNonachat (défaut), dépôt, reprise, échange. Certaines activités n'en admettent qu'une partie — un récupérateur de métaux achète, il ne prend pas en dépôt. Une opération hors périmètre est refusée en 400 OPERATION_NON_AUTORISEE, avec la liste des opérations admises.
designationstringOuiDescription de l'objet
marquesstringNonMarques, signes distinctifs, état
provenanceDeclareestringNonProvenance déclarée par le vendeur
quantitenumberNonQuantité (défaut : 1)
prixAchatnumberConditionnelPrix d'achat en euros. Obligatoire si les régimes de l'objet incluent OBJETS_OCCASION ou METAUX_FERREUX_ET_NON_FERREUX. Facultatif sinon.
modePaiementstringNonvirement, chèque, espèces, carte, autre. Les espèces sont contrôlées — voir ci-dessous.
dateAchatstringOuiDate d'achat (ISO 8601)
regimesstring[]NonRégimes juridiques applicables à cette entrée. Voir Régimes juridiques. Défaut : régimes actifs de l'établissement.
champsActiviteobjectNonChamps spécifiques à l'activité (ex : IMEI, VIN, poinçon, poids/titre métaux…). Clés restreintes — voir ci-dessous.

Règlement en espèces

L'article L112-6 du Code monétaire et financier encadre le paiement en espèces, et l'API applique ces limites à l'écriture :

Régime de l'entréeRègle
Métaux ferreux et non ferreuxEspèces interdites, sans seuil → 400 PAIEMENT_ESPECES_INTERDIT
Autres régimesPlafond légal face à un vendeur particulier400 PAIEMENT_ESPECES_PLAFONNE

Le montant retenu est le total de l'entrée (prixAchat × quantite), pas le prix unitaire. Le plafond ne s'applique pas à un vendeur professionnel.

Ce contrôle est volontairement bloquant : un registre qui enregistre un règlement illégal en constitue la preuve, scellée et horodatée. Mieux vaut refuser l'écriture que produire cette pièce.

Cohérence des dates

  • dateAchat ne peut pas être postérieure à aujourd'hui → 400 DATE_ACHAT_FUTURE.
  • La date d'un mouvement ne peut ni précéder la date d'entrée de l'objet (400 DATE_MOUVEMENT_ANTERIEURE), ni être dans le futur (400 DATE_MOUVEMENT_FUTURE).

La comparaison se fait à la journée près, pour rester tolérante aux fuseaux horaires.

Pièces jointes

Les clés transmises dans photoKeys, vendeurDocumentKeys, signatureKey ou documents doivent correspondre à des fichiers réellement envoyés par votre établissement via upload-photo. Une clé inconnue est refusée en 400 FICHIER_INTROUVABLE — sans quoi le registre citerait une pièce justificative introuvable, ce qui ne se découvrirait qu'au contrôle.

Clés autorisées dans champsActivite

Les clés acceptées dépendent de l'activité de votre établissement et de ses régimes actifs : poincon pour un bijoutier, vin pour un professionnel de l'automobile, poidsMetaux et titreMetaux dès que le régime métaux précieux est actif. La liste exhaustive par activité est publiée sur la page Modèles.

Une clé qui n'appartient ni à votre activité ni à vos régimes est refusée : l'API renvoie 400 CHAMPS_ACTIVITE_INVALIDES en nommant le champ fautif et les champs acceptés.

{
  "success": false,
  "code": "CHAMPS_ACTIVITE_INVALIDES",
  "message": "Champ inconnu pour l'activité « automobile » : epoque. Champs acceptés : vin, immatriculation, carteGrise, controleTechnique."
}

Ce refus est volontaire : une clé inconnue serait enregistrée sans jamais apparaître ni dans le registre, ni dans le PDF remis lors d'un contrôle. Mieux vaut une erreur à l'intégration qu'une donnée perdue en silence.

En modification (PUT), les clés déjà portées par l'entrée restent acceptées même si l'activité de l'établissement a changé depuis — sans quoi les entrées anciennes deviendraient inéditables.

Vendeur particulier

ChampTypeRequisDescription
vendeurTypestringOuiparticulier
vendeurNomstringOuiPrénom et nom
vendeurDateNaissancestringOuiDate de naissance (ISO 8601)
vendeurLieuNaissancestringOuiLieu de naissance
vendeurNationalitestringOuiNationalité
vendeurAdressestringOuiAdresse postale
vendeurIdentiteTypestringOuiCNI, Passeport, Titre de séjour, Permis de conduire, Autre
vendeurIdentiteNumerostringOuiNuméro de la pièce d'identité
vendeurIdentiteDelivrancestringNonAutorité de délivrance

Vendeur professionnel

ChampTypeRequisDescription
vendeurTypestringOuiprofessionnel
vendeurRaisonSocialestringOuiRaison sociale
vendeurSiretstringOuiNuméro SIRET
vendeurAdressestringOuiAdresse du siège
vendeurNomstringNonNom du représentant

Photos et documents

ChampTypeRequisDescription
photoKeysstring[]NonClés de photos uploadées via /api/objets/upload-photo?context=objets (max 5)
vendeurDocumentKeysstring[]NonClés de documents vendeur uploadés — pièce d'identité, Kbis, justificatif… (max 5). Accepté pour un vendeur particulier comme professionnel.
signatureKeystringNonClé de la signature numérique du vendeur (uploadée comme une photo)

Exemple — vendeur particulier

{
  "typeOperation": "achat",
  "designation": "Montre ancienne en or",
  "marques": "Marque visible au dos",
  "provenanceDeclaree": "Succession familiale",
  "quantite": 1,
  "prixAchat": 150.00,
  "modePaiement": "espèces",
  "dateAchat": "2025-03-01",
  "regimes": ["OBJETS_OCCASION", "METAUX_PRECIEUX"],
  "champsActivite": {
    "poincon": "Tête d'aigle 750",
    "poidsMetaux": "Or 18k : 12 g",
    "titreMetaux": "750‰"
  },
  "vendeurType": "particulier",
  "vendeurNom": "Jean Dupont",
  "vendeurDateNaissance": "1985-06-15",
  "vendeurLieuNaissance": "Paris (75)",
  "vendeurNationalite": "Française",
  "vendeurAdresse": "12 rue de la Paix, 75002 Paris",
  "vendeurIdentiteType": "CNI",
  "vendeurIdentiteNumero": "AB123456",
  "vendeurIdentiteDelivrance": "Préfecture de Paris"
}

Exemple — vendeur professionnel avec documents

{
  "typeOperation": "achat",
  "designation": "Lampe art déco",
  "quantite": 1,
  "prixAchat": 80.00,
  "modePaiement": "carte",
  "dateAchat": "2025-04-10",
  "vendeurType": "professionnel",
  "vendeurRaisonSociale": "Emmaüs Armentières",
  "vendeurSiret": "39765682800035",
  "vendeurAdresse": "3 Place du 19 Mars 1962, 59280 Armentières",
  "vendeurDocumentKeys": ["key-du-kbis-uploadé"]
}

Réponse 201

Retourne l'objet créé avec son id, son numero attribué, et tous les champs du type ObjetMobilier.


Modifier un objet

PUT /api/objets/{id}

Mêmes champs que la création. Tous les champs doivent être fournis (pas de patch partiel). Les photos et documents passés dans photoKeys / vendeurDocumentKeys remplacent les listes existantes : une clé absente du tableau sera retirée de l'objet.

Réponse 200

{
  "success": true,
  "changes": 2
}

changes indique le nombre de champs modifiés enregistrés dans le journal d'audit.


Supprimer un objet

La suppression est logique (soft delete) : l'objet reste dans le registre avec une date deletedAt pour assurer la traçabilité réglementaire.

DELETE /api/objets/{id}

Corps de la requête

ChampTypeRequisDescription
reasonstringOuiMotif de suppression (enregistré dans le journal d'audit)

Réponse 200

{
  "success": true
}

Créer un mouvement de stock

Crée un mouvement de stock sur un objet : vente, mise en casse, réparation, exposition, expertise, etc. Le mouvement est enregistré dans le journal d'audit (crypto-chaîné) et le statut de l'objet est recalculé automatiquement.

Tout changement de statut passe par un mouvement. Un mouvement peut être annulé (résolu) pour remettre l'objet en stock.

POST /api/objets/{id}/mouvements

Header obligatoire : X-Agent

Comme pour la création d'objet, le header X-Agent (nom de la personne physique ayant effectué le mouvement) est requis. 400 MISSING_AGENT sans ce header.

Corps de la requête

ChampTypeRequisDescription
statutstringOuiStatut cible de catégorie sorti ou en_cours. Les statuts disponibles dépendent de l'activitéen_exposition existe chez un galeriste, pas chez un professionnel de l'automobile. Voir Statuts. Un statut inconnu est refusé en 400 STATUT_INVALIDE, avec la liste des statuts valides dans validStatuts ; un statut de catégorie disponible (ex. en_stock) en 400 STATUT_NON_MOUVEMENT.
quantitenumberNonQuantité à sortir (par défaut : tout le stock disponible)
datestringNonDate du mouvement (ISO 8601, par défaut : maintenant)
prixVentenumberSi ventePrix de vente en euros
acheteurTypestringNonparticulier (défaut) ou professionnel
acheteurNomstringNonNom de l'acheteur (particulier) ou du représentant (professionnel)
acheteurAdressestringNonAdresse de l'acheteur
acheteurRaisonSocialestringNonRaison sociale si acheteurType = professionnel
acheteurSiretstringNonSIRET si acheteurType = professionnel
notesstringNonInformations complémentaires sur le mouvement
documentsobject[]NonDocuments justificatifs, au format [{ "key": "…" }]. Clés obtenues via upload-photo?context=mouvements.

Réponse 201

{
  "success": true,
  "statut": "vendu",
  "quantite": 5,
  "dateVente": "2025-06-15T00:00:00.000Z",
  "prixVente": 280.00,
  "mouvements": [
    {
      "id": "mouvement-uuid",
      "quantite": 5,
      "statut": "vendu",
      "date": "2025-06-15T00:00:00.000Z",
      "prixVente": 280.00,
      "acheteurType": "particulier",
      "acheteurNom": null,
      "acheteurAdresse": null,
      "acheteurRaisonSociale": null,
      "acheteurSiret": null,
      "resolvedAt": null
    }
  ]
}

Exemple — vente à un particulier

{
  "statut": "vendu",
  "quantite": 2,
  "date": "2025-06-15",
  "prixVente": 120.00,
  "acheteurType": "particulier",
  "acheteurNom": "Marie Martin",
  "acheteurAdresse": "5 Rue des Lilas, 75011 Paris"
}

Exemple — vente à un professionnel

{
  "statut": "vendu",
  "quantite": 1,
  "date": "2025-06-20",
  "prixVente": 850.00,
  "acheteurType": "professionnel",
  "acheteurRaisonSociale": "Affinerie du Nord",
  "acheteurSiret": "39765682800035",
  "acheteurAdresse": "12 Zone Industrielle, 59800 Lille"
}

Exemple — mise en réparation

{
  "statut": "en_reparation"
}

Annuler un mouvement de stock

Si un mouvement de stock a été créé par erreur, il peut être résolu (annulé). Le mouvement reste dans l'historique avec un timestamp resolvedAt, et le stock disponible est recalculé.

PUT /api/objets/{id}/mouvements/{mouvementId}/resolve

Aucun corps de requête requis.

Réponse 200

{
  "success": true,
  "statut": "en_stock",
  "quantite": 5,
  "mouvements": [
    {
      "id": "mouvement-uuid",
      "quantite": 2,
      "statut": "vendu",
      "date": "2025-06-15T00:00:00.000Z",
      "prixVente": 120.00,
      "acheteurNom": "Marie Martin",
      "acheteurAdresse": "5 Rue des Lilas, 75011 Paris",
      "resolvedAt": "2025-06-16T09:00:00.000Z"
    }
  ]
}

Upload de photo ou document

Endpoint unique pour uploader des photos d'objet, des documents vendeur, des documents de mouvement ou des signatures.

POST /api/objets/upload-photo?context=objets

Content-Type : multipart/form-data

Paramètre d'URL obligatoire : context

Le contexte détermine où le fichier est rangé et comment il sera relu ensuite. Il est obligatoire : sans lui, l'API renvoie 400.

ValeurUsageChamp de destination
objetsPhoto ou document de la transactionphotoKeys
vendeursDocument du vendeur (pièce d'identité, Kbis…)vendeurDocumentKeys
mouvementsDocument d'une sortie (facture, attestation…)documents du mouvement
signaturesSignature du vendeursignatureKey
ChampTypeDescription
photoFileFichier image (JPEG, PNG, max 10 Mo). Redimensionné puis converti automatiquement en WebP.

Réponse 201

{
  "key": "7dec0cf719a87d79170be95266c6f35101ae4e6b93a3a2387d53aabe0c84f149.webp",
  "url": "https://api.registeo.fr/registeo/objets/photo?key=7dec0cf7…webp&context=objets"
}

Utilisez la key retournée dans photoKeys, vendeurDocumentKeys, documents ou signatureKey. Traitez-la comme un identifiant opaque : sa forme peut évoluer.


Supprimer une photo d'objet

DELETE /api/objets/{id}/photos

Corps de la requête

ChampTypeRequisDescription
keystringOuiClé de la photo à supprimer

Réponse 200

{
  "success": true
}

La photo n'est supprimée physiquement que si elle n'est plus référencée par aucun autre objet.