Integre assinatura eletrônica em sua stack
Envie envelos, acompanhe assinaturas, receba webhooks. API REST simples, OpenAPI 3.0, exemplos curl/Node/Python — tudo para conectar a Certyneo ao seu HRIS, CRM ou software corporativo em poucas horas.
Início rápido
Três etapas: crie uma chave API nas configurações, codifique seu PDF em base64, envie. A resposta contém o `signUrl` que você pode compartilhar diretamente com o destinatário.
# 1. Upload the PDF (multipart) and capture the returned document id.
DOC_ID=$(curl -s -X POST https://certyneo.com/api/v1/documents \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-F "file=@contrat.pdf" | jq -r .id)
# 2. Create a DRAFT envelope referencing the uploaded document.
ENV_ID=$(curl -s -X POST https://certyneo.com/api/v1/envelopes \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d "{
\"subject\": \"Contrat de prestation\",
\"documentIds\": [\"$DOC_ID\"],
\"recipients\": [
{ \"email\": \"client@example.com\", \"name\": \"Marie Dubois\", \"role\": \"SIGNER\" }
]
}" | jq -r .id)
# 3. Dispatch the envelope — this sends the invitation email/SMS.
curl -X POST https://certyneo.com/api/v1/envelopes/$ENV_ID/send \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"// Plain fetch, no SDK to install.
const auth = { Authorization: `Bearer ${process.env.CERTYNEO_API_KEY}` };
// 1. Upload the PDF (multipart).
const fd = new FormData();
fd.append("file", new Blob([pdfBuffer], { type: "application/pdf" }), "contrat.pdf");
const doc = await fetch("https://certyneo.com/api/v1/documents", {
method: "POST", headers: auth, body: fd,
}).then((r) => r.json());
// 2. Create the DRAFT envelope.
const envelope = await fetch("https://certyneo.com/api/v1/envelopes", {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify({
subject: "Contrat de prestation",
documentIds: [doc.id],
recipients: [
{ email: "client@example.com", name: "Marie Dubois", role: "SIGNER" },
],
}),
}).then((r) => r.json());
// 3. Dispatch — this triggers the invitation channel for every recipient.
await fetch(`https://certyneo.com/api/v1/envelopes/${envelope.id}/send`, {
method: "POST", headers: auth,
});
console.log(envelope.id);import os, requests
auth = {"Authorization": f"Bearer {os.environ['CERTYNEO_API_KEY']}"}
# 1. Upload the PDF (multipart).
with open("contrat.pdf", "rb") as f:
doc = requests.post(
"https://certyneo.com/api/v1/documents",
headers=auth,
files={"file": ("contrat.pdf", f, "application/pdf")},
).json()
# 2. Create the DRAFT envelope.
envelope = requests.post(
"https://certyneo.com/api/v1/envelopes",
headers={**auth, "Content-Type": "application/json"},
json={
"subject": "Contrat de prestation",
"documentIds": [doc["id"]],
"recipients": [
{"email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER"},
],
},
).json()
# 3. Dispatch — this triggers the invitation channel for every recipient.
requests.post(
f"https://certyneo.com/api/v1/envelopes/{envelope['id']}/send",
headers=auth,
)
print(envelope["id"])Experimentar a API a partir de suas ferramentas
A coleção Postman e a ficha RapidAPI são geradas a partir da especificação OpenAPI que documenta também esta página. Os três permanecem portanto alinhados na API real, endpoint por endpoint, em vez de divergirem na primeira adição.
Coleção Postman
As 25 requisições organizadas por domínio — envelopes, documentos, modelos, selos, webhooks — com um exemplo de corpo e resposta para cada uma. Cole sua chave na variável apiKey da coleção, depois execute GET /health: ela não requer nenhuma autenticação e confirma que sua configuração está correta antes da primeira chamada autenticada.
Envelos
Criação, envio, rastreamento de status, cancelamento. Um envelo pode conter vários documentos e vários signatários (paralelo ou sequencial).
Webhooks
Todos os eventos de envelope e destinatário (`envelope.sent`, `recipient.signed`, `envelope.completed`…) entregues no URL à sua escolha — lista completa em /developers/webhooks. HMAC SHA-256 em cada payload para verificar a origem.
Autenticação simples
Bearer token. Uma chave por ambiente (teste/produção). Revogável instantaneamente. Limite de 100 req/min/chave, burst de 200, 429 limpo com cabeçalho Retry-After.
Endpoints disponíveis
O conjunto de todas as rotas públicas: conta, documentos, envelopes, modelos, webhooks, selos eletrónicos, chaves API e faturação. Todas aceitam um token Bearer e devolvem JSON.
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/account/me | Identity of the authenticated caller (id, email, plan) — scope-less credential probe |
| POST | /api/v1/documents | Upload a PDF (multipart) — returns document id |
| GET | /api/v1/documents | List documents |
| GET | /api/v1/documents/{id} | Fetch document metadata |
| DELETE | /api/v1/documents/{id} | Delete document |
| GET | /api/v1/envelopes | List envelopes (filter with ?status= and ?limit=) |
| POST | /api/v1/envelopes | Create envelope (status: DRAFT) — from a templateId, or from documentIds with an optional fields array |
| GET | /api/v1/envelopes/{id} | Fetch envelope state |
| PATCH | /api/v1/envelopes/{id} | Update DRAFT envelope |
| DELETE | /api/v1/envelopes/{id} | Void / delete DRAFT envelope |
| POST | /api/v1/envelopes/{id}/send | Dispatch DRAFT — sends invitations |
| GET | /api/v1/envelopes/{id}/audit-trail | Download eIDAS audit-trail PDF |
| GET | /api/v1/audit-anchors/{root} | Public: metadata of an anchored audit batch, or the .ots proof file with ?format=ots (no auth) |
| POST | /api/v1/audit-anchors/verify | Public: verify an audit entry + proof against its anchored Merkle root (no auth, reveals nothing) |
| GET | /api/v1/envelopes/{id}/signed-document | Download signed PDF (once COMPLETED) |
| GET | /api/v1/envelopes/bulk | List your bulk-send jobs |
| POST | /api/v1/envelopes/bulk | Bulk send: create N envelopes from one template + a CSV (Standard/Business only) |
| GET | /api/v1/envelopes/bulk/{id} | Bulk-send job progress: counts, per-row failures, created envelopes |
| GET | /api/v1/templates | List reusable envelope templates |
| POST | /api/v1/templates | Create a template (documents, roles, positioned fields) |
| POST | /api/v1/sepa-mandates | Generate a SEPA mandate PDF and its DRAFT envelope, fields already placed |
| POST | /api/v1/payroll-adapters/normalize | Normalise a payroll CSV (Silae, Sage Paie, PayFit, Lucca) into the bulk-send shape |
| POST | /api/v1/ag-copropriete | Create a condominium general-meeting envelope (resolutions + ownership shares) |
| POST | /api/v1/ag-copropriete/{envelopeId}/votes | Record a co-owner's votes on the meeting resolutions |
| POST | /api/v1/ag-copropriete/{envelopeId}/tally | Tally the meeting - per-resolution result weighted by ownership shares |
| GET | /api/v1/videos/{videoId} | Download a stored identity video - GDPR art. 15 access path |
| GET | /api/v1/webhooks | List webhooks |
| POST | /api/v1/webhooks | Register webhook — returns the signing secret once |
| GET | /api/v1/webhooks/{id} | Fetch webhook subscription |
| PATCH | /api/v1/webhooks/{id} | Update url / events / active state |
| DELETE | /api/v1/webhooks/{id} | Unregister |
| POST | /api/v1/seals | Apply a qualified electronic seal to a document |
| GET | /api/v1/seals/{id} | Fetch seal status |
| GET | /api/v1/seals/{id}/certificate | Download the seal certificate |
| GET | /api/v1/keys | List API keys |
| POST | /api/v1/keys | Create API key — the secret is shown once |
| PATCH | /api/v1/keys/{id} | Rename / revoke key |
| DELETE | /api/v1/keys/{id} | Delete key |
| GET | /api/v1/billing/usage | Current period usage and projected cost |
| GET | /api/v1/status | Service status |
| GET | /api/v1/openapi | Machine-readable OpenAPI specification |
Modelos de envelope
Um modelo registra uma vez por todas o PDF, as funções e a localização dos campos de assinatura, e depois é reutilizado a cada envio: passe seu identificador em templateId em vez de documentIds, e os campos posicionados são copiados para o envelope criado.
- • Os modelos são criados a partir do painel de controle (Modelos → Novo modelo), onde você faz upload do documento e posiciona os campos com o mouse — este é o caminho mais simples. A API também permite criá-los via POST /api/v1/templates, fornecendo os documentos, as funções e os campos; observe que os campos são posicionados em coordenadas absolutas (página, x, y, largura, altura), então você precisa conhecer o layout do PDF.
- • Em uma conta que ainda não registrou nenhum modelo, GET /api/v1/templates retorna uma lista vazia. Este é o comportamento normal, não é uma falha de autenticação.
- • A lista contém apenas os modelos pertencentes ao usuário proprietário da chave API. Um modelo criado por um colega não aparece nela, mesmo dentro de um espaço de trabalho compartilhado: gere a chave a partir da conta que possui o modelo.
- • templateId e documentIds são mutuamente exclusivos: envie um ou outro, nunca ambos nem nenhum dos dois.
- • Forneça pelo menos tantos destinatários SIGNER quantas funções signatárias o modelo possui, caso contrário a criação é recusada em 400. O campo signerCount retornado pela lista indica o número esperado.
- • O nível de assinatura do modelo é herdado pelo envelope, a menos que a solicitação passe explicitamente signatureLevel.
# 1. Discover the templates saved on this account.
curl -s https://certyneo.com/api/v1/templates \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
# {
# "data": [
# { "id": "cmdq7f4k80001s6y2h1xa9pl3", "name": "Contrat de prestation",
# "signerCount": 2, "documentCount": 1, "signatureLevel": "SIMPLE" }
# ],
# "pagination": { "page": 1, "pageSize": 20, "total": 1, "pages": 1 }
# }
#
# An empty "data" array means no template exists on this account yet —
# create one from the dashboard, it is not an authentication problem.
# 2. Create the envelope FROM the template: no documentIds and no field
# coordinates, both are carried by the template. Pass one SIGNER
# recipient per signer role, in the template's role order.
curl -X POST https://certyneo.com/api/v1/envelopes \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"subject": "Contrat de prestation",
"templateId": "cmdq7f4k80001s6y2h1xa9pl3",
"recipients": [
{ "email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER" },
{ "email": "legal@example.com", "name": "Paul Martin", "role": "SIGNER" }
]
}'
# 3. The envelope is DRAFT at this point — POST /envelopes/{id}/send
# dispatches it, exactly as in the quick-start above.A partir do Power Automate ou Zapier
A ação "Criar um envelope" dos nossos conectores sem código cobre apenas o caminho por modelo: o campo Modelo é obrigatório nele. Para um documento ad hoc que muda a cada execução, use a ação "Fazer upload de um documento" seguida de uma ação HTTP bruta para POST /api/v1/envelopes passando o documentIds retornado.
Envio do documento: duas formas aceitas
POST /api/v1/documents aceita o arquivo de duas formas, à escolha. Os mesmos controles se aplicam nos dois casos: tipos autorizados, limite de 50 Mo, verificação da assinatura binária e análise antivírus.
- • Em multipart/form-data, com uma parte nomeada file. Esta é a forma clássica, a do curl -F e da maioria das bibliotecas.
- • Em corpo bruto: os bytes do arquivo constituem o corpo da requisição, e o cabeçalho Content-Type fornece seu tipo (application/pdf por exemplo). Útil a partir de uma ferramenta que transmite o conteúdo tal qual, sem envolver a requisição — é o que faz o conector Power Automate.
- • Em corpo bruto, o nome do arquivo não tem lugar no corpo: indique-o via cabeçalho X-File-Name ou parâmetro ?fileName=. Sem ele, o documento é nomeado de acordo com seu tipo.
- • Um tipo não suportado responde 415 nomeando as duas formas aceitas, e um corpo anunciado como multipart mas ilegível responde 400. Nenhum dos dois é um erro de servidor.
# a. multipart/form-data — the classic shape.
curl -X POST https://certyneo.com/api/v1/documents \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-F "file=@contrat.pdf;type=application/pdf"
# b. raw body — the file bytes ARE the body, typed by Content-Type.
# The filename has nowhere to live in the body, so pass it as a header
# (or ?fileName=). Without it the document is named after its type.
curl -X POST https://certyneo.com/api/v1/documents \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/pdf" \
-H "X-File-Name: contrat.pdf" \
--data-binary "@contrat.pdf"
# Both return the same 201 with the document id to pass as documentIds.Posicionamento dos campos de assinatura
Sem modelo, um envelope criado a partir de documentIds não tem nenhum campo pré-posicionado: o signatário recebe o documento sem local para assinar. O array fields, transmitido na mesma chamada de criação, coloca cada campo no ponto exato — é o equivalente, no lado da API, do que um modelo registra uma vez por todas.
- • As coordenadas estão em pontos PDF, com origem no canto superior esquerdo da página e eixo Y apontando para baixo (uma página A4 mede 595 × 842 pontos). x e y designam o canto superior esquerdo do campo, width e height seu tamanho.
- • pageNumber começa em 1, documentIndex começa em 0. Um número de página além do documento não é rejeitado na criação: o campo é ignorado no momento da assinatura e não aparece em lugar nenhum — é a primeira coisa a verificar quando um campo está faltando.
- • recipientEmail deve corresponder a um dos destinatários da mesma chamada, sem distinção de maiúsculas e minúsculas. Caso contrário, a criação falhará listando todas as linhas com falha, o que evita corrigi-las uma por uma.
- • fields e templateId são mutuamente exclusivos: um modelo já carrega seu próprio layout. O array fields é usado apenas com documentIds.
- • Tipos aceitos: SIGNATURE, INITIALS, DATE_SIGNED, TEXT, CHECKBOX e RADIO_GROUP. required é verdadeiro por padrão; placeholder e dateFormat são opcionais, e options é considerado apenas para RADIO_GROUP.
- • Um envelope aceita no máximo 100 campos, 20 documentos e 50 destinatários — os limites do seu plano podendo ser inferiores.
# Ad-hoc envelope WITH pre-placed fields — no template involved.
# Coordinates are PDF points, origin TOP-LEFT of the page, +Y downwards
# (A4 = 595 x 842 pt). x / y are the field box's top-left corner.
#
# The last field is positioned by ANCHOR instead of by eye: the server
# locates "Signature du client" in the PDF and computes the spot. x / y
# stay required there — they are the fallback if the text is not found.
curl -X POST https://certyneo.com/api/v1/envelopes \
-H "Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"subject": "Contrat de prestation",
"documentIds": ["cmdq7f4k80001s6y2h1xa9pl3"],
"recipients": [
{ "email": "client@example.com", "name": "Marie Dubois", "role": "SIGNER" }
],
"fields": [
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "SIGNATURE",
"x": 90, "y": 640, "width": 180, "height": 44 },
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "DATE_SIGNED",
"x": 320, "y": 640, "width": 140, "height": 30,
"dateFormat": "DD/MM/YYYY" },
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "TEXT",
"x": 90, "y": 700, "width": 200, "height": 30,
"placeholder": "Fonction", "required": false },
{ "recipientEmail": "client@example.com", "documentIndex": 0,
"pageNumber": 2, "fieldType": "SIGNATURE",
"anchorText": "Signature du client", "anchorPlacement": "below",
"anchorIndex": 0,
"x": 90, "y": 640, "width": 180, "height": 44 }
]
}'
# The envelope is DRAFT at this point — POST /envelopes/{id}/send
# dispatches it, exactly as in the quick-start above.Âncoras textuais: colocar um campo sem conhecer as coordenadas
Em vez de coordenadas, um campo pode citar um texto impresso no documento: anchorText o localiza no PDF e o servidor calcula a posição na criação. Este é o modo a privilegiar quando o documento é regenerado a cada envio — mala direta, gerador de contratos — pois o layout se move enquanto a menção « Assinatura do cliente » permanece. anchorPlacement indica de qual lado do texto o campo se coloca (right por padrão, caso contrário below, above ou left) e anchorIndex escolhe a ocorrência quando o texto aparece várias vezes.
x e y permanecem obrigatórios mesmo com uma âncora: servem como fallback. Uma âncora não encontrada não causa falha na criação — o campo mantém a posição literal que você forneceu, sem erro nem aviso na resposta. Indique portanto um fallback plausível em vez de 0,0, e verifique a renderização em um primeiro envio.
Autenticação
Cada chamada carrega uma chave API no cabeçalho Authorization. As chaves são geradas em Configurações → Chaves API e são exibidas apenas uma vez.
GET /api/v1/account/me HTTP/1.1Host: certyneo.com
Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
# 200 OK
{ "data": { "id": "usr_…", "email": "you@example.com", "plan": "BUSINESS", "environment": "live" } }- • Formato: sk_live_… em produção, sk_test_… para sandbox. Cabeçalho: Authorization: Bearer <clé>.
- • Escopos: envelopes, documents, webhooks, seals — em leitura (:read) ou escrita (:write). A escrita implica leitura; o escopo * concede todos os direitos.
- • As chaves sk_test_ criam recursos na caixa de areia, excluídos da cota e da faturação. Nenhum e-mail real é enviado ao destinatário, a menos que o endereço dele corresponda ao e-mail da conta que envia — útil para testar o fluxo completo em você mesmo.
- • Erros: 401 chave inválida, 403 escopo insuficiente, 429 limite de taxa excedido, 402 cota mensal atingida.
Formato das respostas
Um ponto importante a saber antes de escrever seu cliente: as coleções são encapsuladas em um objeto data, enquanto os recursos unitários são retornados de forma plana. Ler response.data.data em um recurso unitário retorna undefined.
// GET /api/v1/envelopes
// Collections are WRAPPED in a "data" array.
{
"data": [
{ "id": "env_abc123", "subject": "Contrat", "status": "SENT" }
]
}// GET /api/v1/envelopes/{id}
// Single resources are returned FLAT — no "data" envelope.
{
"id": "env_abc123",
"subject": "Contrat",
"status": "COMPLETED",
"recipients": [ /* … */ ]
}Limites de taxa
Os limites garantem qualidade de serviço estável para todos os clientes. Se precisar de mais, entre em contato conosco.
- • 100 requisições por minuto por chave API
- • Burst tolerado até 200 requisições em menos de 10s
- • Resposta 429 com cabeçalho Retry-After indicando o atraso em segundos