Documentação da API

Integre a gestão de encomendas, a otimização de corte 2D e a validação DXF CNC do Handifyit no seu fluxo de trabalho. A otimização suporta resultados JSON e exportações PDF, SVG, DXF, CSV e PTX. Os pedidos API autenticados usam o cabeçalho Authorization: Bearer <token>.

Também pode trabalhar no navegador: criar encomendas, importar peças, comparar resultados guardados, escolher um resultado e atualizar estados. O guia abaixo aborda a API; consulte o fluxo de encomendas para começar sem código. Como Funciona

Ir para: Pré-Validador CNC · POST /api/validate-cnc · Predefinições · Otimização · Importação CSV · Importação DXF · Swagger UI

Autenticação — tokens API de longa duração

O Handifyit usa Tokens API de longa duração para chamadas automáticas ou do servidor. Permanecem válidos até serem revogados explicitamente. Siga estes três passos para obter um.

1

Inicie sessão para obter um token de acesso

Envie as suas credenciais e abra a página Perfil.

POST /login
Content-Type: application/json

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

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

Crie um token API de longa duração

Dê um nome descritivo ao token API. Copie o token recebido: o valor só é apresentado uma vez.

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

{
  "name": "My Production App"
}

→ 201 Created
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",  ← guarde este valor!
  "name": "My Production App"
}

Também pode revogar tokens API no Perfil.

3

Use o token API em cada pedido

Envie o token API como Bearer em todas as chamadas a /api/*.

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

{ ... corpo do pedido ... }

Corpo comum do pedido

Os seis endpoints de otimização aceitam o mesmo corpo JSON. Só materials, stock_plates, parts e optimize_type são obrigatórios; os restantes campos têm valores predefinidos.

CampoEstadoValor predefinidoDescrição
materialsobrigatório—Lista de materiais. Cada objeto exige id (inteiro), name (texto), identifier (código curto) e pode incluir thickness em mm (18 por defeito). Peças e painéis agrupam-se por material_id.
stock_platesobrigatório—Painéis disponíveis para corte. Cada objeto: width, height, quantity (1 por defeito), material_id.
partsobrigatório—Peças a cortar. Cada objeto: width, height, quantity (1 por defeito), material_id e can_rotate (true por defeito). A rotação é definida por peça.
optimize_typeobrigatório—"saw" — cortes retos de guilhotina para serras de painéis. "cnc" — disposição aninhada para fresadoras CNC, mais flexível mas mais lenta.
blade_widthopcional5Largura do corte da serra ou fresa, 1–10, nas unidades das peças e painéis. É descontada entre cortes. A importação de ficheiros aceita 0–10.
trim_stock_plateopcional10Margem retirada de cada borda antes da otimização, 0–50, para excluir material inutilizável.
allow_mixed_stock_sizesopcionaltruePermite formatos diferentes no mesmo material. Com false, todos os painéis desse material têm de ter a mesma dimensão.
pattern_directionopcional"none""paralleltowidth"
performance_modeopcional"normal""fast" — resposta mais rápida. "normal" — equilibrado (recomendado). "high" — compactação intensiva para reduzir desperdício, com processamento mais longo.
random_seedopcional0Semente inteira do gerador aleatório. Reutilize o valor para reproduzir disposições idênticas entre pedidos.
can_rotateopcional—Obsoleto. Este ajuste global mantém-se por compatibilidade e impõe a mesma rotação a todas as peças. Omita-o para aplicar os valores can_rotate de cada peça.

Exemplo completo de pedido

{
  "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 do plano gratuito

As contas gratuitas têm estes limites em todos os endpoints de otimização (/api/optimize, /api/optimize/pdf, /api/optimize/dxf, /api/optimize/svg, /api/optimize/csv, /api/optimize/ptx e todas as variantes from-dxf e from-csv):

  • 25 peças por pedido — a soma dos valores part.quantity no array parts não pode exceder 25. Exceder devolve 400.
  • 25 pedidos de otimização por dia — contagem por conta, reiniciada à meia-noite UTC. Exceder devolve 429.
POST/api/optimize

Executa o otimizador e devolve a disposição completa em JSON. Não cria ficheiros; use este endpoint para processar ou apresentar resultados na sua aplicação.

Estrutura da resposta

{
  "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
      }
    }
  ]
}

A resposta contém uma entrada results por material. Cada entrada inclui:

  • layouts — um objeto por painel utilizado; cuts lista posição (x, y) e dimensão de cada peça
  • summary — total_stock_used, total_parts, material_efficiency (0–100 %) e total_cut_length_mm

stock_width e stock_height descrevem a área útil, após retirar trim_stock_plate das quatro bordas. As coordenadas cut partem do canto superior esquerdo dessa área. part_id é o índice da peça no seu grupo de material, não um identificador global.

cut_length_mm tem duas medidas, indicadas por cut_length_method: "guillotine" é o percurso da serra; "contour", o perímetro percorrido pela fresa. São grandezas físicas diferentes: nunca as some nem compare entre si.

stock_index é a posição do painel de origem em stock_plates. offcuts são os restos sem peças deixados inteiros pelos cortes, incluindo pequenos resíduos. Ao contrário de cuts, posicionam-se na área original. As dimensões físicas já descontam o corte e incluem bordas não aparadas.

saw_plan representa o mesmo plano como árvore no painel original. Uma região "split" é dividida por cortes paralelos: cuts indica o início das faixas ao longo de axis ("x": longitudinal; "y": transversal). Cada faixa mede blade_width. children tem uma entrada por intervalo, ou null quando nada resta. As folhas são peças "part", com cut_index a apontar para cuts, e restos "offcut". O valor null significa que não existe sequência de cortes retos, como em certas disposições "cnc".

POST/api/optimize/pdf

Otimiza e gera um PDF com várias páginas, uma por painel. Guarda-o na nuvem e devolve um URL de transferência assinado.

Pedido

Mesmo corpo JSON que /api/optimize (ver acima).

Resposta

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

A resposta contém um único PDF com todos os painéis.

POST/api/optimize/dxf

Otimiza e gera um DXF por painel, compatível com a maioria dos sistemas CAD/CAM e controladores CNC (AutoCAD, Fusion 360, Mach3, etc.). Devolve URLs individuais dos ficheiros guardados.

Pedido

Mesmo corpo JSON que /api/optimize (ver acima).

Resposta

{
  "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"
    }
  ]
}

Um ficheiro por painel. O nome inclui o índice e a execução: plate-1-25318.dxf corresponde ao painel 1 da execução 25318.

POST/api/optimize/csv

Otimiza e gera um CSV com todas as peças de todos os materiais e painéis. Cada linha inclui posição, dimensões da peça e painel, nome e código do material, para folhas de cálculo, ERP ou pós-processadores. Guarda o ficheiro na nuvem e devolve o URL.

Pedido

Mesmo corpo JSON que /api/optimize (ver acima).

Colunas CSV

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

Resposta

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

Um único CSV com todos os cortes de todos os materiais e painéis.

POST/api/optimize/svg

Otimiza e gera um SVG por painel. Estes gráficos vetoriais escaláveis servem para pré-visualizações web e importação no Inkscape, Illustrator ou Figma. Devolve URLs individuais dos ficheiros guardados.

Pedido

Mesmo corpo JSON que /api/optimize (ver acima).

Resposta

{
  "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
Beta

A exportação PTX está em fase beta e ainda não foi validada em máquinas reais. Verifique os ficheiros exportados na sua máquina antes de os utilizar em produção.

Otimiza e gera um PTX (Pattern Exchange) por grupo de material. Este formato para serras HOLZMA / HOMAG é aceite pelo HolzLink, CADmatic 4/5 e HOMAG intelliDivide. Cada ficheiro contém o trabalho do material, padrões e sequências de corte. Devolve os URLs dos ficheiros na nuvem.

Pedido

Mesmo corpo JSON que /api/optimize (ver acima). optimize_type: "saw" gera disposições de guilhotina adequadas a serras de painéis.

Resposta

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

Um PTX por grupo de material. Cada ficheiro inclui:

  • HEADER, uma linha JOBS e uma linha MATERIALS — metadados do programa, espessura, largura de corte e aparagem.
  • BOARDS + PATTERNS — um par por disposição distinta, com dimensões originais e número de painéis que a usam. Disposições iguais são combinadas.
  • PARTS_REQ + PARTS_INF + PARTS_UDI — linhas correspondentes a cada geometria distinta no padrão, numeradas em todo o trabalho. As quantidades incluem todos os painéis repetidos. Descrições e etiquetas de bordas opcionais ficam vazias; o código do material é DSP.
  • CUTS — instruções de corte inicial, longitudinal, transversal, recorte e aparagem; uma sequência por padrão com referências às peças necessárias.

Espessura predefinida: 18 mm, comum em painéis para mobiliário. Medidas em milímetros. Programas e ficheiros usam J<timestamp>.PTX, com identificador numérico único por material.

Uma disposição aninhada "cnc" nem sempre é cortável com serra. Nesse caso, o endpoint devolve 422 em vez de um ficheiro.

Importação de listas de peças CSV

Os endpoints from-csv aceitam multipart/form-data em vez de JSON. Envie uma lista de uma folha de cálculo: o servidor cria parts a partir das linhas. Materiais, painéis, largura de corte e restantes ajustes seguem como texto JSON em params.

Formato CSV

↓ Transferir modelo / exemplo CSVTodos os campos suportados e 10 peças de exemplo com quantidades variadas. Substitua os códigos de material pelos seus.

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

Como as linhas se tornam peças

  • O cabeçalho é obrigatório. Maiúsculas, espaços exteriores, hífenes e sublinhados são ignorados nos nomes: Material Code, Width, Height, Quantity, Can Rotate.
  • Material Code é associado a identifier de uma entrada em materials. Um código desconhecido faz falhar o pedido; o erro indica a linha.
  • Can Rotate é opcional: 1 permite rodar 90°, 0 não. Célula vazia ou coluna ausente significa sem rotação, para preservar o veio.
  • Material Code, Width, Height e Quantity são obrigatórios em cada linha.
  • São suportados vírgula, ponto e vírgula, tabulação, BOM UTF-8 e texto entre aspas. Dimensões API são milímetros inteiros positivos; quantidades são inteiros positivos.

Orlagem de cada lado

Para Top, Right, Bottom e Left, use Edge Band (código), Edge Thickness (mm), Pre Milling (mm removidos) e Edge Method. Exemplos: Top Edge Band, Top Edge Thickness. Os lados descrevem a peça antes de rodar.

Deixe vazios os lados não processados. Métodos: none, band, pre_mill_band. O método omitido é inferido do código, espessura e fresagem. Lados orlados exigem espessura positiva; a API não consulta o catálogo. O importador da oficina pode copiar dele uma espessura ausente.

Dimension Mode aceita blank (predefinido) ou finished. O otimizador compensa espessura e pré-fresagem. Limites de máquina opcionais: Banding Min Length, Banding Min Cross Size, Pre Milling Min Length, Pre Milling Min Cross Size, em mm; zero desativa o limite. Processing Order lista cada lado uma vez, como top right bottom left. Erros indicam a linha.

A oficina também importa Part Code, Part Name, Grain (none, paralleltowidth, paralleltolength), Priority e Notes. A API CSV sem estado ignora estes metadados da encomenda.

Campos do pedido (multipart)

CampoEstadoTipoDescrição
fileobrigatório—Lista de peças CSV com o cabeçalho indicado acima.
paramsobrigatório—Parâmetros de otimização JSON (tabela abaixo). Todos os campos de OptimizeRequest, exceto parts.

Campos JSON de «params»

CampoEstadoValor predefinidoDescrição
materialsobrigatório—É necessário pelo menos um material. Cada identifier corresponde ao Material Code de uma linha.
stock_platesobrigatório—Como em /api/optimize; é necessário pelo menos um painel.
optimize_typeobrigatório—"saw" ou "cnc" (como /api/optimize).
blade_widthopcional5Largura de corte em mm. Os imports aceitam 0, ao contrário do endpoint JSON.
trim_stock_plateopcional10Como em /api/optimize.
allow_mixed_stock_sizesopcionaltrueComo em /api/optimize.
pattern_directionopcional"none"Como em /api/optimize.
performance_modeopcional"normal"Como em /api/optimize.
random_seedopcional0Como em /api/optimize.

Exemplo 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
}

Exemplo de pedido (curl)

# resultado 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 — o formato de exportação vem antes do sufixo «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"}'

# Mesmo padrão para /svg/from-csv /dxf/from-csv /csv/from-csv /ptx/from-csv

Endpoints

POST/api/optimize/from-csv

Lê CSV, otimiza e devolve a disposição JSON completa com a estrutura de /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

Mesmo import com exportação. {format} é pdf, svg, dxf, csv ou ptx. A resposta usa { "files": [...] } com URLs assinados.

Beta

A exportação PTX está em fase beta e ainda não foi validada em máquinas reais. Verifique os ficheiros exportados na sua máquina antes de os utilizar em produção.

Otimização com importação DXF

Os endpoints from-dxf aceitam multipart/form-data em vez de JSON. O servidor extrai contornos exteriores DXF sem array parts manual. Cada contorno fechado gera uma peça com width e height do retângulo delimitador alinhado aos eixos. Todos os ajustes seguem como JSON em params.

Extração de peças DXF

  • As entidades fechadas LWPOLYLINE, POLYLINE, ELLIPSE e SPLINE no nível superior, sem contorno envolvente, tornam-se peças.
  • width e height são medidas do retângulo alinhado aos eixos, arredondadas a inteiros em unit (mm por defeito). Ignoram-se peças abaixo de 1 unidade em qualquer dimensão.
  • Todas as peças recebem o material material_id; por defeito, o id da primeira entrada de materials.
  • Contornos interiores (furos, recortes, ranhuras) são ignorados. Só o contorno exterior interessa à otimização.

Campos do pedido (multipart)

CampoEstadoTipoDescrição
fileobrigatório—Bytes do ficheiro DXF (R12–2018), com extensão .dxf obrigatória.
paramsobrigatório—Parâmetros JSON (tabela abaixo): todos os campos de OptimizeRequest exceto parts, mais unit e material_id.

Campos JSON de «params»

CampoEstadoValor predefinidoDescrição
materialsobrigatório—Como em /api/optimize; é necessário pelo menos um material.
stock_platesobrigatório—Como em /api/optimize; é necessário pelo menos um painel.
optimize_typeobrigatório—"saw" ou "cnc" (como /api/optimize).
unitopcional"mm""cm"
material_idopcionalid do primeiro materialIdentificador de material para todas as peças DXF. Por defeito, o id da primeira entrada de materials.
blade_widthopcional5Largura de corte em mm. Os imports aceitam 0, ao contrário do endpoint JSON.
can_rotateopcionaltrueAplica-se a todas as peças DXF, pois o desenho não indica rotação por peça.
trim_stock_plateopcional10Como em /api/optimize.
allow_mixed_stock_sizesopcionaltrueComo em /api/optimize.
pattern_directionopcional"none"Como em /api/optimize.
performance_modeopcional"normal"Como em /api/optimize.
random_seedopcional0Como em /api/optimize.

Exemplo 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
}

Exemplo de pedido (curl)

# resultado 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 — use o endpoint de exportação 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"}'

# Mesmo padrão para /svg/from-dxf /dxf/from-dxf /csv/from-dxf /ptx/from-dxf

Endpoints

POST/api/optimize/from-dxf

Lê DXF, otimiza e devolve a disposição JSON completa com a estrutura de /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

Lê DXF, otimiza e devolve um plano PDF, uma página por painel, por URL assinado.

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

Lê DXF, otimiza e devolve um SVG por painel por URLs assinados.

{
  "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

Lê DXF, otimiza e devolve uma disposição DXF por painel por URLs assinados.

{
  "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

Lê DXF, otimiza e devolve uma única lista de corte CSV por URL assinado.

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

A exportação PTX está em fase beta e ainda não foi validada em máquinas reais. Verifique os ficheiros exportados na sua máquina antes de os utilizar em produção.

Lê DXF, otimiza e devolve ficheiros PTX para serras HOLZMA / HOMAG por URLs assinados.

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

Pré-validação CNC

Antes do fabrico, o pré-validador CNC analisa um ficheiro DXF segundo os limites da máquina. Extrai contornos e operações interiores (furos, recortes, ranhuras) e deteta violações das regras geométricas.

CódigoGravidadeDescrição
PART_TOO_SMALLerroO retângulo delimitador é menor que part_min_width × part_min_height.
PART_TOO_LARGEerroO retângulo delimitador excede part_max_width × part_max_height.
OPERATION_OUTSIDE_PARTerroUm ou mais vértices de uma operação ficam fora da peça.
OPERATION_INTERSECTS_EDGEerroAs arestas da operação cruzam o limite mesmo com vértices no interior.
OPERATION_TOO_CLOSE_TO_EDGEerro / avisoA distância à borda é menor que min_edge_offset. Erro se < ½ limite; aviso se < limite.
DRILL_DIAMETER_TOO_SMALLerroO diâmetro do furo é menor que drill_min_diameter.
DRILL_DIAMETER_TOO_LARGEerroO diâmetro do furo excede drill_max_diameter.
OPERATIONS_INTERSECTerroDuas operações na mesma face sobrepõem-se.
OPERATIONS_TOO_CLOSEerro / avisoA distância entre superfícies de operações é menor que min_op_spacing. Erro se < ½ limite; aviso se < limite.
POST/api/validate-cnc

Envie DXF como multipart/form-data. A resposta é um ValidationReport completo: um PartResult por peça com operações e violações.

Campos do pedido (multipart)

CampoEstadoTipoDescrição
fileobrigatório—Bytes DXF. São suportadas quase todas as revisões binárias R12–2018.
configopcionalvalores predefinidosObjeto ValidationConfig em JSON (tabela abaixo). Todos os campos são opcionais; os omitidos usam o valor predefinido.

Campos de ValidationConfig

CampoEstadoValor predefinidoDescrição
part_min_widthopcional50Largura mínima permitida da peça em mm.
part_min_heightopcional50Altura mínima permitida da peça em mm.
part_max_widthopcional2800Largura máxima permitida da peça em mm.
part_max_heightopcional2070Altura máxima permitida da peça em mm.
drill_min_diameteropcional2Diâmetro mínimo da broca em mm.
drill_max_diameteropcional35Diâmetro máximo da broca em mm.
min_edge_offsetopcional8Distância mínima (mm) de uma aresta de operação ao limite da peça.
min_op_spacingopcional8Distância mínima entre superfícies de duas operações em mm.
groove_aspect_ratioopcional3.0Um contorno interior fechado é uma ranhura, e não um recorte genérico, se o lado longo ≥ esta proporção × o lado curto.
unitopcional"mm""cm"

Exemplo de pedido (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"}'

Exemplo de resposta

{
  "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
    }
  ]
}

Principais campos da resposta:

  • valid — true só se nenhuma peça tiver problemas de gravidade erro.
  • summary — totais: total_parts, total_operations, errors, warnings.
  • parts[] — um objeto por peça detetada com retângulo delimitador, operações e problemas.
  • issues[].severity — "error" (bloqueante) ou "warning" (apenas informativo).

Predefinições CNC

Guarde predefinições ValidationConfig com nome para validações futuras. São privadas da sua conta.

GET/api/cnc-configs

Devolve todas as predefinições do utilizador autenticado.

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

Cria uma predefinição com nome. Devolve 201 Created com a entrada guardada.

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}

Substitui nome e/ou configuração. O corpo tem a estrutura de POST. Devolve 200 OK com a entrada atualizada ou 404 se o ID não existe ou pertence a outro utilizador.

DELETE/api/cnc-configs/{id}

Elimina a predefinição permanentemente. Devolve 204 No Content se bem-sucedido ou 404 se não existe.

Respostas de erro

Todos os endpoints devolvem erros no mesmo invólucro JSON:

{ "message": "A part (800×600) does not fit any stock plate" }
  • 400 — limite de peças excedido: a soma de quantity supera o limite por pedido do plano. Também indica CSV inválido ou campo multipart ausente.
  • 401 — token Bearer ausente, expirado ou revogado.
  • 422 — uma peça não cabe em nenhum painel ou faltam painéis. A otimização é completa ou falha; nunca devolve disposição parcial. A mensagem identifica peça ou restrição. PTX também devolve 422 se a disposição não puder ser serrada.
  • 429 — limite diário atingido. Reinicia à meia-noite UTC.
  • 500 — erro interno do servidor, como falha no envio do ficheiro.