> ## Documentation Index
> Fetch the complete documentation index at: https://docs.engineapi.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK TypeScript

> SDK oficial TypeScript para engineAPI. @engineapi/sdk no npm. Tipagem completa, todos os módulos fiscais.

SDK oficial da engineAPI com tipagem completa para Node.js e TypeScript.

```bash theme={null}
npm install @engineapi/sdk
```

<Info>
  Publicado no npm como [`@engineapi/sdk@1.3.0 {/* fact:sdk.version */}`](https://www.npmjs.com/package/@engineapi/sdk). Compatível com Node.js 18+ e TypeScript 5+.
</Info>

***

## Configuração

```typescript theme={null}
import { EngineApiClient } from '@engineapi/sdk';

const client = new EngineApiClient({
  baseUrl: 'https://api.engineapi.com.br',
  apiKey: 'ek_live_SEU_API_KEY',
  timeout: 30000, // opcional, padrão 30s
});
```

### Parâmetros de configuração

| Parâmetro | Tipo   | Obrigatório | Descrição                                                                    |
| --------- | ------ | ----------- | ---------------------------------------------------------------------------- |
| `baseUrl` | string | Sim         | URL base da API (sem `/v1`, o cliente HTTP interno adiciona automaticamente) |
| `apiKey`  | string | Sim\*       | API Key para integração server-to-server (`ek_live_`/`ek_test_`)             |
| `token`   | string | Sim\*       | JWT para sessões de dashboard                                                |
| `timeout` | number | Não         | Timeout em ms (padrão: 30000)                                                |

\*Use `apiKey` para integrações ou `token` para sessões autenticadas via login.

<Info>
  O client anexa **todas** as credenciais configuradas em cada chamada: `apiKey` vira o
  header `X-API-Key`, `token` vira `Authorization: Bearer`, sem distinção por módulo do
  lado do client. O que decide qual funciona é o *guard* de cada endpoint no backend (ver
  [Companies](#companies-empresas-emissoras) abaixo).
</Info>

***

## Módulos Disponíveis

O contrato público hoje cobre **2 módulos** tipados:

```typescript theme={null}
client.nfe        // NFe: emissão, cancelamento, CC-e, download, status SEFAZ
client.companies  // Empresas: cadastro e certificados
```

<Info>
  DFe (Distribuição de documentos recebidos) é fluxo interno do dashboard, fora do
  contrato público da API. `client.dfe` não é suportado e não deve ser usado.
</Info>

***

## NFe: Nota Fiscal Eletrônica

### Emitir NFe

O tipo `CreateNfeParams` publicado nesta versão do pacote já bate com o contrato real da
API: `issuerId` na raiz, `items` (não `itens`), `pagamentos[]` (não `pagamento` singular),
`destinatario.nome`/`destinatario.ie` (não `razaoSocial`/`inscricaoEstadual`). Não precisa
de `as any` para emitir.

```typescript theme={null}
import { EngineApiClient } from '@engineapi/sdk';

const client = new EngineApiClient({
  baseUrl: 'https://api.engineapi.com.br',
  apiKey: 'ek_live_SEU_API_KEY',
});

const nfe = await client.nfe.emitir({
  destinatario: {
    cnpjCpf: '99888777000100',
    nome: 'Cliente Exemplo SA',
    endereco: {
      logradouro: 'Av Brasil', numero: '500',
      bairro: 'Centro', municipio: 'São Paulo',
      uf: 'SP', cep: '01001000',
      codigoMunicipio: '3550308',
    },
  },
  items: [{
    codigo: 'PROD001',
    descricao: 'Produto Teste',
    ncm: '61091000',
    cfop: '5102',
    unidade: 'UN',
    quantidade: 1,
    valorUnitario: 100.00,
    icms: { origem: 0, csosn: '400' },
  }],
  pagamentos: [{ forma: '01', valor: 100.00 }],
});

console.log('Chave:', nfe.data.accessKey);
console.log('Status:', nfe.data.status);  // "AUTHORIZED"
console.log('Protocolo:', nfe.data.protocol);
console.log('Downloads:', nfe.data.downloads); // { xml, pdf }, rotas reais de download
```

<Info>
  O tipo `NfeResponse` bate 1:1 com o JSON real: `protocol`/`number`/`series`/`model`/
  `amount`/`destCNPJ`/`destName`/`updatedAt`/`downloads.{xml,pdf}` (e `qrCode` na NFCe),
  todos acessíveis sem `as any`. `amount` é `string` (ex. `"119.8"`), não `number`,
  para não perder precisão decimal. Shape real completo em
  [Primeira Emissão](/guides/first-emission).
</Info>

<Info>
  `issuerId` é **opcional** no payload de NFe/NFCe: com um único emissor pode omitir; com
  dois ou mais, informe o `issuerId` (UUID) do CNPJ que deve emitir; sem ele a API responde
  `400`.
</Info>

### Listar NFes

```typescript theme={null}
const lista = await client.nfe.listar({
  page: 1,
  limit: 20,
  issuerId: 'ISSUER_UUID',
  status: 'AUTHORIZED',
});
```

### Cancelar NFe

```typescript theme={null}
await client.nfe.cancelar('35260211222333000181550010000000011000000019', {
  justificativa: 'Erro nos dados do destinatário informado na venda',
});
```

### Carta de Correção

```typescript theme={null}
await client.nfe.cartaCorrecao('35260211222333000181550010000000011000000019', {
  correcao: 'Corrijo o endereço do destinatário para Av Brasil, 500',
});
```

### Download PDF e XML

```typescript theme={null}
// PDF (DANFE)
const pdf = await client.nfe.downloadPdf('35260211...');
fs.writeFileSync('danfe.pdf', pdf);

// XML
const xml = await client.nfe.downloadXml('35260211...');
fs.writeFileSync('nfe.xml', xml);

// XML/PDF da CC-e
const cceXml = await client.nfe.downloadCceXml('35260211...');
const ccePdf = await client.nfe.downloadCcePdf('35260211...');
```

### Status do serviço SEFAZ

```typescript theme={null}
const status = await client.nfe.status();
```

***

## Companies: Empresas Emissoras

`client.companies.*` chama `/companies`, o path bate com a API real. O backend aceita
**`apiKey` ou `token`** neste módulo, o mesmo aceito em NFe: configurar só `apiKey` no
client já é suficiente para cadastrar empresa e enviar certificado. O upload de
certificado (`uploadCertificado()`) já envia multipart/form-data com o campo `file`,
batendo com o contrato real do endpoint de upload.

```typescript theme={null}
const company = await client.companies.criar({
  cnpj: '11222333000181',
  name: 'Empresa Exemplo Ltda',
  crt: 1,
});

await client.companies.uploadCertificado(
  company.data.id,
  fs.readFileSync('certificado.pfx'),
  'senhaDoCertificado',
);
```

***

## Tratamento de Erros

```typescript theme={null}
import { EngineApiClient, EngineApiError } from '@engineapi/sdk';

try {
  await client.nfe.emitir(params);
} catch (error) {
  if (error instanceof EngineApiError) {
    console.error('Status:', error.statusCode);
    console.error('Mensagem:', error.message);
    console.error('Response:', error.response); // envelope RFC 7807 completo

    if (error.statusCode === 400) {
      // Validação OU rejeição SEFAZ: ver error.response?.error?.erros
    }
    if (error.statusCode === 401) {
      // API key/token inválido ou expirado
    }
    if (error.statusCode === 429) {
      // Rate limit: retry após Retry-After
    }
    if (error.statusCode === 404) {
      // Recurso não encontrado
    }
    if (error.statusCode >= 500) {
      // Retry com backoff
    }
  }
}
```

<Info>
  **Use a versão 1.3.0  ou superior** (`npm install @engineapi/sdk@latest`).
  Em versões anteriores à 1.2.0, `error.message` pode chegar como `"[object Object]"`;
  nesse caso, leia a mensagem de `error.response.error.detail` (o envelope de erro da API
  é RFC 7807, ver [Erros e respostas](/guides/errors)) ou atualize o pacote.
</Info>

***

## Login via JWT (Dashboard)

Para sessões de dashboard (não recomendado para server-to-server):

```typescript theme={null}
const client = new EngineApiClient({
  baseUrl: 'https://api.engineapi.com.br',
});

const login = await client.login({
  email: 'usuario@partner.com',
  password: 'senha',
});

console.log('Token:', login.access_token);
console.log('Usuário:', login.user.email, login.user.role);

// Agora o client usa o JWT automaticamente (setToken() já foi chamado
// internamente por login())
const empresas = await client.companies.listar();
```

<Info>
  **`client.login()` requer a versão 1.2.0 ou superior** (`npm install @engineapi/sdk@latest`).
  Em versões anteriores o token não era extraído do envelope de resposta e a sessão não
  era autenticada. Se o upgrade não for possível agora, use uma API Key no lugar do login.
</Info>

***

## Integração com Frameworks

### Next.js (App Router)

```typescript theme={null}
// app/api/emitir-nfe/route.ts
import { EngineApiClient } from '@engineapi/sdk';
import { NextResponse } from 'next/server';

const engine = new EngineApiClient({
  baseUrl: process.env.ENGINE_API_URL!,
  apiKey: process.env.ENGINE_API_KEY!,
});

export async function POST(request: Request) {
  const body = await request.json(); // já no formato real: destinatario/items/pagamentos
  const nfe = await engine.nfe.emitir(body);
  return NextResponse.json(nfe);
}
```

### Express

```typescript theme={null}
import express from 'express';
import { EngineApiClient, EngineApiError } from '@engineapi/sdk';

const app = express();
const engine = new EngineApiClient({
  baseUrl: process.env.ENGINE_API_URL!,
  apiKey: process.env.ENGINE_API_KEY!,
});

app.post('/nfe', async (req, res) => {
  try {
    const nfe = await engine.nfe.emitir(req.body);
    res.json(nfe);
  } catch (error) {
    if (error instanceof EngineApiError) {
      res.status(error.statusCode).json({ error: error.message });
    } else {
      res.status(500).json({ error: 'Internal server error' });
    }
  }
});
```

***

## Próximos passos

<CardGroup cols={2}>
  <Card title="AI Integration" icon="robot" href="/guides/ai-integration">
    Como usar IA para integrar automaticamente
  </Card>

  <Card title="Webhooks" icon="bell" href="/guides/webhooks">
    Receber eventos em tempo real
  </Card>
</CardGroup>
