# Promax Studio: guia completo da API para IA > Promax Studio é o software de projeto de móveis planejados no navegador. Esta API deixa o ChatGPT e o Claude lerem e desenharem nos projetos de uma empresa (ambientes, paredes, portas e janelas, móveis, eletros, tampos e peças avulsas) com as mesmas regras da tela, e lerem a lista de peças, as ferragens e o DXF de cada peça. Tudo em mm. Endereço base: https://promaxstudio.com.br. Se você leu este arquivo de outro endereço (um ambiente de teste), use o começo de onde ele veio: o guia, a API e o MCP moram no mesmo lugar. - API REST: `/api/v1` (exemplo: `GET /api/v1/catalog`). - Documento OpenAPI 3.1: `/api/v1/openapi.json` (público). - Servidor MCP: `/api/v1/mcp` (só POST). - Este guia: `/llms-full.txt`; o resumo: `/llms.txt`. ## 1. A chave da empresa - Toda chamada à API leva a chave no cabeçalho: `Authorization: Bearer pmxs_...`. A documentação é pública; os dados não. - Quem cria: o dono ou o administrador da empresa, em Empresa > Integrações, com a senha de novo. A chave aparece uma vez só, na criação: copie na hora. - Escopo: "Ler e mudar projetos" (escrita) ou "Só ler" (leitura). A chave de leitura lê tudo e pode "só conferir" (dryRun); gravar com ela dá 403 `api_key_read_only`. - Validade: 30 dias, 90 dias, 1 ano ou sem validade. Revogar corta o acesso no próximo pedido. - A chave age em nome de quem criou. Se essa pessoa sair da empresa ou deixar de ser dona ou administradora, as chaves dela param de valer. - Toda gravação feita com a chave aparece no registro de atividades da empresa, com o nome da chave. - Nunca mostre a chave na conversa nem a grave em arquivo que outra pessoa lê. ## 2. Ligar o ChatGPT 1. Crie a chave (escrita, se o GPT for desenhar). 2. Num GPT personalizado, em Ações > Criar nova ação, importe por endereço: `https://promaxstudio.com.br/api/v1/openapi.json`. 3. Autenticação: "Chave de API", tipo Bearer, e cole a chave. 4. Nas instruções do GPT, peça que ele leia este guia e siga o roteiro da seção 5. Sugestão: "Você desenha móveis planejados no Promax Studio. Medidas em mm. Antes de desenhar, chame getCatalog e getProject. Mande os comandos primeiro com dryRun=true; só grave com a baseRevision da última leitura. Ajuste pendente: aplique um dos consertos da resposta (fix). Tampo pede espessura e material: pergunte se a pessoa não disse." ## 3. Ligar o Claude - Claude Code: `claude mcp add --transport http promax-studio https://promaxstudio.com.br/api/v1/mcp --header "Authorization: Bearer pmxs_..."`. - Claude Desktop: no arquivo de configuração dele, um servidor com `"command": "npx"`, `"args": ["mcp-remote", "https://promaxstudio.com.br/api/v1/mcp", "--header", "Authorization: Bearer ${PROMAX_KEY}"]` e `"env": { "PROMAX_KEY": "pmxs_..." }`. - Conector do claude.ai (pelo navegador): ainda não funciona. Ele pede OAuth, que o Promax Studio ainda não tem; está na lista do que vem depois. Até lá, use o Claude Code, o Claude Desktop ou a API REST. - Sem MCP, qualquer agente com HTTP usa a API REST da seção 7, com o mesmo cabeçalho. ## 4. Como o projeto é organizado - **Projeto:** código `PRJ-0001`, nome e **revisão** (um número que sobe a cada gravação). Gravar pede a revisão da última leitura (`baseRevision`). - **Ambiente:** código `AMB-01`, `AMB-02`... (é o que vai no caminho das operações do ambiente) e tipo (`kitchen`, `bedroom`, `closet`, `bathroom`, `commercial`, `generic`). - **Paredes:** o ambiente é uma corrente de paredes, chamadas "parede 1", "parede 2"... na ordem. No resumo, cada parede traz `id` (o que vai nos comandos), `label` ("parede 1"), `length` (comprimento da face de dentro), `thickness`, `height`, `openings` (portas e janelas), `startCorner`, `leftWallId` e `rightWallId`. - **O lugar de um item na parede** (móvel, eletro de piso, tampo, peça avulsa): `offset` é a medida ao longo da face de dentro, a partir do **canto de início** da parede até o lado do item mais perto dele; o item ocupa de `offset` a `offset + largura`. - **De que lado fica o canto de início:** `startCorner` diz, pra quem está dentro do ambiente de frente pra parede, se o offset 0 é o canto da direita (`right`) ou da esquerda (`left`). O resumo traz também `placementHint` com a regra em português. `leftWallId` e `rightWallId` são as paredes vizinhas da esquerda e da direita, nessa mesma vista. - **A sala retangular criada pela API** (`createEnvironment` com `width` e `depth`) tem as paredes no sentido anti-horário, vista de cima: parede 1 embaixo (comprimento `width`), parede 2 à direita (`depth`), parede 3 em cima (`width`) e parede 4 à esquerda (`depth`). Em todas, `startCorner` é `right`: pra quem olha a parede de dentro, o offset 0 é o canto da direita e a medida cresce pra esquerda. - "Encostado no canto direito da parede 1": `offset = 0`. - "Encostado no canto esquerdo da parede 1": `offset = comprimento da parede - largura do item`. - "Da esquerda pra direita: A, B, C" na parede 1: C é o que fica com o offset menor. - O canto da esquerda da parede 1 é o mesmo canto do começo da parede 2 (`leftWallId` da parede 1 é a parede 2). Na planta vista de cima (y pra cima), cada canto é o início de uma parede: ``` parede 3 início da 4 +--------------------+ início da 3 | | parede 4 | (lado de dentro) | parede 2 | | início da 1 +--------------------+ início da 2 parede 1 ``` Na planta, a parede 1 vai da esquerda pra direita; mas quem está dentro, de frente pra ela, vê o início dela à direita. É essa vista de dentro que vale pra "canto direito", "canto esquerdo" e "da esquerda pra direita". - **Códigos dos itens:** balcão `B01`, aéreo `A01`, torre `T01`, painel `P01`; eletro `ELE-0001`; tampo `TPO-0001`; peça avulsa `PEC-0001`. Os números são do projeto inteiro; o servidor preenche quando faltam. - **Medidas do catálogo:** `getCatalog` traz cada modelo com as medidas do padrão da empresa. Os modelos de partida costumam ser: balcão de 870 de altura e 560 de fundo (o tampo vai em cima); aéreo de 700 de altura e 330 de fundo, a 1500 do piso; torre de 2200 de altura e 580 de fundo; geladeira de exemplo de 700 de largura, 1850 de altura e 720 de fundo. Use sempre as do catálogo da empresa, não estas. ## 5. Roteiro: "desenhe estes móveis neste ambiente" 1. **Ache o projeto e o ambiente.** `listProjects` (ou `list_projects`) com `q` pra procurar pelo nome; `getProject` (ou `get_project`) pra ler o resumo: a `revision`, os ambientes (`code`, nome), as paredes (`id`, `label`, `length`, `startCorner`) e os itens que já existem (`items`, com `code`, `wallId`, `offset`, `width`). Projeto novo: `createProject` e depois `createEnvironment` com `baseRevision: 0`. 2. **Leia o catálogo.** `getCatalog` (ou `get_catalog`): em `modules`, cada `catalogItem` com o nome, o corpo (`base` balcão, `wall` aéreo, `tall` torre, `panel` painel), os limites e o `template` com as medidas e as zonas; em `appliances`, cada eletro (`kind`) e onde ele fica (`placementKind`); em `commands`, o guia de todos os comandos com os campos. Esse guia é a fonte de verdade dos campos de cada comando. 3. **Planeje as medidas em cada parede.** Some as larguras e confira que cabem no `length` da parede. Encoste um item no outro: o próximo começa onde o anterior termina (`offset` do anterior + largura dele). Respeite portas e janelas (`openings`, com `distanceFromStart` e `width` medidos do mesmo canto de início). 4. **Monte o lote.** Cada móvel é `addModule` com `catalogItem`, `placement: { wallId, offset }` e só o que muda do modelo (normalmente `width`). Eletro é `addAppliance` com `kind` e `placement`. Tampo é `addCountertop`. Quando um eletro vai num móvel do mesmo lote, dê um `id` ao móvel (letras, números, `-` e `_`) e use ele no `moduleId` do eletro. 5. **Confira sem gravar.** `applyCommands` com `"dryRun": true` (ou `apply_commands` com `dry_run: true`). Leia a resposta: - erro 422 com `code`, `message` e `index` (o comando do lote que falhou, contando de 0): corrija e confira de novo; - `warnings`: avisos que não seguram a produção; - `adjustments`: ajustes pendentes (seguram a produção), cada um com os consertos; - `openingIssues`: porta, gaveta ou eletro que bate em algo ao abrir; não segura a produção. Cada aviso tem `key` e `fixes` (trocar a dobradiça de lado, pôr um tamponamento da medida que falta, afastar o eletro), aplicados como os ajustes (passo 7). Só os 3 primeiros avisos da resposta trazem a lista de consertos; os outros vêm com `fixes: null` e ganham a lista quando os primeiros forem resolvidos (confira de novo). Aplique o conserto que fizer sentido, ou avise a pessoa; - `buildErrors`: conta de peça que não fecha (por exemplo, a dobradiça do lado do canto cego). 6. **Grave.** O mesmo lote com `"baseRevision": ` no lugar do `dryRun`. A resposta traz a revisão nova, que vale pra próxima gravação. Revisão velha é 409 `revision_conflict`: leia o projeto de novo e refaça em cima do que está lá. 7. **Resolva os ajustes pendentes** (e, se quiser, os avisos de abertura). Pra cada item de `adjustments` (ou de `openingIssues`), escolha um conserto de `fixes` (cada um com `index`, `kind` e `label`, como "Diminuir o B05 em 50 mm") e aplique com `{ "fix": { "key": "", "index": }, "baseRevision": }`. Um conserto por pedido; depois de cada um, a lista muda (leia a resposta). 8. **Confira o resultado.** `getProject` sem `pendingAdjustments`, e `getParts` (ou `get_parts`) devolvendo a lista de peças. Com ajuste pendente, `getParts` responde 409 `production_blocked`. ### Regras de desenho que evitam erro - **Ordem dos itens:** quando a pessoa lista os itens de uma parede sem dizer o sentido ("na parede 1: pia, gaveteiro e balcão"), leia da esquerda pra direita, vistos de dentro, como se lê. Item preso num canto ("geladeira no canto direito") fica no canto, e os outros encostam nele na ordem da lista. Diga na resposta a ordem que ficou, da esquerda pra direita. - **Balcões numa parede:** um ao lado do outro, `base-sink` (pia), `base-drawers` (gaveteiro), `base-doors` (portas). A largura de cada um é a que a pessoa disse ("pia de 80" é `width: 800`). - **Aéreos em cima:** `wall-doors` com os mesmos `offset` e `width` dos balcões de baixo; a altura do piso vem do catálogo. Em cima da geladeira (que é mais alta), o aéreo vai mais alto e mais baixo de altura, e vem pra frente: `placement.elevation` acima da geladeira (1850 + 50 = 1900), `height` que caiba até o teto (por exemplo 600) e `placement.fromWall` = fundo da geladeira - fundo do aéreo (720 - 330 = 390). - **Geladeira:** eletro de piso, `{ "kind": "fridge", "placement": { "kind": "floor", "wallId": "...", "offset": 0 } }`. Encostada numa parede do canto, a porta dela bate na parede vizinha (aparece em `openingIssues`); é normal, avise a pessoa. - **Lava-louças:** eletro de piso entre dois balcões, embaixo do tampo (o tampo passa por cima dela). Não ponha colada no balcão de canto: a porta bate no puxador. - **Cooktop e cuba:** vão sobre o tampo de um balcão: `placement: { "kind": "countertop", "moduleId": "", "offset": }` (no balcão de 800, o meio é 400). O balcão precisa de tampo em cima (senão `appliance_without_countertop`). A cuba vai no balcão de pia; o cooktop, num gaveteiro ou balcão de portas. - **Forno e micro-ondas de embutir:** na torre quente (`tall-oven`), que tem as zonas, de baixo pra cima: 0 gavetas, 1 nicho do forno, 2 nicho do micro-ondas, 3 portas. `{ "kind": "oven", "placement": { "kind": "niche", "moduleId": "", "zoneIndex": 1 } }` e `{ "kind": "microwave", "placement": { "kind": "niche", "moduleId": "", "zoneIndex": 2 } }`. - **Coifa:** eletro de parede, `{ "kind": "hood", "placement": { "kind": "wall", "wallId": "...", "offset": , "elevation": } }`, em cima do cooktop, sem aéreo no mesmo lugar. - **Tampo:** `addCountertop` com `wallId`, `offset` e `length` cobrindo os balcões e a lava-louças daquela parede, `depth` 600 (cobre o balcão de 560) e `thickness` e `material` **sem padrão**: pergunte à pessoa. Se não der pra perguntar e o pedido precisa de tampo (cooktop ou cuba pedem), escolha (por exemplo 20 mm, "Granito") e diga na resposta o que escolheu; se o pedido não precisa, deixe sem tampo e avise. Um tampo por trecho reto. - **Torre e despenseiro** (`tall-oven`, `tall-pantry`): ficam do piso até perto do teto; não ponha aéreo nem tampo no mesmo trecho. - **Medidas da sala:** "cozinha de 3,2 por 2,6" (ou "cozinha em L de 3,2 por 2,6") são as medidas de dentro da sala: `createEnvironment` com `width: 3200` e `depth: 2600`. O L é o desenho dos móveis em duas paredes vizinhas (normalmente a parede 1 e a parede 2). - **Cozinha em L** (duas paredes vizinhas): o canto é o fim de uma parede (a parede N) e o começo da vizinha (a parede N+1, o `leftWallId` da parede N quando `startCorner` é `right`). - Na parede N+1, com `offset: 0`: o balcão de canto (`base-corner`, 1100 de largura) e, se houver aéreos, o aéreo de canto (`wall-corner`). O modelo já vem com o canto cego (`blind`) do lado direito e a dobradiça do outro lado, que é o certo quando `startCorner` é `right`. Se `startCorner` for `left`, mande `blind.side: "left"` e a dobradiça (`zones[0].hingeSide`) `"right"`. - Na parede N, a fileira de balcões vai **até** `length - 578`: o último balcão termina exatamente aí, encostado na frente do balcão de canto (que entra 560 + 18 de frente na parede N), sem folga. Os aéreos vão até `length - 348` (o mesmo pro aéreo de canto). 578 e 348 são o `blind.width` dos modelos de canto do catálogo: use os da empresa. - Tampos: o da parede N+1 começa no canto (`offset: 0`) e cobre o canto; o da parede N termina em `length - 600`, na frente do outro. O último balcão da parede N fica com 22 mm debaixo do tampo da outra parede: é assim mesmo. - Exemplo, sala de 3200 x 2600 com o L nas paredes 1 e 2: parede 2 com `base-corner` em `offset: 0` (de 0 a 1100) e os outros balcões depois dele (de 1100 em diante); parede 1 com os balcões terminando em 3200 - 578 = 2622 (por exemplo torre de 0 a 600, balcão de 600 a 1422, lava-louças de 1422 a 2022, balcão de 2022 a 2622); tampo da parede 2 de 0 até o fim dos balcões dela; tampo da parede 1 de 600 (depois da torre) até 3200 - 600 = 2600. - **Dobradiça:** `hingeSide` (`left` ou `right`) é o lado das dobradiças da porta de uma folha, visto de frente pro móvel; a porta gira em torno desse lado. Na porta de duas folhas (`leaves: 2`), cada folha tem a dela e o campo não muda nada. Quando `openingIssues` diz que uma porta bate, use um dos `fixes` do aviso (o sistema já conferiu que a porta passa a abrir); no canto em L, o normal é o "Pôr tamponamento" no balcão que encosta no balcão de canto. - **Itens que se tocam** são normais; itens que ocupam o mesmo lugar dão `items_overlap`. O balcão de canto ocupa o canto inteiro (o fundo dele entra na outra parede). - **"Só conferir" primeiro, sempre.** O lote é tudo ou nada: com um comando errado, nada é gravado. ### Exemplo completo (cozinha reta) Pedido: "na parede 1 (4000 mm), da direita pra esquerda: geladeira no canto direito, pia de 80, gaveteiro de 60 e balcão de 40; aéreos em cima dos balcões; tampo de granito de 20 mm; cuba na pia". Visto de dentro, da esquerda pra direita fica: balcão de 40, gaveteiro, pia e geladeira. Ids de parede do resumo; aqui `parede-1`. ```json { "dryRun": true, "commands": [ { "type": "addAppliance", "appliance": { "kind": "fridge", "placement": { "kind": "floor", "wallId": "parede-1", "offset": 0 } } }, { "type": "addModule", "module": { "id": "pia", "catalogItem": "base-sink", "width": 800, "placement": { "wallId": "parede-1", "offset": 700 } } }, { "type": "addModule", "module": { "catalogItem": "base-drawers", "width": 600, "placement": { "wallId": "parede-1", "offset": 1500 } } }, { "type": "addModule", "module": { "catalogItem": "base-doors", "width": 400, "placement": { "wallId": "parede-1", "offset": 2100 } } }, { "type": "addModule", "module": { "catalogItem": "wall-doors", "width": 800, "placement": { "wallId": "parede-1", "offset": 700 } } }, { "type": "addModule", "module": { "catalogItem": "wall-doors", "width": 600, "placement": { "wallId": "parede-1", "offset": 1500 } } }, { "type": "addModule", "module": { "catalogItem": "wall-doors", "width": 400, "placement": { "wallId": "parede-1", "offset": 2100 } } }, { "type": "addCountertop", "countertop": { "wallId": "parede-1", "offset": 700, "length": 1800, "depth": 600, "thickness": 20, "material": "Granito" } }, { "type": "addAppliance", "appliance": { "kind": "sink", "placement": { "kind": "countertop", "moduleId": "pia", "offset": 400 } } } ] } ``` Resposta (resumida): `applied: false`, `environment.items` com `ELE-0001` (geladeira), `B01`, `B02`, `B03`, `A01`, `A02`, `A03`, `TPO-0001` e `ELE-0002` (cuba); `adjustments: []`; `openingIssues` com a porta da geladeira que bate na parede 4 (normal no canto). Depois, o mesmo corpo com `"baseRevision": 1` no lugar de `"dryRun": true` grava. ### Exemplo: medida da obra A pessoa conta que a parede 1 mediu 3950 em vez de 4000: ```json { "baseRevision": 2, "commands": [{ "type": "setWallLength", "wallId": "parede-1", "length": 3950 }] } ``` A medida entra mesmo que algum item passe a não caber: ele vira um ajuste pendente, por exemplo: ```json { "adjustments": [ { "key": "item_outside_wall|parede-1||movel-b03||||", "code": "item_outside_wall", "message": "O móvel B03 passa 50 mm do fim da parede 1.", "fixes": [ { "index": 0, "kind": "shrink", "label": "Diminuir o B03 em 50 mm" }, { "index": 1, "kind": "shift", "label": "Recuar o B03 50 mm" } ] } ], "productionBlocked": true } ``` Escolha um conserto (o que a pessoa preferir; sem preferência, o que muda menos) e aplique, copiando a `key` inteira da resposta: `{ "baseRevision": 3, "fix": { "key": "", "index": 0 } }`. Os textos dos consertos (`label`) dizem o que cada um faz: diminuir, encurtar, recuar, levar pro lugar livre mais perto, baixar a altura. Repita até `adjustments` ficar vazio. Mudar a medida de uma parede retangular também muda a paralela (os ângulos ficam). ## 6. Ferramentas do MCP Endereço `/api/v1/mcp`, JSON-RPC 2.0, uma mensagem por POST (lote de mensagens é recusado), com `Accept: application/json, text/event-stream` e `Content-Type: application/json`. O `initialize` devolve um roteiro curto em `instructions`. As ferramentas chamam as mesmas funções da API REST e devolvem JSON em texto; erro volta como resultado com `isError: true` e o texto `{ "error": { "code", "message", "index"?, "path"? } }`. | Ferramenta | Argumentos | Faz o mesmo que | |---|---|---| | `list_projects` | `page?`, `query?`, `archived?` | `listProjects` | | `get_project` | `project` | `getProject` | | `get_environment` | `project`, `environment` | `getEnvironment` | | `get_catalog` | (nenhum) | `getCatalog` | | `create_project` | `name` | `createProject` | | `add_environment` | `project`, `name`, `type?`, `width?`, `depth?`, `ceiling_height?`, `wall_thickness?`, `base_revision` | `createEnvironment` | | `apply_commands` | `project`, `environment`, `commands?` ou `fix?`, `dry_run`, `base_revision?` | `applyCommands` | | `get_parts` | `project`, `environment` | `getParts` | | `get_part_dxf` | `project`, `environment`, `part` | `getPartDxf` | Exemplo de chamada: ```json { "jsonrpc": "2.0", "id": 7, "method": "tools/call", "params": { "name": "apply_commands", "arguments": { "project": "PRJ-0001", "environment": "AMB-01", "dry_run": true, "commands": [{ "type": "addModule", "module": { "catalogItem": "base-doors", "width": 600, "placement": { "wallId": "parede-1", "offset": 0 } } }] } } } ``` ## 7. Operações da API REST Todas debaixo de `/api/v1`, com `Authorization: Bearer pmxs_...`. Corpo em JSON. Campo a mais no corpo é 400 `invalid_body` (com `path`, o campo). Nenhuma resposta vai pra cache. ### whoAmI: `GET /api/v1/me` Confere a chave. Resposta: `{ "company": "Marcenaria Exemplo", "apiKey": "Claude do escritório", "scope": "write" }`. ### getCatalog: `GET /api/v1/catalog` O catálogo: `units`, `howTo`, `modules` (cada um com `catalogItem`, `name`, `body`, `limits`, `startingElevation` e `template`), `zoneKinds`, `appliances` (`kind`, `name`, `placementKind`, `template`), `finishParts`, `environmentTypes`, `buildStandards` (o padrão de construção da empresa), `drawingDefaults` (medidas de partida de porta, janela e parede) e `commands` (o guia de todos os comandos, fonte de verdade dos campos). ### listProjects: `GET /api/v1/projects?page=1&q=cozinha&archived=false` Os projetos, os mexidos por último primeiro, 30 por página: `{ "projects": [{ "code": "PRJ-0007", "name": "Cozinha da Ana", "updatedAt": "..." }], "page": 1, "pageSize": 30, "total": 1 }`. ### createProject: `POST /api/v1/projects` Corpo `{ "name": "Cozinha da Joana" }` (chave de escrita). Resposta 201: `{ "code": "PRJ-0008", "name": "Cozinha da Joana", "revision": 0 }`. ### getProject: `GET /api/v1/projects/{code}` O resumo: `code`, `name`, `revision`, `environments` (cada um com `id`, `code`, `name`, `type`, `ceilingHeight`, `closed`, `interiorSide`, `placementHint`, `walls`, `items` e `pendingAdjustments`) e `nextNumbers`. Exemplo de parede: `{ "id": "parede-AbC1", "label": "parede 1", "length": 4000, "thickness": 150, "height": 2600, "startCorner": "right", "leftWallId": "parede-XyZ2", "rightWallId": "parede-QwE4", "openings": [] }`. Exemplo de item: `{ "kind": "module", "code": "B01", "catalogItem": "base-sink", "body": "base", "wallId": "parede-AbC1", "offset": 700, "elevation": 0, "width": 800, "height": 870, "depth": 560 }`. ### createEnvironment: `POST /api/v1/projects/{code}/environments` Sala retangular ou vazia, com o padrão de construção da empresa: ```json { "name": "Cozinha", "type": "kitchen", "width": 3200, "depth": 2600, "baseRevision": 0 } ``` `width` e `depth` são as medidas de dentro (as duas juntas, ou nenhuma pra desenhar com `drawWall`); `ceilingHeight` e `wallThickness` podem faltar (padrão da empresa). Resposta 201: `{ "applied": true, "revision": 1, "beforeAiVersion": null, "environment": { ...resumo... } }`. ### getEnvironment: `GET /api/v1/projects/{code}/environments/{environment}` A intenção completa do ambiente (`{ "revision": 3, "environment": { ... } }`), com cada móvel inteiro (zonas, canto cego, anotação). Use antes de `updateModule` e parecidos: o item vai inteiro, com o mesmo `id` e `number`. ### applyCommands: `POST /api/v1/projects/{code}/environments/{environment}/commands` Lote de comandos (`commands`, até 550) ou um conserto (`fix`, de `adjustments` ou de `openingIssues`), nunca os dois no mesmo pedido; tudo ou nada. `"dryRun": true` confere sem gravar (serve com a chave de leitura); gravar pede `"baseRevision"`. Resposta: `applied`, `revision`, `beforeAiVersion`, `environment` (o resumo), `warnings`, `adjustments`, `openingIssues`, `buildErrors` e `productionBlocked`. Exemplos na seção 5. ### getParts: `GET /api/v1/projects/{code}/environments/{environment}/parts` A lista de peças (`parts`, com medidas prontas e de corte, material e fita), as ferragens (`hardware`), as compras (`purchases`: tampos com recortes e eletros), a fita em metros (`edgeBands`), a chapa em m² (`sheets`) e `issues`. Com ajuste pendente: 409 `production_blocked`. ### getPartDxf: `GET /api/v1/projects/{code}/environments/{environment}/parts/dxf?part=B01%20LAT-E` O DXF de uma peça, em texto: `{ "fileName": "...", "text": "0\nSECTION..." }`. `part` é o código da lista de peças. ## 8. Os comandos Os campos de cada comando estão em `GET /api/v1/catalog`, no campo `commands` (a fonte de verdade). Resumo: - Ambiente: `rename` (`name`), `setEnvironmentNotes` (`notes`), `setCeilingHeight` (`ceilingHeight`). - Paredes: `drawWall` (`length`, `angle` em graus, `thickness`; `start: { x, y }` só na primeira; `wallId` e `endVertexId` opcionais), `closeContour` (`thickness`), `moveVertex` (`vertexId`, `x`, `y`), `setWallLength` (`wallId`, `length`), `setWallThickness` (`wallId`, `thickness`), `setWallHeight` (`wallId`, `height` ou null), `removeWall` (`wallId`), `flipInteriorSide`. - Portas e janelas: `addOpening` (`wallId`, `opening: { kind door|window|passage, distanceFromStart, width?, height?, sillHeight?, hingeSide?, openDirection? }`), `updateOpening` (`wallId`, `openingId`, `changes`), `moveOpeningToWall` (`fromWallId`, `openingId`, `toWallId`, `distanceFromStart`), `removeOpening` (`wallId`, `openingId`). - Padrão de construção: `setStandards` (`standards` opcional: sem ele, o da empresa; o servidor já põe o da empresa no ambiente que não tem). - Móveis: `addModule` (`module`), `updateModule` (`module` inteiro), `removeModule` (`moduleId`). - Eletros: `addAppliance` (`appliance`), `updateAppliance` (`appliance` inteiro: muda o lugar e o lado da porta, nunca as medidas), `removeAppliance` (`applianceId`). - Tampos: `addCountertop` (`countertop`), `updateCountertop` (`countertop` inteiro), `removeCountertop` (`countertopId`). - Peças avulsas: `addFinishPart` (`finishPart: { kind filler|finish_side|shelf|slatted_panel|free, placement, ... }`), `updateFinishPart`, `removeFinishPart` (`finishPartId`). Um lote mistura o que precisar. O lote **só de planta** (paredes, cantos, vãos, pé direito) é conferido a cada passo, como se fossem mandados um por um, e vai até 50 comandos; num ambiente grande, menos (o erro `batch_too_heavy` diz quantos cabem). Com um comando de móvel junto, o lote é conferido uma vez só, no fim. ## 9. Erros Formato: `{ "error": { "code": "...", "message": "...", "index"?: 0, "path"?: "commands[0].type" } }`. O `code` é estável; a `message` é pra mostrar à pessoa. | HTTP | code | O que fazer | |---|---|---| | 400 | `invalid_body` | O corpo não está no formato (`path` diz o campo; `index`, o comando). | | 400 | `base_revision_required` | Gravar pede `baseRevision`. | | 401 | `api_key_missing`, `api_key_invalid` | Falta a chave, ou ela não vale (inexistente, vencida, revogada, de quem saiu). | | 403 | `api_key_read_only` | A chave só lê. | | 403 | `company_locked` | A empresa pediu a exclusão. | | 404 | `not_found`, `environment_not_found`, `part_not_found` | O projeto, o ambiente ou a peça não existe (projeto de outra empresa também dá 404). | | 404 | `fix_not_found` | O conserto não existe mais (o ambiente mudou): leia de novo. | | 409 | `revision_conflict` | O projeto mudou desde a sua leitura: leia de novo e refaça. | | 409 | `production_blocked` | Há ajuste pendente: resolva antes da lista de peças. | | 409 | `project_archived` | Projeto arquivado: só a tela desarquiva. | | 413 | `environment_too_large`, `project_too_large`, `payload_too_large` | Grande demais: divida em mais ambientes ou lotes. | | 422 | (código do motor) | O comando foi recusado; `index` diz qual. | | 429 | `too_many_requests`, `too_many_writes`, `too_much_work`, `too_many_attempts` | Limite: espere os segundos do cabeçalho `Retry-After`. | Códigos do motor mais comuns (422, ou dentro de `adjustments`, `warnings` e `openingIssues`): - Lugar: `items_overlap` (dois itens no mesmo lugar), `item_outside_wall` (passa do fim da parede), `item_crosses_wall`, `item_above_ceiling`, `item_covers_opening` (na frente de porta), `item_in_front_of_window` (aviso), `item_wall_missing`. Os cinco primeiros, quando nascem de uma medida de parede, viram ajuste pendente com consertos. - Eletro: `appliance_without_countertop` (cooktop ou cuba sem tampo no balcão), `appliance_needs_base`, `appliance_outside_module`, `appliance_zone_not_niche`, `appliance_doesnt_fit`, `appliance_module_missing`, `appliance_size_fixed` (as medidas do eletro são as do catálogo e não mudam; só o lugar e o lado da porta). - Móvel: `invalid_module` (medida fora dos limites ou campo inválido; a mensagem diz qual), `invalid_zone`, `module_size_locked` (o móvel está com as medidas travadas: dá pra mudar o lugar, não o tamanho), `door_too_short`, `door_too_tall`, `drawer_too_small`. - Tampo: `invalid_countertop`, `countertop_without_base`, `countertop_uneven`, `countertop_base_ahead`. - Abertura: `door_blocked`, `drawer_blocked` (bate em algo ao abrir). - Paredes e vãos: `walls_cross`, `walls_fold_back`, `wall_too_short`, `invalid_length`, `invalid_angle`, `opening_outside_wall`, `opening_above_wall`, `too_many_misplaced` (a medida deixaria itens demais fora do lugar: confira o número). - Lote e números: `too_many_commands`, `batch_too_heavy`, `nested_batch`, `unknown_command`, `duplicate_id`, `duplicate_number`, `not_found` (o id do comando não existe no ambiente), `no_standards`. Outros códigos podem aparecer; a `message` em português sempre diz o que aconteceu. ## 10. Limites Quando passa, a resposta é 429 com o cabeçalho `Retry-After` (os segundos que faltam): - 120 pedidos por minuto por chave; - 600 gravações por hora por chave; - o trabalho de conferência do motor, por chave e por empresa somando as chaves ("só conferir" conta): mande lotes maiores e menos vezes, em vez de um comando por pedido; - chave errada demais vinda do mesmo endereço: espere alguns minutos; - criar projeto também conta no limite de gravações da pessoa dona da chave. ## 11. Versão "Antes da IA" e como desfazer - Antes da primeira gravação pela API num projeto que já existia, e de novo quando a API volta depois de 30 minutos sem gravar nele, o servidor guarda uma versão do projeto com o nome "Antes da IA (chave )". A resposta da gravação traz o número dela em `beforeAiVersion` (null quando não precisou). No projeto que a própria API criou não há "antes": a versão só aparece quando a API volta a ele depois de 30 minutos. - Pra desfazer o que a IA fez: na tela do projeto, no histórico de versões, restaure a versão "Antes da IA". Restaurar nunca apaga: o estado de agora também vira uma versão. A API não restaura versão (só a tela). - Também dá pra desfazer pedaços pela API, com os comandos de apagar (`removeModule` e parecidos) ou de troca. - Toda gravação pela API aparece no registro de atividades da empresa ("Criou o projeto pela API", "Mudou o projeto pela API"), com o nome da chave. ## 12. O que ainda não dá - Móvel de receita nova (o catálogo próprio da empresa) chega numa etapa seguinte; até lá, componha com o catálogo base, que é parametrizado (largura, altura, profundidade, zonas de portas, gavetas e nichos, canto cego), e com as peças avulsas. - PDF técnico pela API; arquivar, renomear o projeto, restaurar versão e mexer na equipe ou nos padrões da empresa: só na tela. - Conector do claude.ai: pede OAuth (seção 3).