# PES2B Sign Service — Documentação da API

**Versão:** 1.0 MVP  
**Data:** 11/07/2026  
**Base URL de produção:** `https://sign.pes2b.com`

## 1. Visão geral

O PES2B Sign Service é uma API REST para assinatura digital de documentos PDF com certificado ICP-Brasil A1. O serviço recebe um PDF em Base64 e devolve o PDF assinado também em Base64.

Recursos disponíveis na versão atual:

- Assinatura PAdES-B ou PAdES-T, conforme configuração do servidor.
- Cadeia ICP-Brasil incorporada ao documento.
- Carimbo do tempo RFC 3161 quando habilitado.
- Selo visual em todas as páginas.
- Página final premium com QR Code de validação.
- Extração automática do nome, CPF/CNPJ e autoridade certificadora a partir do certificado utilizado.
- Identificador visual único por documento.
- Autenticação por API Key.
- Request ID para rastreamento de erros e logs.

## 2. Autenticação

Todas as requisições protegidas devem enviar o header:

```http
X-API-Key: SUA_CHAVE_DE_API
```

Não use `Bearer`, `Authorization` ou aspas ao redor da chave.

## 3. Endpoint de assinatura

### POST `/api/v1/sign`

Assina um documento PDF recebido em Base64.

### Headers

| Header | Obrigatório | Valor |
|---|---:|---|
| `Content-Type` | Sim | `application/json` |
| `X-API-Key` | Sim | Chave fornecida pelo administrador |

### Body

```json
{
  "pdfBase64": "JVBERi0xLjQK..."
}
```

O campo `pdfBase64` aceita:

- Base64 puro, normalmente iniciado por `JVBER`;
- Data URI, por exemplo `data:application/pdf;base64,JVBER...`.

### Resposta de sucesso

```json
{
  "success": true,
  "data": {
    "signedPdfBase64": "JVBERi0xLjQK..."
  }
}
```

O cliente deve decodificar `data.signedPdfBase64` e salvar o resultado com extensão `.pdf`.

### Exemplo cURL

```bash
curl -X POST "https://sign.pes2b.com/api/v1/sign" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: SUA_CHAVE_DE_API" \
  -d '{"pdfBase64":"JVBERi0xLjQK..."}'
```

### Exemplo PowerShell

```powershell
$bytes = [System.IO.File]::ReadAllBytes("C:\documentos\arquivo.pdf")
$base64 = [Convert]::ToBase64String($bytes)

$headers = @{
    "X-API-Key" = "SUA_CHAVE_DE_API"
}

$body = @{
    pdfBase64 = $base64
} | ConvertTo-Json

$response = Invoke-RestMethod `
    -Method Post `
    -Uri "https://sign.pes2b.com/api/v1/sign" `
    -Headers $headers `
    -ContentType "application/json" `
    -Body $body

$signedBytes = [Convert]::FromBase64String($response.data.signedPdfBase64)
[System.IO.File]::WriteAllBytes("C:\documentos\arquivo-assinado.pdf", $signedBytes)
```

### Exemplo Python

```python
import base64
import requests

with open("arquivo.pdf", "rb") as file:
    pdf_base64 = base64.b64encode(file.read()).decode("utf-8")

response = requests.post(
    "https://sign.pes2b.com/api/v1/sign",
    headers={
        "Content-Type": "application/json",
        "X-API-Key": "SUA_CHAVE_DE_API",
    },
    json={"pdfBase64": pdf_base64},
    timeout=120,
)
response.raise_for_status()

signed_base64 = response.json()["data"]["signedPdfBase64"]

with open("arquivo-assinado.pdf", "wb") as file:
    file.write(base64.b64decode(signed_base64))
```

### Exemplo JavaScript / Node.js

```javascript
import fs from 'node:fs/promises';

const pdf = await fs.readFile('./arquivo.pdf');

const response = await fetch('https://sign.pes2b.com/api/v1/sign', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': 'SUA_CHAVE_DE_API',
  },
  body: JSON.stringify({
    pdfBase64: pdf.toString('base64'),
  }),
});

if (!response.ok) {
  throw new Error(await response.text());
}

const result = await response.json();
await fs.writeFile(
  './arquivo-assinado.pdf',
  Buffer.from(result.data.signedPdfBase64, 'base64'),
);
```

## 4. Integração com n8n

### Code node — converter PDF binário para Base64

```javascript
const item = $input.first();

if (!item.binary?.pdf) {
  throw new Error('PDF não encontrado no campo binário "pdf".');
}

const buffer = await this.helpers.getBinaryDataBuffer(0, 'pdf');

return [{
  json: {
    pdfBase64: buffer.toString('base64'),
    fileName: item.binary.pdf.fileName,
    mimeType: item.binary.pdf.mimeType,
  },
}];
```

### HTTP Request node

- Method: `POST`
- URL: `https://sign.pes2b.com/api/v1/sign`
- Authentication: `None`
- Header: `X-API-Key = SUA_CHAVE_DE_API`
- Body Content Type: `JSON`
- Body:

```javascript
={{ {
  pdfBase64: $json.pdfBase64
} }}
```

### Code node — reconstruir o PDF assinado

```javascript
const signedPdfBase64 = $json.data?.signedPdfBase64;

if (!signedPdfBase64) {
  throw new Error('PDF assinado não encontrado na resposta da API.');
}

return [{
  json: {
    fileName: 'documento-assinado.pdf',
  },
  binary: {
    data: {
      data: signedPdfBase64,
      mimeType: 'application/pdf',
      fileName: 'documento-assinado.pdf',
      fileExtension: 'pdf',
    },
  },
}];
```

## 5. Aparência visual

A versão atual suporta quatro modos de aparência, configurados no servidor:

| Modo | Resultado |
|---|---|
| `NONE` | Apenas assinatura criptográfica, sem elementos visuais |
| `STAMP_ONLY` | Selo visual em todas as páginas |
| `PAGE_ONLY` | Página final premium com QR Code |
| `STAMP_AND_PAGE` | Selo em todas as páginas e página final premium |

O modo atual de produção é normalmente `STAMP_AND_PAGE`.

O selo contém:

- nome do assinante;
- CPF ou CNPJ;
- data e hora do processamento;
- padrão PAdES-B ou PAdES-T;
- identificador do documento.

A página final contém:

- nome do assinante;
- CPF/CNPJ;
- autoridade certificadora;
- data e hora;
- padrão da assinatura;
- identificador;
- QR Code para o Validador do ITI.

**Importante:** na versão atual, o modo de aparência é uma configuração do servidor. Ele ainda não é enviado individualmente no body da requisição. A seleção por cliente ou documento será implementada posteriormente por integração com SharePoint/n8n.

## 6. Níveis de assinatura

| Configuração do servidor | Resultado |
|---|---|
| Timestamp desabilitado | PAdES-B |
| Timestamp habilitado | PAdES-T com carimbo RFC 3161 |

O documento enviado à TSA não é transmitido integralmente; o serviço envia somente o resumo criptográfico necessário ao protocolo de carimbo do tempo.

## 7. Endpoints operacionais

### GET `/api/v1/health`

Endpoint público de disponibilidade.

Exemplo de resposta:

```json
{
  "success": true,
  "message": "Serviço disponível.",
  "data": {
    "service": "pes2b-sign-service",
    "status": "UP",
    "environment": "development",
    "javaVersion": "21.0.11"
  },
  "warnings": [],
  "executionTimeMs": 2,
  "timestamp": "2026-07-11T14:28:55.671972222-03:00",
  "version": "1.0.0"
}
```

### GET `/actuator/health`

Health check técnico do Spring Boot Actuator. Pode exigir API Key conforme a política de segurança do ambiente.

### GET `/actuator/info`

Informações da aplicação e versão. Atualmente pode exigir API Key.

## 8. Respostas de erro

Formato padronizado geral:

```json
{
  "success": false,
  "code": "INVALID_PDF",
  "message": "O conteúdo informado não corresponde a um PDF válido.",
  "requestId": "e4bcb98e-f87f-45bc-b8d7-55f9ebf84f15",
  "timestamp": "2026-07-11T18:30:00-03:00",
  "path": "/api/v1/sign"
}
```

Códigos previstos:

| Código | Significado |
|---|---|
| `INVALID_REQUEST` | Requisição inválida |
| `VALIDATION_ERROR` | Falha de validação de campos |
| `INVALID_JSON` | JSON malformado |
| `INVALID_CERTIFICATE` | Certificado inválido ou indisponível |
| `INVALID_PDF` | Conteúdo não corresponde a um PDF válido |
| `FILE_TOO_LARGE` | Arquivo acima do limite configurado |
| `MULTIPART_ERROR` | Erro ao processar upload multipart |
| `UNSUPPORTED_MEDIA_TYPE` | Content-Type não suportado |
| `RESOURCE_NOT_FOUND` | Recurso não encontrado |
| `TIMESTAMP_ERROR` | Falha ao obter ou incorporar carimbo do tempo |
| `INTERNAL_ERROR` | Erro interno não classificado |

A falha de autenticação pode retornar:

```json
{
  "success": false,
  "message": "API Key inválida."
}
```

Guarde o `requestId` e informe-o ao suporte para rastreamento nos logs.

## 9. Status HTTP

| Status | Uso esperado |
|---:|---|
| `200` | Assinatura concluída |
| `400` | Requisição, JSON, PDF ou parâmetros inválidos |
| `401` ou `403` | API Key ausente ou inválida, conforme configuração |
| `413` | Arquivo maior que o limite, quando habilitado no servidor |
| `415` | Tipo de conteúdo não suportado |
| `500` | Falha interna, certificado, TSA ou processamento do PDF |

## 10. Boas práticas de consumo

- Nunca registre a API Key em logs.
- Nunca envie a chave no corpo ou na URL.
- Valide se o Base64 começa normalmente por `JVBER`.
- Use timeout de pelo menos 120 segundos em documentos grandes.
- Não tente alterar o PDF depois da assinatura; qualquer modificação pode invalidá-la.
- Preserve o arquivo final exatamente como retornado.
- Em falhas, registre `status`, `code`, `message` e `requestId`.
- Não envie senha de certificado no request; o certificado é administrado pelo serviço.

## 11. Limitações atuais

- Um certificado ativo por instância/configuração do serviço.
- A escolha do modo visual ainda é global no servidor.
- Não há assinatura múltipla por vários certificados no mesmo request.
- Não há endpoint público próprio de validação por ID.
- O QR Code atual direciona ao Validador do ITI, não a um registro individual do documento.
- Limites máximos de tamanho e taxa devem ser confirmados com o administrador do ambiente.

## 12. Roadmap previsto

- Seleção de certificado por alias.
- Assinaturas múltiplas incrementais.
- Aparência escolhida por requisição.
- Preferências por cliente/documento via SharePoint.
- Endpoint de validação próprio por identificador.
- Histórico e auditoria de consumo.
- Multiempresa e API Keys por cliente.

## 13. Checklist rápido de integração

1. Obter a API Key com o administrador.
2. Confirmar `GET /api/v1/health`.
3. Ler o PDF em binário e converter para Base64.
4. Enviar `POST /api/v1/sign` com `X-API-Key`.
5. Ler `data.signedPdfBase64`.
6. Decodificar e salvar como PDF.
7. Validar o primeiro arquivo no Validador do ITI.
8. Em produção, armazenar o `requestId` em caso de erro.

---

**Contato técnico:** equipe de TI P&S2B  
**Serviço:** PES2B Sign Service  
**Ambiente de produção:** `https://sign.pes2b.com`
