Pular para o conteúdo
AssinAPI

Integração · Claude Code

Integre assinatura eletrônica com o Claude Code

Cole o prompt no Claude Code dentro do seu repositório. Ele cria a camada assinapi.service, o endpoint de webhook e os testes — lendo a chave do ambiente.

curl -X POST /v1/signature-requests

201 Created · 312 ms

"status": "SENT", "signingUrl": "…/s/9xq4m"

webhook signer.signed → 200 OK

Antes de tudo: a chave fica no servidor

Onde fica a chave: Adicione ASSINAPI_SECRET_KEY ao .env do servidor (e ao .env.example sem valor). O prompt nunca contém sua chave.

Como funciona

  1. 1

    Você gera a chave de Sandbox no dashboard e coloca no .env.

  2. 2

    O Claude Code analisa a stack e cria assinapi.service (todas as chamadas server-side).

  3. 3

    Seu backend envia com POST /v1/signature-requests e guarda o id sigreq_….

  4. 4

    O webhook valida o HMAC, deduplica por X-Event-Id e atualiza o status no banco.

  5. 5

    Na conclusão, o backend baixa o PDF assinado com a página de auditoria.

Prompt e código prontos

Prompt para o Claude Code

markdown
Implemente a integração com a AssinAPI neste repositório. Analise a stack existente e siga as convenções do projeto.

## Objetivo
Implementar: criação de envelope; upload de PDF; cadastro de signatário (nome, e-mail, CPF); envio para assinatura e armazenamento dos signingLinks; endpoint de webhook para envelope.completed (com validação HMAC); download do documento assinado e do certificado de evidências.

## Configuração
- Base URL: https://api.assinapi.com.br/v1
- Autenticação: header `Authorization: Bearer ${ASSINAPI_SECRET_KEY}`
- Leia a chave SOMENTE da variável de ambiente ASSINAPI_SECRET_KEY no servidor.
- Leia o segredo do webhook de ASSINAPI_WEBHOOK_SECRET.
- Chaves ask_test_… = Sandbox (OTP de teste 123456, CPF de teste 529.982.247-25). Chaves ask_live_… = Produção.

## Regras de segurança (obrigatórias)
- NUNCA exponha ASSINAPI_SECRET_KEY no frontend, no navegador, em app mobile, em logs ou no repositório.
- Crie uma camada server-side chamada `assinapi.service` que concentra todas as chamadas à AssinAPI.
- O frontend conversa apenas com o seu backend.
- Não registre CPF, telefone, códigos OTP ou links de assinatura em logs.

## Endpoints
- POST https://api.assinapi.com.br/v1/envelopes
  body: { "title": string, "message"?: string, "signatureLevel"?: "SIMPLE"|"ADVANCED", "expirationDays"?: number, "externalId"?: string }
  201 → { "id": "env_…", "status": "DRAFT", … }
- POST https://api.assinapi.com.br/v1/envelopes/{envelopeId}/documents
  multipart/form-data com o campo "file" (PDF)  — ou JSON { "contentBase64": string, "filename": string }
  201 → envelope com documents[].sha256
- POST https://api.assinapi.com.br/v1/envelopes/{envelopeId}/signers
  body: { "name": string, "email": string, "cpf": string, "birthDate"?: "AAAA-MM-DD", "phone"?: "+5511999999999" (envie sempre que tiver: usado no WhatsApp, planos pagos), "authenticationMethod"?: "EMAIL_LINK"|"EMAIL_OTP"|"WHATSAPP_LINK"|"WHATSAPP_OTP" (WHATSAPP_* só nos planos pagos; no Free → 402 WHATSAPP_REQUIRES_PAID_PLAN. Omitido = padrão do projeto: simples → só link por e-mail; avançada → código pelo WhatsApp nos planos pagos, por e-mail no Free), "signingOrder"?: number }
  201 → { "id": "sgn_…", "status": "PENDING", "cpfMasked": "***.456.789-**" }
- POST https://api.assinapi.com.br/v1/envelopes/{envelopeId}/send
  body: { "notify"?: boolean }   (notify=false → você envia o link por outro canal)
  200 → { "status": "SENT", "signingLinks": [{ "signerId", "name", "signingUrl", "expiresAt" }] }
  Os signingLinks só aparecem NESTA resposta: salve-os se precisar.
- GET https://api.assinapi.com.br/v1/envelopes/{envelopeId}
  200 → { "status": "DRAFT"|"SENT"|"IN_PROGRESS"|"COMPLETED"|"DECLINED"|"EXPIRED"|"CANCELLED", "signers": [{ "status" }] }
- GET https://api.assinapi.com.br/v1/envelopes/{envelopeId}/download   (somente após COMPLETED)
  200 → arquivo PDF assinado, com relatório de assinaturas e auditoria (IP, localização, dispositivo, trilha). ?type=certificate → certificado de evidências.
  Baixe e armazene no seu storage.

Atalho recomendado (uma chamada só): POST https://api.assinapi.com.br/v1/signature-requests
  body: { "title": string, "document": { "filename": string, "contentBase64": string }, "externalId"?: string, "signers": [{ "name", "email", "cpf", "birthDate"?, "fields"?: [{ "anchor": "{{assinatura_cliente}}" } | { "page": "last", "x": 60, "y": 80 }], "requireDocumentPhoto"?: boolean, "requireSelfie"?: boolean }], "redirectUrl"?: string, "reminderIntervalDays"?: number }
  201 → { "id": "sigreq_…", "envelopeId": "env_…", "status": "SENT", "signingUrl": string, "signers": [{ "id", "signingUrl" }] }  — salve o id (sigreq_…).
  GET https://api.assinapi.com.br/v1/signature-requests/{id} → { "status", "completed": boolean, "nextStep": string, "downloads": { "signedPdfUrl" } | null }
  GET https://api.assinapi.com.br/v1/signature-requests/{id}/download → PDF assinado. POST …/{id}/remind (lembrete) e POST …/{id}/cancel.

## Idempotência e retries
- Envie `Idempotency-Key: <uuid>` em todo POST. Em retry (timeout, 429, 5xx) reenvie com a MESMA chave.
- Em 429 respeite o header Retry-After e use backoff exponencial.

## Tratamento de erros
Erros têm o formato { "error": { "code", "message", "hint", "docs", "requestId" } }. Trate pelo `code` (estável), exiba `message` ao usuário quando fizer sentido e registre o `requestId` para suporte.
Códigos comuns: VALIDATION_FAILED, CPF_REQUIRED, INVALID_CPF, ENVELOPE_INCOMPLETE, INVALID_STATE_TRANSITION, INVALID_API_KEY, RATE_LIMITED.

## Webhook
- Registre o endpoint: POST https://api.assinapi.com.br/v1/webhooks { "url": "https://…/api/webhooks/assinapi", "events": ["envelope.completed", "signer.signed"] } → guarde o "secret" retornado (exibido uma vez).
- Headers recebidos: X-Signature (v1=<hex>), X-Event-Id, X-Timestamp.
- Valide: HMAC_SHA256(ASSINAPI_WEBHOOK_SECRET, `${X-Event-Id}.${X-Timestamp}.${corpo_bruto}`) === X-Signature (comparação em tempo constante).
- Rejeite X-Timestamp com mais de 5 minutos; deduplique por X-Event-Id; responda 2xx rápido.
- Todo evento: data.envelope = { "id" (env_…), "signatureRequestId" (sigreq_…), "status", "externalId" }.
- Ao receber envelope.completed (o PDF já está pronto), marque o registro como FINALIZADO e baixe o PDF em GET /signature-requests/{signatureRequestId}/download.
- signer.signed = assinatura parcial; signer.declined / envelope.expired / envelope.cancelled = encerrado sem assinatura; signer.identity_submitted = fotos aguardando aprovação.

## Entregáveis
- `assinapi.service` com funções tipadas para cada operação.
- Persistir o id (sigreq_…) e o envelopeId junto ao registro do seu sistema, e enviar o seu id em externalId.
- Variáveis de ambiente documentadas (.env.example sem valores reais).
- Testes da camada de serviço com a API mockada.

O que o código gerado usa (SDK Node)

typescript
import { AssinAPI } from '@assinapi/sdk';

const assinapi = new AssinAPI({ apiKey: process.env.ASSINAPI_SECRET_KEY });

const request = await assinapi.signatureRequests.create({
  title: 'Contrato de prestação de serviços',
  file: pdfBuffer,
  filename: 'contrato.pdf',
  signer: { name: 'João da Silva', email: 'joao@email.com', cpf: '529.982.247-25', phone: '+5511999999999' },
  // authentication omitido = padrão do projeto (avançada: código pelo WhatsApp nos planos pagos; por e-mail no Free)
});
console.log(request.signingUrl);

Exemplos apontam para o Sandbox (https://api.assinapi.com.br/v1). Em Produção, troque apenas a chave (ask_live_…); o endereço é o mesmo.

Perguntas frequentes

Perguntas técnicas.

O Claude Code consegue ler a documentação da API?

Sim. Existe uma versão compacta para agentes em /docs/ai.md e /llms.txt, além do OpenAPI completo.

Funciona com qualquer linguagem?

O prompt pede que o agente siga a stack existente (Node, Python, PHP…). Para Node/TypeScript há o SDK oficial @assinapi/sdk.

Como o signatário se autentica? Dá para usar WhatsApp?

WhatsApp está nos planos pagos. Simples: por padrão só o link por e-mail (EMAIL_LINK). Avançada (CPF e data de nascimento conferidos): nos planos pagos, link por e-mail + código pelo WhatsApp (WHATSAPP_OTP) ou só WhatsApp (WHATSAPP_LINK); no Free, link e código por e-mail (EMAIL_OTP), e pedir WhatsApp devolve 402 WHATSAPP_REQUIRES_PAID_PLAN. Envie o celular em phone (+5511999999999) e, se quiser, defina o padrão no projeto.

Consigo testar sem custo?

Sim. O Sandbox é gratuito: chaves ask_test_…, código de confirmação fixo 123456 e CPFs de teste. Documentos de Sandbox não têm validade e não consomem a franquia do plano.

Primeira assinatura no Sandbox em menos de 10 minutos.

Sem cartão de crédito. Sandbox gratuito; você só paga por documentos enviados em Produção.