Aceitou — API Reference
API REST para integração com a plataforma de assinatura eletrônica Aceitou.
Base URL produção: https://api.aceitou.com.br
Base URL demo: https://api-demo.aceitou.com.br
Sumário
Autenticação
JWT (Bearer token)
Para usuários humanos (portal). Obtenha o token via POST /auth/login:
curl -X POST https://api.aceitou.com.br/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"voce@empresa.com","password":"sua-senha"}'
Resposta:
{
"accessToken": "eyJhbGc...",
"refreshToken": "eyJhbGc..."
}
Use em requisições subsequentes:
curl https://api.aceitou.com.br/api/v1/documents \
-H "Authorization: Bearer eyJhbGc..."
O accessToken expira em 5 minutos. Renove com POST /auth/refresh:
curl -X POST https://api.aceitou.com.br/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken":"eyJhbGc..."}'
API Key
Para integrações server-to-server. Gere uma chave em Configurações → Chaves de API no portal, ou via:
curl -X POST https://api.aceitou.com.br/api/v1/api-keys \
-H "Authorization: Bearer <jwt>" \
-H "Content-Type: application/json" \
-d '{"name":"Integração Backoffice","scopes":["documents:read","documents:write"]}'
A chave (ak_live_...) é exibida uma única vez na resposta. Guarde com cuidado.
Use em requisições com o header X-Api-Key:
curl https://api.aceitou.com.br/api/v1/documents \
-H "X-Api-Key: ak_live_xxxxxxxxxxx"
Escopos
Cada API key tem escopos que limitam o que ela pode fazer. Tente sempre dar o mínimo necessário.
| Escopo | Permite |
|---|---|
documents:read |
Listar e ler documentos |
documents:write |
Criar, enviar, cancelar documentos |
templates:read |
Listar e ler templates |
templates:write |
Criar, atualizar, excluir templates |
webhooks:read |
Listar webhooks |
webhooks:write |
Criar, atualizar, excluir, rotacionar segredo |
api_keys:read |
Listar chaves de API |
api_keys:write |
Gerar e revogar chaves |
* |
Todos (use com cautela) |
Chaves sem o escopo apropriado recebem 403 Forbidden com:
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.4",
"title": "Forbidden",
"status": 403,
"detail": "A chave de API não possui o escopo 'documents:write' necessário para este recurso.",
"requiredScope": "documents:write"
}
Documentos
Em todos os payloads, documentId é o identificador interno do envelope (não confunda com o arquivo PDF anexado).
Criar documento
curl -X POST https://api.aceitou.com.br/api/v1/documents \
-H "X-Api-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{"title":"Contrato de prestação de serviços"}'
Resposta 201:
{ "id": "12345", "status": "Draft" }
Anexar PDF original
curl -X POST https://api.aceitou.com.br/api/v1/documents/12345/documents \
-H "X-Api-Key: $KEY" \
-F "file=@contrato.pdf;type=application/pdf"
Adicionar signatário
curl -X POST https://api.aceitou.com.br/api/v1/documents/12345/signers \
-H "X-Api-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Maria Silva",
"email": "maria@cliente.com",
"verificationMethod": "simple",
"signingOrder": 1
}'
verificationMethod aceita: simple (clique apenas), email_token (OTP por e-mail), sms_token, email_and_sms.
signingOrder define a ordem sequencial: signatários com mesmo número assinam em paralelo; números maiores só recebem o convite após todos os menores assinarem.
Enviar para assinatura
curl -X POST https://api.aceitou.com.br/api/v1/documents/12345/send \
-H "X-Api-Key: $KEY"
Dispara os e-mails de convite e o webhook document_sent.
Baixar versões
# PDF original
curl -O -J https://api.aceitou.com.br/api/v1/documents/12345/download?version=original \
-H "X-Api-Key: $KEY"
# PDF assinado (gerado automaticamente quando todos assinam)
curl -O -J https://api.aceitou.com.br/api/v1/documents/12345/download?version=signed \
-H "X-Api-Key: $KEY"
Trilha de auditoria
curl https://api.aceitou.com.br/api/v1/documents/12345/audit-trail \
-H "X-Api-Key: $KEY"
Retorna eventos: document_sent, document_signed, document_rejected, document_completed, etc.
Templates
Cada tenant nasce com 4 templates pré-feitos (CLT, Locação, NDA, Prestação de Serviços). Você pode criar os seus:
curl -X POST https://api.aceitou.com.br/api/v1/templates \
-H "X-Api-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Contrato XPTO",
"htmlContent": "<h1>Contrato com {{nome}}</h1><p>CPF: {{cpf}}</p>",
"variables": ["nome","cpf"]
}'
Gerar documento a partir de template
curl -X POST https://api.aceitou.com.br/api/v1/documents/12345/documents/from-template \
-H "X-Api-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "9",
"variables": { "nome": "Maria Silva", "cpf": "111.222.333-44" }
}'
O backend gera o PDF substituindo as variáveis e o anexa ao envelope.
Webhooks
Cadastrar
curl -X POST https://api.aceitou.com.br/api/v1/webhooks \
-H "X-Api-Key: $KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://meu-app.com/webhooks/aceitou",
"events": ["document_sent","document_completed","signer_signed"]
}'
Resposta 201:
{
"id": 7,
"secret": "abc123def456..."
}
O
secreté exibido apenas uma vez. Guarde-o — você precisará dele para validar o HMAC. Para gerar um novo (rotação), usePOST /api/v1/webhooks/{id}/rotate-secret.
Verificação HMAC
Toda entrega traz dois headers:
X-Aceitou-Signature: sha256=<hex>
X-Aceitou-Event: document_sent
A signature é HMAC-SHA256(secret, body) em hex lowercase. Sempre valide antes de processar:
Node.js
import crypto from 'node:crypto';
function verify(rawBody, signatureHeader, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody, 'utf8')
.digest('hex');
// constant-time compare
const a = Buffer.from(signatureHeader);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Express
app.post('/webhooks/aceitou', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.header('X-Aceitou-Signature');
if (!sig || !verify(req.body, sig, process.env.ACEITOU_WEBHOOK_SECRET)) {
return res.status(401).send('invalid signature');
}
const event = req.header('X-Aceitou-Event');
const payload = JSON.parse(req.body.toString('utf8'));
// ... processe
res.status(200).send();
});
Python
import hmac, hashlib
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature_header, expected)
PHP
function verify($raw_body, $signature_header, $secret) {
$expected = 'sha256=' . hash_hmac('sha256', $raw_body, $secret);
return hash_equals($signature_header, $expected);
}
Eventos
| Evento | Quando dispara | Payload (campos chave) |
|---|---|---|
document_sent |
Envelope sai do status Draft para Sent |
documentId, title, status, signers[], sentAt |
signer_signed |
Cada signatário assina | documentId, title, signer{id,name,email}, signedAt |
document_completed |
Todos os signatários assinaram | documentId, title, status, completedAt |
document_rejected |
Um signatário recusa | documentId, signer, rejectionReason |
document_cancelled |
Dono cancela o envelope | documentId, cancelledAt |
document_expired |
ExpiresAt passa antes de todos assinarem |
documentId, expiredAt |
Retry e dead-letter
A entrega tenta 1, 5, 15, 60, 360 minutos antes de marcar como Failed. Você pode replay manualmente em Configurações → Webhooks → Entregas.
Códigos de erro
Todos os erros seguem o formato RFC 7807 Problem Details:
{
"type": "https://tools.ietf.org/html/rfc4918#section-11.2",
"title": "auth.invalid_credentials",
"status": 422,
"detail": "Invalid email or password.",
"traceId": "00-abc...-01"
}
Códigos comuns:
| Código | Significado |
|---|---|
auth.invalid_credentials |
Email ou senha errados |
auth.invalid_refresh_token |
Refresh expirado/inválido |
auth.tenant_required |
Token sem tenant_id |
document.not_found |
Documento não existe ou não pertence a este tenant |
document.invalid_status |
Operação inválida no status atual (ex.: cancelar um Completed) |
signing.token_expired |
Link de assinatura expirou |
signing.invalid_status |
Documento já assinado ou rejeitado |
webhook.not_found |
Webhook não existe ou não pertence ao tenant |
subscription.not_found |
Tenant sem assinatura ativa (raro — fail-safe) |
trial_expired |
Trial encerrou e não há plano ativo (HTTP 402) |
Limites de plano
Cada plano define limites máximos que o tenant pode atingir:
| Limite | Onde aplica |
|---|---|
MaxDocumentsPerMonth |
Criação de envelopes (POST /documents) |
MaxTemplates |
Criação de templates (POST /templates) |
MaxWebhooks |
Cadastro de webhooks |
MaxUsers |
Convite de membros adicionais |
Ao atingir, a API retorna 400 Bad Request com title: "plan.limit_reached" (erro
de regra de negócio — não confundir com o 402 Payment Required do trial/assinatura
expirada, acima). Durante o trial, o limite (Billing:TrialDocumentLimit) é
sinalizado com title: "plan.trial_limit_reached", também 400.