Documentation API

Intégrez la gestion des commandes, l’optimisation de découpe 2D et la validation DXF CNC de Handifyit à votre flux de travail. L’optimisation fournit des résultats JSON et des exports PDF, SVG, DXF, CSV et PTX. Les requêtes API authentifiées utilisent un en-tête Authorization: Bearer <token>.

Vous pouvez aussi travailler dans le navigateur : créer des commandes, importer des pièces, comparer les résultats enregistrés, choisir un résultat et modifier les statuts. Le guide ci-dessous présente l’API ; consultez le parcours d’une commande pour commencer sans code. Comment ça marche

Aller à : Pré-validateur CNC · POST /api/validate-cnc · Préréglages · Optimisation · Import CSV · Import DXF · Swagger UI

Authentification — jetons API à longue durée de vie

Handifyit utilise des Jetons API à longue durée de vie pour les appels automatisés ou côté serveur. Ils restent valides jusqu’à leur révocation explicite. Suivez les trois étapes ci-dessous pour en obtenir un.

1

Connectez-vous pour obtenir un jeton d’accès

Envoyez vos identifiants et ouvrez la page Profil.

POST /login
Content-Type: application/json

{
  "email": "[email protected]",
  "password": "yourpassword"
}

→ 200 OK
{
  "access_token":  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
2

Créez un jeton API à longue durée de vie

Donnez un nom explicite à votre jeton API. Copiez le jeton reçu : sa valeur n’est affichée qu’une seule fois.

POST /api/tokens
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "My Production App"
}

→ 201 Created
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",  ← conservez cette valeur !
  "name": "My Production App"
}

Vous pouvez aussi révoquer les jetons API depuis le Profil.

3

Utilisez le jeton API dans chaque requête

Transmettez le jeton API comme jeton Bearer dans tous les appels à /api/*.

POST /api/optimize
Authorization: Bearer <your-api-token>
Content-Type: application/json

{ ... corps de requête ... }

Corps de requête commun

Les six points de terminaison d’optimisation acceptent le même corps JSON. Seuls materials, stock_plates, parts et optimize_type sont obligatoires ; les autres champs ont une valeur par défaut.

ChampStatutValeur par défautDescription
materialsobligatoire—Tableau des matériaux. Chaque objet exige id (entier), name (chaîne), identifier (code court) et peut inclure thickness en mm (18 par défaut). Pièces et panneaux sont regroupés par material_id.
stock_platesobligatoire—Panneaux disponibles pour la découpe. Chaque objet : width, height, quantity (1 par défaut), material_id.
partsobligatoire—Pièces à découper. Chaque objet : width, height, quantity (1 par défaut), material_id et can_rotate (true par défaut). La rotation est définie pour chaque pièce.
optimize_typeobligatoire—"saw" — coupes guillotine rectilignes adaptées aux scies à panneaux. "cnc" — imbrication pour fraiseuses CNC, plus souple mais plus lente.
blade_widthfacultatif5Largeur du trait de scie ou de fraise, de 1 à 10, dans l’unité des pièces et panneaux. Déduite de l’espace entre les coupes. Les imports de fichiers acceptent 0–10.
trim_stock_platefacultatif10Marge retirée de chaque bord du panneau avant optimisation, de 0 à 50, pour éliminer les bords inutilisables.
allow_mixed_stock_sizesfacultatiftrueAutorise le mélange de formats de panneaux pour un matériau. La valeur false impose une seule taille pour tous ses panneaux.
pattern_directionfacultatif"none""paralleltowidth"
performance_modefacultatif"normal""fast" — réponse la plus rapide. "normal" — équilibré (recommandé). "high" — regroupement intensif pour réduire les chutes, avec un calcul plus long.
random_seedfacultatif0Graine entière du générateur aléatoire. Réutilisez la même graine pour reproduire des calepinages identiques entre requêtes.
can_rotatefacultatif—Obsolète. Ce réglage global reste disponible pour compatibilité : il impose la même rotation à toutes les pièces. Omettez-le pour appliquer les indicateurs can_rotate de chaque pièce.

Exemple de requête complet

{
  "materials": [
    { "id": 1, "name": "Plywood 18mm", "identifier": "PLY18", "thickness": 18 }
  ],
  "stock_plates": [
    { "width": 2440, "height": 1220, "quantity": 5, "material_id": 1 }
  ],
  "parts": [
    { "width": 800, "height": 600, "quantity": 4, "material_id": 1, "can_rotate": true  },
    { "width": 400, "height": 300, "quantity": 8, "material_id": 1, "can_rotate": false }
  ],
  "optimize_type":           "saw",
  "blade_width":              5,
  "trim_stock_plate":         10,
  "allow_mixed_stock_sizes":  true,
  "pattern_direction":       "none",
  "performance_mode":        "normal",
  "random_seed":              0
}

Limites de l’offre gratuite

Ces limites s’appliquent aux comptes gratuits sur tous les points d’optimisation (/api/optimize, /api/optimize/pdf, /api/optimize/dxf, /api/optimize/svg, /api/optimize/csv, /api/optimize/ptx et toutes les variantes from-dxf et from-csv) :

  • 25 pièces par requête — la somme des valeurs part.quantity de tout le tableau parts ne doit pas dépasser 25. Un dépassement renvoie 400.
  • 25 requêtes d’optimisation par jour — comptées par compte, avec remise à zéro à minuit UTC. Un dépassement renvoie 429.
POST/api/optimize

Exécute l’optimiseur et renvoie le calepinage complet en JSON. Aucun fichier n’est créé ; utilisez ce point pour traiter ou afficher les résultats dans votre application.

Structure de la réponse

{
  "results": [
    {
      "material": { "id": 1, "name": "Plywood 18mm", "identifier": "PLY18",
                    "thickness": 18 },
      "layouts": [
        {
          "stock_width":  2420,
          "stock_height": 1200,
          "stock_index":  0,
          "cuts": [
            { "x": 0,   "y": 0,   "width": 800, "height": 600, "part_id": 0 },
            { "x": 805, "y": 0,   "width": 400, "height": 300, "part_id": 1 },
            { "x": 805, "y": 305, "width": 400, "height": 300, "part_id": 1 }
          ],
          "cut_length_mm":     10420,
          "cut_length_method": "guillotine",
          "offcuts": [
            { "x": 1220, "y": 0, "width": 1220, "height": 1220 },
            …
          ],
          "saw_plan": {
            "kind": "split", "x": 0, "y": 0, "width": 2440, "height": 1220,
            "axis": "x", "cuts": [5, 810, 1215],
            "children": [
              { "kind": "offcut", "x": 0, "y": 0, "width": 5, "height": 1220 },
              { "kind": "split", "x": 10, "y": 0, "width": 800, "height": 1220,
                "axis": "y", "cuts": [5, 610],
                "children": [
                  { "kind": "offcut", "x": 10, "y": 0, "width": 800, "height": 5 },
                  { "kind": "part", "x": 10, "y": 10, "width": 800, "height": 600,
                    "cut_index": 0 },
                  …
                ] },
              …
            ]
          }
        }
      ],
      "summary": {
        "total_stock_used":    1,
        "total_parts":         12,
        "material_efficiency": 91.8,
        "total_cut_length_mm": 10420
      }
    }
  ]
}

La réponse contient une entrée results par matériau. Chaque entrée comprend :

  • layouts — un objet par panneau utilisé ; cuts indique la position (x, y) et la taille de chaque pièce
  • summary — total_stock_used, total_parts, material_efficiency (0–100 %) et total_cut_length_mm

stock_width et stock_height décrivent la surface utile après retrait de trim_stock_plate sur les quatre bords. Les coordonnées cut partent du coin supérieur gauche de cette surface. part_id est l’indice de la pièce dans son groupe de matériau, pas un identifiant global.

cut_length_mm a deux méthodes de mesure, indiquées par cut_length_method : "guillotine" mesure le trajet de la scie à panneaux ; "contour", le périmètre parcouru par la fraise autour des pièces. Ces grandeurs physiques différentes ne doivent jamais être additionnées ni comparées entre elles.

stock_index est la position du panneau d’origine dans stock_plates. offcuts désigne les morceaux sans pièce laissés entiers par les coupes, y compris les petites chutes. Contrairement à cuts, ils sont positionnés sur la surface brute. Leurs dimensions physiques tiennent déjà compte du trait de scie et incluent les bords non délignés.

saw_plan représente le même plan en arbre sur le panneau brut. Un élément "split" est divisé par des coupes parallèles : cuts donne le début des bandes de coupe selon axis ("x" : coupe longitudinale ; "y" : transversale). Chaque bande mesure blade_width. children contient une entrée par intervalle, ou null s’il ne reste rien. Les feuilles sont des pièces "part", dont cut_index pointe dans cuts, ou des chutes "offcut". La valeur null signifie qu’aucune séquence rectiligne n’est possible, notamment pour certains calepinages "cnc".

POST/api/optimize/pdf

Optimise et rassemble les calepinages dans un PDF multipage, une page par panneau. Le fichier est envoyé au stockage cloud et une URL de téléchargement signée est renvoyée.

Requête

Même corps JSON que /api/optimize (voir ci-dessus).

Réponse

{
  "files": [
    {
      "name": "layout-1708433600000.pdf",
      "url":  "https://storage.example.com/pdf/1708433600000/layout-1708433600000.pdf"
    }
  ]
}

La réponse contient un seul fichier PDF regroupant tous les panneaux.

POST/api/optimize/dxf

Optimise et crée un DXF par panneau, compatible avec la plupart des logiciels CAO/FAO et contrôleurs CNC (AutoCAD, Fusion 360, Mach3, etc.). Renvoie une URL par fichier envoyé au stockage.

Requête

Même corps JSON que /api/optimize (voir ci-dessus).

Réponse

{
  "files": [
    {
      "name": "plate-1-25318.dxf",
      "url":  "https://storage.example.com/dxf/1708433600000/plate-1-25318.dxf"
    },
    {
      "name": "plate-2-25318.dxf",
      "url":  "https://storage.example.com/dxf/1708433600000/plate-2-25318.dxf"
    }
  ]
}

Un fichier par panneau. Son nom encode l’indice du panneau et l’exécution : plate-1-25318.dxf désigne le panneau 1 de l’exécution 25318.

POST/api/optimize/csv

Optimise et crée un CSV pour toutes les pièces de tous les matériaux et panneaux. Chaque ligne contient position, dimensions de la pièce et du panneau, nom et code du matériau, pour tableurs, ERP ou postprocesseurs. Le fichier est stocké dans le cloud et son URL est renvoyée.

Requête

Même corps JSON que /api/optimize (voir ci-dessus).

Colonnes CSV

Part ID, x, y, width, height, stock_width, stock_height, material(as name), material(as identifier)

Réponse

{
  "files": [
    {
      "name": "layout-1708433600000.csv",
      "url":  "https://storage.example.com/csv/1708433600000/layout-1708433600000.csv"
    }
  ]
}

Un seul CSV avec toutes les coupes de tous les matériaux et panneaux.

POST/api/optimize/svg

Optimise et crée un SVG par panneau. Ces graphiques vectoriels conviennent aux aperçus web et à l’import dans Inkscape, Illustrator ou Figma. Renvoie une URL par fichier envoyé au stockage.

Requête

Même corps JSON que /api/optimize (voir ci-dessus).

Réponse

{
  "files": [
    {
      "name": "plate-1-25318.svg",
      "url":  "https://storage.example.com/svg/1708433600000/plate-1-25318.svg"
    },
    {
      "name": "plate-2-25318.svg",
      "url":  "https://storage.example.com/svg/1708433600000/plate-2-25318.svg"
    }
  ]
}
POST/api/optimize/ptx
Bêta

L’export PTX est en version bêta et n’a pas encore été validé sur des machines réelles. Vérifiez les fichiers exportés sur votre machine avant toute utilisation en production.

Optimise et crée un PTX (Pattern Exchange) par groupe de matériau. Ce format de transfert pour scies HOLZMA / HOMAG est accepté par HolzLink, CADmatic 4/5 et HOMAG intelliDivide. Chaque fichier contient le travail du matériau, ses schémas et séquences de sciage. Renvoie les URL des fichiers stockés dans le cloud.

Requête

Même corps JSON que /api/optimize (voir ci-dessus). Le réglage optimize_type: "saw" produit des calepinages guillotine adaptés aux scies à panneaux.

Réponse

{
  "files": [
    {
      "name": "J1708433600000.PTX",
      "url":  "https://storage.example.com/ptx/1708433600000/J1708433600000.PTX"
    }
  ]
}

Un PTX par groupe de matériau. Chaque fichier inclut :

  • HEADER, une ligne JOBS et une ligne MATERIALS — métadonnées du programme, épaisseur du panneau, trait de scie et délignage.
  • BOARDS + PATTERNS — une paire par calepinage distinct, avec dimensions brutes et nombre de panneaux utilisant ce schéma. Les schémas identiques sont fusionnés.
  • PARTS_REQ + PARTS_INF + PARTS_UDI — lignes correspondantes pour chaque géométrie distincte dans un schéma, numérotées sur tout le travail. Les quantités incluent les panneaux répétés. Descriptions et étiquettes de chants facultatives sont vides ; le code matériau est DSP.
  • CUTS — instructions de coupe initiale, longitudinale, transversale, de recoupe et de délignage ; une séquence par schéma avec références aux pièces requises.

L’épaisseur par défaut est 18 mm, courante pour les panneaux de mobilier. Mesures en millimètres. Programmes et fichiers portent un nom J<timestamp>.PTX, avec un identifiant numérique unique par matériau.

Un calepinage imbriqué "cnc" n’est pas toujours réalisable à la scie. Dans ce cas, ce point renvoie 422 au lieu d’un fichier.

Import de listes de pièces CSV

Les points from-csv acceptent multipart/form-data au lieu de JSON. Importez une liste de pièces depuis un tableur : le serveur construit parts à partir des lignes. Matériaux, panneaux, trait de scie et autres réglages sont transmis comme chaîne JSON dans params.

Format CSV

↓ Télécharger le modèle / exemple CSVTous les champs pris en charge et 10 pièces d’exemple avec différentes quantités. Remplacez les codes matériaux par les vôtres.

Material Code,Width,Height,Quantity,Can Rotate
PLY18,800,600,4,1
PLY18,400,300,8,0
MDF16,1200,400,2,1

Comment les lignes deviennent des pièces

  • La ligne d’en-tête est obligatoire. Casse, espaces périphériques, tirets et traits de soulignement sont ignorés dans : Material Code, Width, Height, Quantity, Can Rotate.
  • Material Code est associé à identifier d’une entrée de materials. Un code inconnu fait échouer la requête ; l’erreur indique la ligne concernée.
  • Can Rotate est facultatif : 1 autorise une rotation de 90°, 0 l’interdit. Une cellule vide ou une colonne absente signifie aucune rotation, afin de préserver le sens du fil.
  • Material Code, Width, Height et Quantity sont obligatoires sur chaque ligne.
  • Virgule, point-virgule, tabulation, BOM UTF-8 et texte entre guillemets sont acceptés. Les dimensions API sont des millimètres entiers positifs ; les quantités, des entiers positifs.

Placage des chants de chaque côté

Pour Top, Right, Bottom et Left, utilisez Edge Band (code), Edge Thickness (mm), Pre Milling (mm retirés) et Edge Method. Exemples : Top Edge Band, Top Edge Thickness. Les côtés décrivent la pièce avant rotation.

Laissez vides les côtés non traités. Méthodes : none, band, pre_mill_band. Une méthode omise est déduite du code, de l’épaisseur et du fraisage. Un chant plaqué exige une épaisseur positive ; l’API ne consulte pas le catalogue. L’importeur de l’atelier peut y récupérer une épaisseur manquante.

Dimension Mode accepte blank (par défaut) ou finished. L’optimiseur compense l’épaisseur et le préfraisage. Limites machine facultatives : Banding Min Length, Banding Min Cross Size, Pre Milling Min Length, Pre Milling Min Cross Size, en mm ; zéro désactive une limite. Processing Order énumère chaque côté une fois, par exemple top right bottom left. Les erreurs indiquent le numéro de ligne.

L’atelier importe aussi Part Code, Part Name, Grain (none, paralleltowidth, paralleltolength), Priority et Notes. Ces métadonnées de commande sont ignorées par l’API CSV sans état.

Champs de requête (multipart)

ChampStatutTypeDescription
fileobligatoire—Liste de pièces CSV avec la ligne d’en-tête ci-dessus.
paramsobligatoire—Paramètres d’optimisation en JSON (tableau ci-dessous). Tous les champs de OptimizeRequest, sauf parts.

Champs JSON de « params »

ChampStatutValeur par défautDescription
materialsobligatoire—Au moins un matériau est requis. Chaque identifier correspond au Material Code d’une ligne.
stock_platesobligatoire—Comme dans /api/optimize ; au moins un panneau est requis.
optimize_typeobligatoire—"saw" ou "cnc" (comme /api/optimize).
blade_widthfacultatif5Trait de scie en mm. Les imports acceptent 0, contrairement au point JSON.
trim_stock_platefacultatif10Comme /api/optimize.
allow_mixed_stock_sizesfacultatiftrueComme /api/optimize.
pattern_directionfacultatif"none"Comme /api/optimize.
performance_modefacultatif"normal"Comme /api/optimize.
random_seedfacultatif0Comme /api/optimize.

Exemple JSON de « params »

{
  "materials": [
    { "id": 1, "name": "Plywood 18mm", "identifier": "PLY18", "thickness": 18 },
    { "id": 2, "name": "MDF 16mm",     "identifier": "MDF16", "thickness": 16 }
  ],
  "stock_plates": [
    { "width": 2440, "height": 1220, "quantity": 5, "material_id": 1 },
    { "width": 2800, "height": 2070, "quantity": 3, "material_id": 2 }
  ],
  "optimize_type":           "saw",
  "blade_width":              5,
  "trim_stock_plate":         10,
  "allow_mixed_stock_sizes":  true,
  "pattern_direction":       "none",
  "performance_mode":        "normal",
  "random_seed":              0
}

Exemple de requête (curl)

# résultat JSON
curl -X POST https://handifyit.com/api/optimize/from-csv \
  -H "Authorization: Bearer <your-api-token>" \
  -F "[email protected]" \
  -F 'params={
    "materials":    [{"id":1,"name":"Plywood 18mm","identifier":"PLY18","thickness":18}],
    "stock_plates": [{"width":2440,"height":1220,"quantity":5,"material_id":1}],
    "optimize_type":   "saw",
    "blade_width":      5,
    "trim_stock_plate": 10
  }'

# PDF — le format d’export précède le suffixe « from-csv » :
curl -X POST https://handifyit.com/api/optimize/pdf/from-csv \
  -H "Authorization: Bearer <your-api-token>" \
  -F "[email protected]" \
  -F 'params={"materials":[...],"stock_plates":[...],"optimize_type":"saw"}'

# Même principe pour /svg/from-csv /dxf/from-csv /csv/from-csv /ptx/from-csv

Points de terminaison

POST/api/optimize/from-csv

Analyse le CSV, optimise et renvoie le calepinage JSON complet, de même structure que /api/optimize.

{
  "results": [
    {
      "material": { "id": 1, "name": "Plywood 18mm", "identifier": "PLY18",
                    "thickness": 18 },
      "layouts": [
        {
          "stock_width":  2420,
          "stock_height": 1200,
          "stock_index":  0,
          "cuts": [
            { "x": 0,   "y": 0,   "width": 800, "height": 600, "part_id": 0 },
            { "x": 805, "y": 0,   "width": 400, "height": 300, "part_id": 1 },
            { "x": 805, "y": 305, "width": 400, "height": 300, "part_id": 1 }
          ],
          "cut_length_mm":     10420,
          "cut_length_method": "guillotine",
          "offcuts": [
            { "x": 1220, "y": 0, "width": 1220, "height": 1220 },
            …
          ],
          "saw_plan": {
            "kind": "split", "x": 0, "y": 0, "width": 2440, "height": 1220,
            "axis": "x", "cuts": [5, 810, 1215],
            "children": [
              { "kind": "offcut", "x": 0, "y": 0, "width": 5, "height": 1220 },
              { "kind": "split", "x": 10, "y": 0, "width": 800, "height": 1220,
                "axis": "y", "cuts": [5, 610],
                "children": [
                  { "kind": "offcut", "x": 10, "y": 0, "width": 800, "height": 5 },
                  { "kind": "part", "x": 10, "y": 10, "width": 800, "height": 600,
                    "cut_index": 0 },
                  …
                ] },
              …
            ]
          }
        }
      ],
      "summary": {
        "total_stock_used":    1,
        "total_parts":         12,
        "material_efficiency": 91.8,
        "total_cut_length_mm": 10420
      }
    }
  ]
}
POST/api/optimize/{format}/from-csv

Même import, avec export. {format} vaut pdf, svg, dxf, csv ou ptx. La réponse utilise l’enveloppe { "files": [...] } contenant des URL signées.

Bêta

L’export PTX est en version bêta et n’a pas encore été validé sur des machines réelles. Vérifiez les fichiers exportés sur votre machine avant toute utilisation en production.

Optimisation avec import DXF

Les points from-dxf acceptent multipart/form-data plutôt que JSON. Le serveur extrait les contours extérieurs du DXF sans tableau parts manuel. Chaque contour fermé devient une pièce dont width et height proviennent du rectangle englobant aligné sur les axes. Tous les réglages sont une chaîne JSON dans params.

Extraction des pièces DXF

  • Les entités fermées LWPOLYLINE, POLYLINE, ELLIPSE et SPLINE au premier niveau, sans contour englobant, deviennent des pièces.
  • width et height sont les dimensions du rectangle englobant aligné sur les axes, arrondies à l’entier dans l’unité unit (mm par défaut). Les pièces inférieures à 1 unité dans une dimension sont ignorées.
  • Toutes les pièces sont associées au matériau material_id, par défaut le id de la première entrée de materials.
  • Les contours intérieurs (perçages, découpes, rainures) sont ignorés : seul le contour extérieur compte pour l’optimisation.

Champs de requête (multipart)

ChampStatutTypeDescription
fileobligatoire—Octets du fichier DXF (R12–2018), avec extension .dxf obligatoire.
paramsobligatoire—Paramètres JSON (tableau ci-dessous) : tous les champs de OptimizeRequest, sauf parts, plus unit et material_id.

Champs JSON de « params »

ChampStatutValeur par défautDescription
materialsobligatoire—Comme dans /api/optimize ; au moins un matériau est requis.
stock_platesobligatoire—Comme dans /api/optimize ; au moins un panneau est requis.
optimize_typeobligatoire—"saw" ou "cnc" (comme /api/optimize).
unitfacultatif"mm""cm"
material_idfacultatifid du premier matériauIdentifiant du matériau attribué à chaque pièce DXF. Par défaut, le id de la première entrée de materials.
blade_widthfacultatif5Trait de scie en mm. Les imports acceptent 0, contrairement au point JSON.
can_rotatefacultatiftrueAppliqué à chaque pièce DXF, car le dessin n’a pas d’indicateur individuel de rotation.
trim_stock_platefacultatif10Comme /api/optimize.
allow_mixed_stock_sizesfacultatiftrueComme /api/optimize.
pattern_directionfacultatif"none"Comme /api/optimize.
performance_modefacultatif"normal"Comme /api/optimize.
random_seedfacultatif0Comme /api/optimize.

Exemple JSON de « params »

{
  "materials": [
    { "id": 1, "name": "Plywood 18mm", "identifier": "PLY18", "thickness": 18.0 }
  ],
  "stock_plates": [
    { "width": 2440, "height": 1220, "quantity": 5, "material_id": 1 }
  ],
  "optimize_type":           "saw",
  "blade_width":              5,
  "can_rotate":               true,
  "trim_stock_plate":         10,
  "allow_mixed_stock_sizes":  true,
  "pattern_direction":       "none",
  "performance_mode":        "normal",
  "random_seed":              0,
  "unit":                    "mm",
  "material_id":              1
}

Exemple de requête (curl)

# résultat JSON
curl -X POST https://handifyit.com/api/optimize/from-dxf \
  -H "Authorization: Bearer <your-api-token>" \
  -F "[email protected]" \
  -F 'params={
    "materials":       [{"id":1,"name":"Plywood 18mm","identifier":"PLY18","thickness":18}],
    "stock_plates":    [{"width":2440,"height":1220,"quantity":5,"material_id":1}],
    "optimize_type":   "saw",
    "blade_width":      5,
    "can_rotate":       true,
    "trim_stock_plate": 10,
    "unit":            "mm"
  }'

# PDF — utilisez le point d’export PDF :
curl -X POST https://handifyit.com/api/optimize/pdf/from-dxf \
  -H "Authorization: Bearer <your-api-token>" \
  -F "[email protected]" \
  -F 'params={"materials":[...],"stock_plates":[...],"optimize_type":"saw"}'

# Même principe pour /svg/from-dxf /dxf/from-dxf /csv/from-dxf /ptx/from-dxf

Points de terminaison

POST/api/optimize/from-dxf

Analyse le DXF, optimise et renvoie le calepinage JSON complet, de même structure que /api/optimize.

{
  "results": [
    {
      "material": { "id": 1, "name": "Plywood 18mm", "identifier": "PLY18",
                    "thickness": 18 },
      "layouts": [
        {
          "stock_width":  2420,
          "stock_height": 1200,
          "stock_index":  0,
          "cuts": [
            { "x": 0,   "y": 0,   "width": 800, "height": 600, "part_id": 0 },
            { "x": 805, "y": 0,   "width": 400, "height": 300, "part_id": 1 },
            { "x": 805, "y": 305, "width": 400, "height": 300, "part_id": 1 }
          ],
          "cut_length_mm":     10420,
          "cut_length_method": "guillotine",
          "offcuts": [
            { "x": 1220, "y": 0, "width": 1220, "height": 1220 },
            …
          ],
          "saw_plan": {
            "kind": "split", "x": 0, "y": 0, "width": 2440, "height": 1220,
            "axis": "x", "cuts": [5, 810, 1215],
            "children": [
              { "kind": "offcut", "x": 0, "y": 0, "width": 5, "height": 1220 },
              { "kind": "split", "x": 10, "y": 0, "width": 800, "height": 1220,
                "axis": "y", "cuts": [5, 610],
                "children": [
                  { "kind": "offcut", "x": 10, "y": 0, "width": 800, "height": 5 },
                  { "kind": "part", "x": 10, "y": 10, "width": 800, "height": 600,
                    "cut_index": 0 },
                  …
                ] },
              …
            ]
          }
        }
      ],
      "summary": {
        "total_stock_used":    1,
        "total_parts":         12,
        "material_efficiency": 91.8,
        "total_cut_length_mm": 10420
      }
    }
  ]
}
POST/api/optimize/pdf/from-dxf

Analyse le DXF, optimise et renvoie un plan PDF, une page par panneau, via une URL signée.

{
  "files": [
    {
      "name": "layout-1708433600000.pdf",
      "url":  "https://storage.example.com/pdf/1708433600000/layout-1708433600000.pdf"
    }
  ]
}
POST/api/optimize/svg/from-dxf

Analyse le DXF, optimise et renvoie un SVG par panneau via des URL signées.

{
  "files": [
    {
      "name": "plate-1-25318.svg",
      "url":  "https://storage.example.com/svg/1708433600000/plate-1-25318.svg"
    },
    {
      "name": "plate-2-25318.svg",
      "url":  "https://storage.example.com/svg/1708433600000/plate-2-25318.svg"
    }
  ]
}
POST/api/optimize/dxf/from-dxf

Analyse le DXF, optimise et renvoie un calepinage DXF par panneau via des URL signées.

{
  "files": [
    {
      "name": "plate-1-25318.dxf",
      "url":  "https://storage.example.com/dxf/1708433600000/plate-1-25318.dxf"
    },
    {
      "name": "plate-2-25318.dxf",
      "url":  "https://storage.example.com/dxf/1708433600000/plate-2-25318.dxf"
    }
  ]
}
POST/api/optimize/csv/from-dxf

Analyse le DXF, optimise et renvoie une liste de coupe CSV unique via une URL signée.

{
  "files": [
    {
      "name": "layout-1708433600000.csv",
      "url":  "https://storage.example.com/csv/1708433600000/layout-1708433600000.csv"
    }
  ]
}
POST/api/optimize/ptx/from-dxf
Bêta

L’export PTX est en version bêta et n’a pas encore été validé sur des machines réelles. Vérifiez les fichiers exportés sur votre machine avant toute utilisation en production.

Analyse le DXF, optimise et renvoie des PTX pour scies HOLZMA / HOMAG via des URL signées.

{
  "files": [
    {
      "name": "J1708433600000.PTX",
      "url":  "https://storage.example.com/ptx/1708433600000/J1708433600000.PTX"
    }
  ]
}

Prévalidation CNC

Avant fabrication, le prévalidateur CNC analyse un fichier DXF selon les contraintes machine. Il extrait les limites des pièces et les opérations internes (perçages, découpes, rainures) pour détecter les violations des règles géométriques.

CodeGravitéDescription
PART_TOO_SMALLerreurLe rectangle englobant est inférieur à part_min_width × part_min_height.
PART_TOO_LARGEerreurLe rectangle englobant dépasse part_max_width × part_max_height.
OPERATION_OUTSIDE_PARTerreurAu moins un sommet du contour d’une opération est hors de la pièce.
OPERATION_INTERSECTS_EDGEerreurLes arêtes de l’opération traversent le bord, même si les sommets sont à l’intérieur.
OPERATION_TOO_CLOSE_TO_EDGEerreur / avertissementLa distance au bord est inférieure à min_edge_offset. Erreur si < ½ limite ; avertissement si < limite.
DRILL_DIAMETER_TOO_SMALLerreurLe diamètre de perçage est inférieur à drill_min_diameter.
DRILL_DIAMETER_TOO_LARGEerreurLe diamètre de perçage dépasse drill_max_diameter.
OPERATIONS_INTERSECTerreurDeux opérations sur la même face se chevauchent.
OPERATIONS_TOO_CLOSEerreur / avertissementLa distance entre surfaces de deux opérations est inférieure à min_op_spacing. Erreur si < ½ limite ; avertissement si < limite.
POST/api/validate-cnc

Envoyez un DXF en multipart/form-data. La réponse est un ValidationReport complet : un PartResult par pièce détectée, avec opérations et violations.

Champs de requête (multipart)

ChampStatutTypeDescription
fileobligatoire—Octets DXF. La plupart des révisions binaires R12–2018 sont acceptées.
configfacultatifvaleurs par défautObjet ValidationConfig encodé en JSON (tableau ci-dessous). Tous les champs sont facultatifs ; les champs omis prennent leur valeur par défaut.

Champs de ValidationConfig

ChampStatutValeur par défautDescription
part_min_widthfacultatif50Largeur minimale autorisée de la pièce en mm.
part_min_heightfacultatif50Hauteur minimale autorisée de la pièce en mm.
part_max_widthfacultatif2800Largeur maximale autorisée de la pièce en mm.
part_max_heightfacultatif2070Hauteur maximale autorisée de la pièce en mm.
drill_min_diameterfacultatif2Diamètre minimal du foret en mm.
drill_max_diameterfacultatif35Diamètre maximal du foret en mm.
min_edge_offsetfacultatif8Distance minimale (mm) entre une arête d’opération et le contour de la pièce.
min_op_spacingfacultatif8Distance minimale entre surfaces de deux opérations en mm.
groove_aspect_ratiofacultatif3.0Un contour intérieur fermé est une rainure, plutôt qu’une découpe générique, si son grand côté ≥ ce rapport × son petit côté.
unitfacultatif"mm""cm"

Exemple de requête (curl)

curl -X POST https://handifyit.com/api/validate-cnc \
  -H "Authorization: Bearer <your-api-token>" \
  -F "[email protected]" \
  -F 'config={"part_min_width":50,"part_min_height":50,\
              "part_max_width":2800,"part_max_height":2070,\
              "drill_min_diameter":2,"drill_max_diameter":35,\
              "min_edge_offset":8,"min_op_spacing":8,\
              "groove_aspect_ratio":3,"unit":"mm"}'

Exemple de réponse

{
  "valid": false,
  "summary": {
    "total_parts":      3,
    "total_operations": 12,
    "total_issues":     2,
    "errors":           1,
    "warnings":         1
  },
  "parts": [
    {
      "id":    "part_1",
      "layer": "PART",
      "bbox":  { "min_x": 0, "min_y": 0, "max_x": 400, "max_y": 300,
                 "width": 400, "height": 300 },
      "operations": [
        {
          "id":     "op_1",
          "kind":   "drilling",
          "side":   "through",
          "layer":  "DRILL",
          "bbox":   { "min_x": 46, "min_y": 46, "max_x": 54, "max_y": 54,
                      "width": 8, "height": 8 },
          "center": { "x": 50, "y": 50 },
          "diameter": 8
        }
      ],
      "issues": [
        {
          "code":         "OPERATION_TOO_CLOSE_TO_EDGE",
          "message":      "Operation op_1 is 3.50 mm from the part edge (minimum: 8.0 mm)",
          "severity":     "error",
          "operation_id": "op_1"
        }
      ],
      "valid": false
    }
  ]
}

Principaux champs de réponse :

  • valid — true seulement si aucune pièce ne présente de problème de gravité erreur.
  • summary — totaux : total_parts, total_operations, errors, warnings.
  • parts[] — un objet par pièce détectée avec rectangle englobant, opérations et problèmes.
  • issues[].severity — "error" (bloquant) ou "warning" (simple conseil).

Préréglages de configuration CNC

Enregistrez des préréglages ValidationConfig nommés pour les validations suivantes. Ils restent privés à votre compte.

GET/api/cnc-configs

Renvoie tous les préréglages enregistrés de l’utilisateur authentifié.

GET /api/cnc-configs
Authorization: Bearer <your-api-token>

→ 200 OK
{
  "configs": [
    {
      "id":         "d4f2a1b3-...",
      "name":       "Standard Router",
      "config":     { ... },
      "created_at": "2026-04-05T10:00:00Z",
      "updated_at": "2026-04-05T10:00:00Z"
    }
  ]
}
POST/api/cnc-configs

Crée un préréglage nommé. Renvoie 201 Created avec l’entrée enregistrée.

POST /api/cnc-configs
Authorization: Bearer <your-api-token>
Content-Type: application/json

{
  "name": "Standard Router",
  "config": {
    "part_min_width":     50,
    "part_min_height":    50,
    "part_max_width":     2800,
    "part_max_height":    2070,
    "drill_min_diameter": 2,
    "drill_max_diameter": 35,
    "min_edge_offset":    8,
    "min_op_spacing":     8,
    "groove_aspect_ratio": 3,
    "unit": "mm"
  }
}

→ 201 Created
{
  "id":         "d4f2a1b3-...",
  "name":       "Standard Router",
  "config":     { ... },
  "created_at": "2026-04-05T10:00:00Z",
  "updated_at": "2026-04-05T10:00:00Z"
}
PUT/api/cnc-configs/{id}

Remplace le nom et/ou la configuration. Le corps a la même structure que POST. Renvoie 200 OK avec l’entrée mise à jour, ou 404 si l’ID est absent ou appartient à un autre utilisateur.

DELETE/api/cnc-configs/{id}

Supprime définitivement le préréglage. Renvoie 204 No Content en cas de succès, ou 404 s’il est introuvable.

Réponses d’erreur

Tous les points renvoient leurs erreurs dans la même enveloppe JSON :

{ "message": "A part (800×600) does not fit any stock plate" }
  • 400 — limite de pièces dépassée : la somme des quantity dépasse le plafond par requête de l’offre. Signale aussi un CSV mal formé ou un champ multipart absent.
  • 401 — jeton Bearer absent, expiré ou révoqué.
  • 422 — une pièce ne tient sur aucun panneau ou les panneaux sont insuffisants. L’optimisation réussit entièrement ou échoue ; aucun calepinage partiel n’est renvoyé. Le message nomme la pièce ou la contrainte. L’export PTX renvoie aussi 422 si le plan est irréalisable à la scie.
  • 429 — limite quotidienne atteinte. Remise à zéro à minuit UTC.
  • 500 — erreur interne du serveur, par exemple échec d’envoi d’un fichier.