# Convenções da API do GransoftWeb

Regras que valem para todos os métodos. Código que ignora estes pontos costuma funcionar no primeiro teste e falhar depois.

## 1. Requisição

| Item | Regra |
|---|---|
| Endereço | `{Servidor}/API/{Metodo}`. O `Servidor` vem da autenticação. |
| Método HTTP | `POST`. |
| Corpo | JSON, com `Content-Type: application/json`. |
| `userHash` | No corpo, campo `userHash`. Em `ObterLista`, na query string. |
| Campos omitidos | Podem ser omitidos. O servidor usa o valor vazio do tipo (`0`, `""`, `false`). |
| Nomes de campos | Exatamente como na documentação, com maiúsculas e minúsculas (`Search`, `Keyword`, `MaxResult`). |

Alguns métodos antigos também aceitam formulário (`application/x-www-form-urlencoded`) ou query string. O catálogo de APIs indica o formato de cada um. Para métodos novos na sua integração, prefira JSON.

## 2. Erros: HTTP 200 com o campo Erro

**A API responde HTTP 200 mesmo quando a operação falha.** Confira cada resposta nesta ordem:

1. **Status diferente de 200 ou corpo que não é JSON:** serviço indisponível. Repita depois, com intervalo crescente.
2. **`Erro` preenchido:** a operação foi recusada. Mostre o texto ao usuário. Não use os demais dados, mesmo que venham preenchidos.
3. **`Erro` vazio:** sucesso.

```js
const resp = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body });
const texto = await resp.text();
let dados;
try { dados = JSON.parse(texto); } catch { throw new Error("Serviço indisponível"); }
const bloco = Array.isArray(dados) ? dados[0] : dados;   // ObterLista devolve uma lista
if (bloco && bloco.Erro) throw new Error(bloco.Erro);
```

O texto de `Erro` pode mudar. Use-o para exibir e registrar, **não o compare com textos fixos no código**. Exceções documentadas: "Limite de requisições" (item 4) e o aviso de medidas na criação de item de produção ([Criar orçamento](guias/criar-orcamento.md)).

Alguns métodos antigos devolvem a mensagem de erro em outro campo. Por exemplo, `VisualizarAgendas` usa o campo `NomeCliente` do primeiro item. O Swagger indica esses casos.

## 3. Datas

Datas nas respostas vêm como texto no formato:

```
"/Date(1731283200000)/"
```

O número é a quantidade de milissegundos desde 01/01/1970 (UTC). Conversão em JavaScript:

```js
function lerData(v) {
  const m = /^\/Date\((-?\d+)([+-]\d{4})?\)\/$/.exec(v ?? "");
  return m ? new Date(Number(m[1])) : null;
}
```

O kit em Node.js tem `lerData` e `converterDatas` (que converte todas as datas de um objeto).

**Para enviar datas**, siga o formato indicado em cada método. Nos métodos de agenda, envie data e hora locais em ISO **sem fuso horário**: `"2026-11-20T14:30:00"`. Com fuso (`Z` ou `-03:00`), o horário pode ser deslocado.

## 4. Limite de requisições

- **50 requisições por minuto** por usuário (`userHash`).
- Acima disso, a resposta vem com `Erro` igual a `Limite de requisições excedido. Tente novamente em instantes.`
- Espere alguns segundos e repita. Distribua as chamadas: um orçamento com muitos itens gera muitas requisições.
- O kit em Node.js limita o ritmo a 45 chamadas por minuto e repete automaticamente quando recebe esse erro.

## 5. Enums e códigos

- Enums (`Acao`, `Status`, `TipoItem`, `Reserva`…) são enviados e recebidos **como número**.
- Códigos de cliente, produto, material, tabela de preço e plano de pagamento são **números inteiros** que vêm das listas de apoio (`ObterLista`) ou das consultas. Não os fixe no código: variam de uma empresa para outra.
- `Hash` (do documento de venda) e `userHash` (do usuário) são valores diferentes. Não os confunda.

## 6. Números e unidades

- Números decimais em JSON usam **ponto** (`1.20`). Vírgula só dentro de textos interpretados pelo servidor, como a medida `"Tampo 1,20 X 0,60"` dos orçamentos.
- Medidas de peças e de chapas são em **metros**. `QtdM2` e `QtdTotalM2` estão em m², `QtdTotalML` em metros lineares.
- Acabamentos de borda (boleado, meia-esquadria…) são cobrados em **metros lineares (ML)**. Revenda e serviços, em unidades.

## 7. Textos de busca

Alguns métodos antigos montam a busca no banco a partir do texto recebido. **Remova aspas simples e duplas, `%` e `;`** dos textos de busca (`Keyword`, `Search`) antes de enviar. O kit faz isso com `limparBusca()`.

## 8. Segurança

- O `userHash` dá acesso ao sistema com as permissões do usuário. Guarde-o em variável de ambiente, nunca no código, no repositório, no navegador ou em logs.
- Chame a API do seu servidor, não de código que roda no navegador.
- Use um **usuário exclusivo para a integração**, com apenas as permissões necessárias.
- Teste gravações (orçamentos, agendas, cadastros) em um usuário e dados de teste, não em dados reais de clientes.
- Trate textos vindos da API (observações, nomes) como dados. Escape-os antes de exibir em HTML.
