ArcSign API · versão 1.0.0 · OpenAPI 3.1.0

A documentação da ArcSign API

A API do ArcSign deixa o seu sistema enviar documentos para assinar, acompanhar cada assinatura e baixar o documento concluído, com o certificado de conclusão.

Quem chama é o seu servidor, com uma chave de API no cabeçalho Authorization. A organização vem da chave, e de nada mais: não existe parâmetro de organização.

Regras que valem em todo endereço:

  • os nomes que o seu programa lê — campos, fases, códigos de erro, escopos — estão em inglês e não mudam; as frases (title, detail, label) estão em português e podem mudar: decida pelo código, nunca pela frase;
  • o campo que o endereço não conhece é RECUSADO (422 invalid_body, com errors[].code unknown_field): um erro de digitação num campo opcional não passa calado;
  • toda resposta leva X-Request-Id; a de uma chave reconhecida leva também os RateLimit-*;
  • as datas vão e voltam em ISO 8601 (a hora com o fuso, ou Z).

Comece em cinco minutos

  1. Cadastre-se e confirme o e-mail: a chave de teste (arcs_test_…) sai na página do link.
  2. Guarde a chave numa variável de ambiente do seu servidor — os exemplos leem ARCSIGN_API_KEY.
  3. Confirme a chave e os limites com o «quem sou eu»:

curl

curl -X GET "https://app.arcsphera.com/api/v1/me" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Depois, mande um envelope de teste — o mesmo pedido da produção, com a chave de teste. Troque o e-mail pelo da SUA conta: com a chave de teste, o convite vai só às contas confirmadas da sua organização.

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/envelopes" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: createEnvelope-exemplo-1" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Meu primeiro envelope",
  "documentType": "OTHER",
  "status": "sent",
  "documents": [
    {
      "name": "contrato-de-teste.pdf",
      "contentBase64": "JVBERi0xLjcKJYGBgYEKCjUgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL0xlbmd0aCAxMTAKPj4Kc3RyZWFtCnicHYpBCsJQDAX37xRZF4r5aX7yC+JCaOnCjZALiNTSoouKeH5tGRhmMSvOAaaN94TDMD6/42e+32rntmhhLy0loXhAlOKCtK+JhCkzxQtHbay3ztXFkqv1wqaWhf+dvdl8olgQFbrAFT+/TBi+CmVuZHN0cmVhbQplbmRvYmoKCjYgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL1R5cGUgL09ialN0bQovTiA0Ci9GaXJzdCAyMAovTGVuZ3RoIDIzMgo+PgpzdHJlYW0KeJxVj91qAjEQhe/zFPMCZRK7siuIoNIfKMWihRZKL+LusKRIUsxs0bfvTLpFvAhkzjkzfMeBhQlUFdxC3UAFk2kN87nB1/M3Ab74nrLBp9Bl+JCIhS18GlynITI4s1iYS3bt2R9Sb/6WwGn4OqGG2kfS7eLjlnIajq0saO4+iaOfRzr8EIfW39R21lSNrZuZ0I0n8X2z/6K2RHW8O/HDjj3TKKj2TF3wq3QSbqsdrQUnT+iXMSbWPqVJZKHRaTq2u0JWIIO7Yc9lVNEZXPlMBfXCKRCxTV2IPeBbiMuYw7+gF38B7fhizAplbmRzdHJlYW0KZW5kb2JqCgo3IDAgb2JqCjw8Ci9TaXplIDgKL1Jvb3QgMiAwIFIKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL1R5cGUgL1hSZWYKL0xlbmd0aCAzOAovVyBbIDEgMiAyIF0KL0luZGV4IFsgMCA4IF0KPj4Kc3RyZWFtCnicFcSxDQAgCACwgomzi/+fw1koHYrutJliymmFQ6hfXh5UAAMWCmVuZHN0cmVhbQplbmRvYmoKCnN0YXJ0eHJlZgo1MzMKJSVFT0Y="
    }
  ],
  "recipients": [
    {
      "name": "Você",
      "email": "o-email-da-sua-conta@example.com"
    }
  ]
}'

O convite chega ao seu e-mail. Assine pelo celular, receba o aviso no seu webhook e baixe o PDF e o certificado.

A chave

A chave vai SÓ no cabeçalho Authorization: Bearer arcs_live_…. A chave na URL é recusada com 400 (api_key_in_url): a URL vai para o log. A chave de teste (arcs_test_…) nasce no cadastro; a de produção (arcs_live_…), depois que a Arcsphera libera a produção.

Os escopos: envelopes:read (consultar), envelopes:send (enviar), envelopes:download (baixar os documentos), envelopes:void (cancelar), templates:read (consultar os modelos) e webhooks:manage (gerenciar os webhooks). Cada operação diz o seu no security.

  • A chave age como o DONO dela: vale o menor entre os escopos marcados e as permissões que o dono tem hoje.
  • Uma chave por sistema, com o escopo mínimo. O escopo não muda depois de criada: outro escopo, outra chave.
  • Trocar sem parar: crie a nova, troque no seu sistema, revogue a velha. A revogação vale na chamada seguinte.

Teste e produção

O endereço é o mesmo; é a chave que diz o ambiente. A chave de teste manda envelopes fora da cota, sem a conferência paga na Receita, só às contas confirmadas da sua organização, e com o limite de 10 envelopes por semana (conta o envio; o rascunho não conta). O PDF concluído de um envelope de teste sai com a tarja «TESTE — SEM VALIDADE» em toda página; o convite e a página de assinar não levam a tarja. A chave de produção só nasce depois que a Arcsphera libera a produção da sua organização — peça no painel do desenvolvedor, com o aceite dos termos de uso e do acordo de dados. Na produção conta o envelope FINALIZADO: o recusado, o expirado e o cancelado não custam.

Repetir sem medo: Idempotency-Key

Obrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho Idempotent-Replayed: true (e o Location, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de limits → idempotency_key_hours. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (delivery_in_progress, webhook_disabled) não se guardam: corrija e mande de novo com a mesma chave.

O externalReference não é idempotência: dois envelopes podem ter o mesmo (o contrato reenviado depois de cancelado). Ele serve para achar.

Os erros

Os erros seguem a RFC 9457 (application/problem+json), com dois campos a mais: code, estável, em inglês — é o que o seu programa lê — e errors[], com o pointer do campo. Trate o erro pelo code, nunca pela frase: a frase pode mudar, o code não. Mostre o detail ao seu usuário e guarde o requestId. Os campos de limite (limit, window, retryAfter, resetsAt, limitBytes) e o envelopeId vêm só nos erros que os usam.

HTTPcodeQuando acontece
400malformed_requestPedido malformado. O corpo não é JSON, ou não é um objeto.
400idempotency_key_requiredFalta a Idempotency-Key. POST sem o cabeçalho Idempotency-Key.
400idempotency_key_invalidIdempotency-Key inválida. A Idempotency-Key está vazia, tem espaço ou passa de 255 caracteres.
400api_key_in_urlA chave de API não vai na URL. A chave — ou um parâmetro com cara de chave (key, api_key, token…) — veio na URL, mesmo com o cabeçalho certo.
401unauthorizedChave de API ausente ou inválida. Sem chave, chave desconhecida, revogada ou vencida: a mesma resposta para os quatro.
403not_available_in_testNão disponível com a chave de teste. Com a chave de teste: a conferência paga na Receita (CPF ou CNPJ), ou o convite para quem não é uma conta confirmada da organização nem um contato confirmado da ArcSign API.
403scope_missingA chave não tem este escopo. A chave não tem o escopo que o endereço pede.
403owner_lacks_permissionO dono da chave não tem a permissão. O dono da chave perdeu, na plataforma, a permissão que o escopo pede.
403owner_inactiveO dono da chave está desativado. O dono da chave foi desativado: a chave para junto.
403ip_not_allowedIP não permitido para esta chave. A chave tem lista de IPs, e o pedido veio de fora dela.
403license_requiredA organização não contratou a ArcSign API. A organização da chave não tem a ArcSign API na licença — ou só a tem em modo teste, e a chamada veio de uma chave de produção (arcs_live_).
404not_foundNão encontrado. Não existe, ou é de outra organização — as duas respostas são iguais.
409idempotency_in_progressO primeiro pedido com esta Idempotency-Key ainda está rodando. O primeiro pedido com a mesma Idempotency-Key ainda está rodando; tente de novo em segundos.
409envelope_not_in_circulationO envelope não está em circulação. Cancelar o envelope que não está em circulação (concluído, recusado, expirado ou rascunho).
409envelope_not_draftO envelope não é mais um rascunho. Enviar o envelope que já não é rascunho nem está em circulação (concluído, recusado, cancelado ou expirado).
409document_not_readyO documento ainda não existe. Baixar o PDF concluído ou o certificado antes de todos assinarem e o selo sair. O aviso envelope.completed pode chegar segundos antes do PDF selado: tente de novo em instantes.
409webhook_already_existsJá existe um webhook com este endereço. A URL já está cadastrada em outro webhook da organização — cada evento iria duas vezes.
409webhook_disabledO webhook está desligado. Reenviar uma entrega de webhook desligado: religue-o antes (PATCH com enabled: true). O teste funciona no desligado.
409delivery_in_progressA entrega está saindo agora. Reenviar uma entrega que está saindo naquele instante; tente de novo em segundos.
413payload_too_largeO pedido passa do tamanho máximo. O pedido passa de 25 MB — uns 18 MB de PDF somados, em base64. Mande menos documentos no envelope, ou PDFs menores.
422idempotency_key_reusedIdempotency-Key já usada em outro pedido. A mesma Idempotency-Key num pedido diferente (outro corpo ou outro endereço). Pedido novo, chave nova.
422invalid_bodyO pedido tem campos a corrigir. Campos a corrigir no corpo ou nos parâmetros; errors[] aponta cada um (o campo que o endereço não conhece vem como unknown_field; o parâmetro da URL, como /query/<nome>).
422document_invalidO documento não serve. O documento não é PDF, passa de 15 MB, está vazio, tem senha ou não abre.
422document_infectedO antivírus recusou o documento. O antivírus achou vírus num PDF; nada foi gravado.
422not_supported_by_apiAinda não pela API. O modelo que leva o Selo da organização: a API ainda não o põe. O destinatário SEAL no corpo vem como invalid_body, com errors[].code not_supported_by_api.
422blocked_by_ruleUma regra da organização impediu. Uma regra «Impedir» da organização recusou a gravação; o detail traz a frase dela.
422envelope_not_sentO envelope não saiu. O envio foi recusado (falta documento, o prazo, a conferência sem serviço…); o rascunho fica, e o envelopeId vem no corpo.
422webhook_limit_reachedA organização já tem o máximo de webhooks. Criar webhook quando a organização já tem o máximo («Webhooks por organização»).
423license_read_onlyA licença está só de leitura. A licença está só de leitura (suspensa, cancelada ou vencida): o GET responde, o que grava não.
429rate_limitedLimite de chamadas atingido. A chave passou das chamadas por minuto ou da rajada. Retry-After diz quando voltar.
429too_many_failed_attemptsChaves erradas demais deste endereço. Este endereço errou a chave vezes demais em 10 minutos. Espere a janela virar.
429test_weekly_limitLimite semanal de envelopes de teste. A chave de teste passou dos envelopes de teste da semana; resetsAt diz quando volta.
429invite_limitLimite de convites por hora. O envio mandaria mais convites do que a organização pode nesta hora.
500internal_errorErro inesperado. Algo deu errado do nosso lado e o pedido não terminou. Tente de novo com a mesma Idempotency-Key; se repetir, mande o requestId ao suporte.
503webhooks_unavailableO webhook não está disponível nesta instalação. A instalação não tem a chave do cofre que guarda o segredo do webhook: não cria webhook nem troca segredo.
503api_unavailableA API não está disponível nesta instalação. A API não está habilitada nesta instalação (falta a configuração do servidor).
503read_only_databaseO banco está só de leitura. O banco está só de leitura (a máquina reserva atende): leia, e tente gravar mais tarde.
503antivirus_unavailableO antivírus não respondeu. O antivírus não respondeu; o documento não foi aceito sem varredura.

Os limites

Os números de fábrica, abaixo; o da sua organização pode ser outro (o negócio fechado), e o GET /api/v1/me mostra os que valem para você, pela chave de cada um (limits[].key). Toda resposta de uma chave reconhecida — o sucesso e o erro dela — leva RateLimit-Limit, -Remaining e -Reset, das chamadas da chave; o 401, a chave na URL, o 429 por chaves erradas e o 503 da API desligada não levam, porque acontecem antes de saber de quem é a chave. O 429 diz no corpo o limite que valeu (limit, window, retryAfter) e traz Retry-After; o 401 traz WWW-Authenticate.

  • Envelopes de teste por semana10
  • Chamadas por minuto, por chave120
  • Rajada por chave, em 5 segundos30
  • Consultas ao mesmo envelope, por minuto1
  • Downloads do mesmo documento, por hora10
  • Convites por hora, por organização200
  • Chaves vivas por organização10
  • Webhooks por organização5
  • Horas que a Idempotency-Key fica guardada24
  • Dias de novas tentativas do webhook3

A referência

Conta

Quem é a chave, o que ela pode, e os limites que valem.

get/api/v1/mequalquer chave

Quem sou eu

A organização, a chave, os escopos que valem HOJE, os limites e a cota do período. Chame na subida do seu sistema: confirma a chave, os escopos e os limites antes do primeiro envio.

Os escopos que valem são o menor de dois: os marcados na chave e as permissões que o dono da chave tem hoje. Os limites são os da sua organização — o número que o seu programa compara está em limits[].key e rateLimits[].counter.

curl

curl -X GET "https://app.arcsphera.com/api/v1/me" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/me", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
organization *object
organization.id *string
organization.name *string | null
apiKey *object
apiKey.id *string
apiKey.name *string
apiKey.prefix *stringO começo visível da chave, para reconhecê-la.
apiKey.scopes *Scope[]Os escopos que VALEM hoje.
owner *objectO usuário dono da chave. A chave age como ele, e para quando ele perde a permissão.
owner.name *string | nullNulo quando a conta não tem nome.
owner.email *string
rateLimits *object[]Os contadores de chamada, com o que já se usou na janela.
rateLimits[].counter *stringA chave do contador — a mesma do número em `limits`.requests_per_minute · requests_per_5_seconds
rateLimits[].label *string
rateLimits[].windowSeconds *integer
rateLimits[].limit *integer
rateLimits[].used *integer
rateLimits[].resetSeconds *integer
limits *object[]Os números que valem para a organização (o de fábrica, ou o que o negócio fechado mudou).
limits[].key *LimitKeyA chave de cada número do `/me`: `test_envelopes_per_week` (envelopes de teste por semana), `requests_per_minute` e `requests_per_5_seconds` (as chamadas da chave), `envelope_reads_per_minute` (consultas ao mesmo envelope), `document_downloads_per_hour` (downloads do mesmo documento), `invitations_per_hour` (convites da organização), `active_api_keys`, `webhooks`, `webhook_retry_days` (os dias de novas tentativas, e de falha seguida até desligar) e `idempotency_key_hours`. test_envelopes_per_week · requests_per_minute · requests_per_5_seconds · envelope_reads_per_minute · document_downloads_per_hour · invitations_per_hour · active_api_keys · webhooks · webhook_retry_days · idempotency_key_hours
limits[].label *string
limits[].value *integer
envelopeQuota *objectA cota de envelopes do período.
envelopeQuota.periodFrom *string (date-time)
envelopeQuota.periodTo *string (date-time)
envelopeQuota.quota *integer
envelopeQuota.counted *integerOs envelopes que contaram na cota no período — os CONCLUÍDOS (a 1b, 21/10/2026).
envelopeQuota.countedWhen *stringQuando o envelope conta: ao concluir. O recusado, o expirado e o cancelado não contam.completed
envelopeQuota.overage *integer
  • 200 A chave vale.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/me/revokequalquer chave

Revogar esta chave

A chave que chama revoga A SI MESMA, na hora — o botão de pânico de quem viu a chave num commit, num log ou num print. A chamada seguinte com ela recebe 401, e todos os contatos da sua empresa recebem o e-mail «Uma chave foi revogada pela própria chave».

Uma chave nunca revoga OUTRA (quem roubasse uma derrubaria as boas), nem cria outra: a chave nova se pede pelo e-mail, e chega por um link que a mostra uma única vez. Vale também com o contrato suspenso ou encerrado — revogar é segurança. O corpo é vazio, ou {}. Repetir depois de revogada responde 401: a chave já não vale.

ParâmetroOndeO que é
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/me/revoke" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: revokeMe-exemplo-1"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/me/revoke", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
revoked *booleantrue
revokedAt *string (date-time)
apiKey *object
apiKey.id *string (uuid)
apiKey.name *string
apiKey.prefix *string
  • 200 A chave foi revogada. Daqui em diante, ela recebe 401.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.

Envelopes

Enviar, acompanhar, baixar e cancelar.

get/api/v1/envelopesescopo envelopes:read

Listar os envelopes

Os envelopes da organização da chave, em ordem de updatedAt crescente, com cursor. Para saber o que mudou desde a última vez, passe updatedSince e siga nextCursor até ele vir null.

Peça com SOBREPOSIÇÃO: updatedSince = o maior updatedAt que você já viu MENOS 5 minutos, e descarte o envelope que você já processou com o mesmo updatedAt. O updatedAt é a hora em que a mudança foi gravada, e uma gravação longa pode aparecer na lista com a hora de um pouco antes da que você já leu. Todo passo que a API mostra — de cada destinatário também (abriu, assinou, o convite saiu) — muda o updatedAt do envelope.

A chave de teste lista só os envelopes de teste; a de produção, só os de verdade.

ParâmetroOndeO que é
statusquery
updatedSincequeryISO 8601: a data (2026-10-21) ou a data e a hora com o fuso (2026-10-21T15:04:05Z). Só os envelopes alterados desde então.
externalReferencequeryIgual, exato. Não é única: o contrato reenviado repete.
onBehalfOfIdqueryO id do seu cliente, como você o mandou em onBehalfOf.id.
limitquery
cursorqueryO nextCursor da página anterior.

curl

curl -X GET "https://app.arcsphera.com/api/v1/envelopes" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
data *EnvelopeSummary[]
nextCursor *string | null
  • 200 Uma página da lista.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/envelopesescopo envelopes:send

Criar um envelope

Pelo PDF (documents, em base64, com recipients e documentType) ou pelo Modelo (templateId + fields + templateRoles) — um dos dois. status: "draft" (o padrão) guarda o rascunho; "sent" envia na mesma chamada. Se o envio for recusado, o rascunho FICA e a resposta 422 traz o envelopeId: corrija e envie por POST /envelopes/{id}/send.

O pedido inteiro vai até 25 MB — uns 18 MB de PDF somados, em base64 (413 acima disso); cada PDF, até 15 MB.

Quem assina recebe o e-mail e a página SEM MARCAS: os dois nomes em texto — «Imobiliária Sol Ltda. enviou este documento pelo CRM Exemplo» —, nem o logo do ArcSign nem o seu. O selo do PDF concluído e o certificado de conclusão continuam dizendo quem os emitiu.

expectedCpf não preenche o CPF por ninguém: a pessoa digita o dela, e a página confere que é o esperado (três erros travam a assinatura).

Com a chave de TESTE, o convite sai de verdade — para você ver o e-mail chegar —, mas só para as contas confirmadas da sua organização e para os contatos confirmados da ArcSign API (quem abriu o link de uma chave pelo e-mail). Outro destinatário recusa com 403 not_available_in_test, apontando qual.

ParâmetroOndeO que é
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

O corpo

Pelo PDF

CampoTipoO que é
title *string
documentType *DocumentTypeEMPLOYMENT_CONTRACT · INTERNSHIP_AGREEMENT · SERVICES_AGREEMENT · B2B_AGREEMENT · SUPPLIER_AGREEMENT · DATA_PROCESSING_AGREEMENT · NDA · CONTRACT_AMENDMENT · INTERNAL_POLICY · OTHER
externalReferencestringO id do registro no seu sistema. Não é única.
onBehalfOfOnBehalfOfO seu cliente, em nome de quem o envelope sai. Quem assina lê os dois nomes — o dele e o da sua organização. O nome não é conferido (o certificado diz «informado por» você); o CNPJ e o CPF, só pelos dígitos verificadores. No máximo um dos dois. A separação entre os seus clientes é sua: o filtro `onBehalfOfId` ajuda, mas não é parede.
statusstringdraft · sent
expiresInDaysinteger
emailSubjectstring
messagestringA mensagem a todos, no corpo do e-mail.
documents *object[]
documents[].name *string
documents[].contentBase64 *stringO PDF em base64 — até 15 MB por arquivo, sem senha; passa pelo antivírus antes de gravar.
recipients *NewRecipient[]

Pelo Modelo

CampoTipoO que é
templateId *string (uuid)
titlestringVazio usa o título do modelo.
externalReferencestringO id do registro no seu sistema. Não é única.
onBehalfOfOnBehalfOfO seu cliente, em nome de quem o envelope sai. Quem assina lê os dois nomes — o dele e o da sua organização. O nome não é conferido (o certificado diz «informado por» você); o CNPJ e o CPF, só pelos dígitos verificadores. No máximo um dos dois. A separação entre os seus clientes é sua: o filtro `onBehalfOfId` ajuda, mas não é parede.
statusstringdraft · sent
expiresInDaysinteger
emailSubjectstring
messagestringA mensagem a todos, no corpo do e-mail.
fieldsobjectO valor de cada campo do modelo, pela key, como TEXTO na forma do tipo (`TemplateFieldType`).
templateRolesNewTemplateRole[]

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/envelopes" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: createEnvelope-exemplo-1" \
  -H "Content-Type: application/json" \
  -d '{
  "title": "Locação — Rua das Flores, 120, ap. 31",
  "documentType": "OTHER",
  "externalReference": "crm:contrato:ck9x2",
  "onBehalfOf": {
    "id": "imob_42",
    "name": "Imobiliária Sol Ltda.",
    "cnpj": "11.222.333/0001-81"
  },
  "status": "sent",
  "expiresInDays": 15,
  "documents": [
    {
      "name": "contrato.pdf",
      "contentBase64": "JVBERi0xLjcKJYGBgYEKCjUgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL0xlbmd0aCAxMTAKPj4Kc3RyZWFtCnicHYpBCsJQDAX37xRZF4r5aX7yC+JCaOnCjZALiNTSoouKeH5tGRhmMSvOAaaN94TDMD6/42e+32rntmhhLy0loXhAlOKCtK+JhCkzxQtHbay3ztXFkqv1wqaWhf+dvdl8olgQFbrAFT+/TBi+CmVuZHN0cmVhbQplbmRvYmoKCjYgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL1R5cGUgL09ialN0bQovTiA0Ci9GaXJzdCAyMAovTGVuZ3RoIDIzMgo+PgpzdHJlYW0KeJxVj91qAjEQhe/zFPMCZRK7siuIoNIfKMWihRZKL+LusKRIUsxs0bfvTLpFvAhkzjkzfMeBhQlUFdxC3UAFk2kN87nB1/M3Ab74nrLBp9Bl+JCIhS18GlynITI4s1iYS3bt2R9Sb/6WwGn4OqGG2kfS7eLjlnIajq0saO4+iaOfRzr8EIfW39R21lSNrZuZ0I0n8X2z/6K2RHW8O/HDjj3TKKj2TF3wq3QSbqsdrQUnT+iXMSbWPqVJZKHRaTq2u0JWIIO7Yc9lVNEZXPlMBfXCKRCxTV2IPeBbiMuYw7+gF38B7fhizAplbmRzdHJlYW0KZW5kb2JqCgo3IDAgb2JqCjw8Ci9TaXplIDgKL1Jvb3QgMiAwIFIKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL1R5cGUgL1hSZWYKL0xlbmd0aCAzOAovVyBbIDEgMiAyIF0KL0luZGV4IFsgMCA4IF0KPj4Kc3RyZWFtCnicFcSxDQAgCACwgomzi/+fw1koHYrutJliymmFQ6hfXh5UAAMWCmVuZHN0cmVhbQplbmRvYmoKCnN0YXJ0eHJlZgo1MzMKJSVFT0Y="
    }
  ],
  "recipients": [
    {
      "type": "SIGNER",
      "order": 1,
      "name": "Ana Souza",
      "email": "ana@example.com",
      "expectedCpf": "529.982.247-25"
    },
    {
      "type": "SIGNER",
      "order": 1,
      "name": "Bruno Lima",
      "email": "bruno@example.com"
    },
    {
      "type": "SIGNER",
      "order": 2,
      "name": "Testemunha 1",
      "email": "t1@example.com"
    },
    {
      "type": "CARBON_COPY",
      "order": 3,
      "name": "Imobiliária Sol",
      "email": "contratos@example.com"
    }
  ]
}'

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "title": "Locação — Rua das Flores, 120, ap. 31",
    "documentType": "OTHER",
    "externalReference": "crm:contrato:ck9x2",
    "onBehalfOf": {
      "id": "imob_42",
      "name": "Imobiliária Sol Ltda.",
      "cnpj": "11.222.333/0001-81"
    },
    "status": "sent",
    "expiresInDays": 15,
    "documents": [
      {
        "name": "contrato.pdf",
        "contentBase64": "JVBERi0xLjcKJYGBgYEKCjUgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL0xlbmd0aCAxMTAKPj4Kc3RyZWFtCnicHYpBCsJQDAX37xRZF4r5aX7yC+JCaOnCjZALiNTSoouKeH5tGRhmMSvOAaaN94TDMD6/42e+32rntmhhLy0loXhAlOKCtK+JhCkzxQtHbay3ztXFkqv1wqaWhf+dvdl8olgQFbrAFT+/TBi+CmVuZHN0cmVhbQplbmRvYmoKCjYgMCBvYmoKPDwKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL1R5cGUgL09ialN0bQovTiA0Ci9GaXJzdCAyMAovTGVuZ3RoIDIzMgo+PgpzdHJlYW0KeJxVj91qAjEQhe/zFPMCZRK7siuIoNIfKMWihRZKL+LusKRIUsxs0bfvTLpFvAhkzjkzfMeBhQlUFdxC3UAFk2kN87nB1/M3Ab74nrLBp9Bl+JCIhS18GlynITI4s1iYS3bt2R9Sb/6WwGn4OqGG2kfS7eLjlnIajq0saO4+iaOfRzr8EIfW39R21lSNrZuZ0I0n8X2z/6K2RHW8O/HDjj3TKKj2TF3wq3QSbqsdrQUnT+iXMSbWPqVJZKHRaTq2u0JWIIO7Yc9lVNEZXPlMBfXCKRCxTV2IPeBbiMuYw7+gF38B7fhizAplbmRzdHJlYW0KZW5kb2JqCgo3IDAgb2JqCjw8Ci9TaXplIDgKL1Jvb3QgMiAwIFIKL0ZpbHRlciAvRmxhdGVEZWNvZGUKL1R5cGUgL1hSZWYKL0xlbmd0aCAzOAovVyBbIDEgMiAyIF0KL0luZGV4IFsgMCA4IF0KPj4Kc3RyZWFtCnicFcSxDQAgCACwgomzi/+fw1koHYrutJliymmFQ6hfXh5UAAMWCmVuZHN0cmVhbQplbmRvYmoKCnN0YXJ0eHJlZgo1MzMKJSVFT0Y="
      }
    ],
    "recipients": [
      {
        "type": "SIGNER",
        "order": 1,
        "name": "Ana Souza",
        "email": "ana@example.com",
        "expectedCpf": "529.982.247-25"
      },
      {
        "type": "SIGNER",
        "order": 1,
        "name": "Bruno Lima",
        "email": "bruno@example.com"
      },
      {
        "type": "SIGNER",
        "order": 2,
        "name": "Testemunha 1",
        "email": "t1@example.com"
      },
      {
        "type": "CARBON_COPY",
        "order": 3,
        "name": "Imobiliária Sol",
        "email": "contratos@example.com"
      }
    ]
  }),
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 201: os campos
CampoTipoO que é
id *string (uuid)
numberstring | null
title *string
documentTypeDocumentType | null
status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
statusChangedAtstring | null (date-time)
test *booleanNasceu pela chave de teste: fora da cota, e o PDF concluído leva a tarja «TESTE — SEM VALIDADE».
createdAtstring (date-time)
updatedAtstring (date-time)Muda a cada passo que a API mostra, do envelope ou de um destinatário.
sentAtstring | null (date-time)
expiresAtstring | null (date-time)
completedAtstring | null (date-time)
voidedAtstring | null (date-time)
voidReasonstring | null
externalReference *string | null
onBehalfOf *OnBehalfOf | null
templateobject | null
documents *object[]
documents[].idstring (uuid)
documents[].namestring
documents[].sha256string
documents[].sizeBytesinteger
completedDocumentobject
completedDocument.availableboolean
certificateobject
certificate.availableboolean
recipients *Recipient[]
  • 201 O envelope nasceu (e, com `status sent`, saiu).
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
get/api/v1/envelopes/{id}escopo envelopes:read

Consultar um envelope

A fase, quando entrou nela, cada destinatário com a fase dele e a hora de cada passo. Há um limite de consultas ao MESMO envelope por minuto (limits → envelope_reads_per_minute no GET /me): para saber que mudou, use o webhook ou a lista com updatedSince. Não existe e é de outra organização são o mesmo 404. O HEAD não gasta a consulta.

ParâmetroOndeO que é
id *pathO `id` do envelope (nunca o `number`). O de outra organização responde o mesmo 404 do que não existe.

curl

curl -X GET "https://app.arcsphera.com/api/v1/envelopes/<id>" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes/<id>", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
id *string (uuid)
numberstring | null
title *string
documentTypeDocumentType | null
status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
statusChangedAtstring | null (date-time)
test *booleanNasceu pela chave de teste: fora da cota, e o PDF concluído leva a tarja «TESTE — SEM VALIDADE».
createdAtstring (date-time)
updatedAtstring (date-time)Muda a cada passo que a API mostra, do envelope ou de um destinatário.
sentAtstring | null (date-time)
expiresAtstring | null (date-time)
completedAtstring | null (date-time)
voidedAtstring | null (date-time)
voidReasonstring | null
externalReference *string | null
onBehalfOf *OnBehalfOf | null
templateobject | null
documents *object[]
documents[].idstring (uuid)
documents[].namestring
documents[].sha256string
documents[].sizeBytesinteger
completedDocumentobject
completedDocument.availableboolean
certificateobject
certificate.availableboolean
recipients *Recipient[]
  • 200 O envelope.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/envelopes/{id}/sendescopo envelopes:send

Enviar o rascunho

Envia o rascunho. Enviar o que já saiu responde o envelope, sem reenviar nem recontar — a nova tentativa depois da rede que caiu é segura. O corpo é vazio, ou {}. Com a chave de teste, o convite vai só às contas e aos contatos confirmados da sua organização (403 not_available_in_test para outro destinatário).

ParâmetroOndeO que é
id *pathO `id` do envelope (nunca o `number`). O de outra organização responde o mesmo 404 do que não existe.
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

O corpo

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/envelopes/<id>/send" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: sendEnvelope-exemplo-1" \
  -H "Content-Type: application/json" \
  -d '{}'

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes/<id>/send", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({}),
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
id *string (uuid)
numberstring | null
title *string
documentTypeDocumentType | null
status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
statusChangedAtstring | null (date-time)
test *booleanNasceu pela chave de teste: fora da cota, e o PDF concluído leva a tarja «TESTE — SEM VALIDADE».
createdAtstring (date-time)
updatedAtstring (date-time)Muda a cada passo que a API mostra, do envelope ou de um destinatário.
sentAtstring | null (date-time)
expiresAtstring | null (date-time)
completedAtstring | null (date-time)
voidedAtstring | null (date-time)
voidReasonstring | null
externalReference *string | null
onBehalfOf *OnBehalfOf | null
templateobject | null
documents *object[]
documents[].idstring (uuid)
documents[].namestring
documents[].sha256string
documents[].sizeBytesinteger
completedDocumentobject
completedDocument.availableboolean
certificateobject
certificate.availableboolean
recipients *Recipient[]
  • 200 O envelope, enviado.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/envelopes/{id}/voidescopo envelopes:void

Cancelar o envelope

Cancela o envelope em circulação (sent ou delivered), com o motivo. Quem já recebeu o convite recebe um e-mail com o motivo, dizendo que o link não abre mais — sem marcas, como o convite. Quem ainda não tinha recebido não recebe nada. O concluído não se cancela: assinado não se desfaz (409).

ParâmetroOndeO que é
id *pathO `id` do envelope (nunca o `number`). O de outra organização responde o mesmo 404 do que não existe.
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

O corpo

CampoTipoO que é
reason *stringVai no e-mail a quem já recebeu o convite, e no `voidReason` do envelope.

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/envelopes/<id>/void" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: voidEnvelope-exemplo-1" \
  -H "Content-Type: application/json" \
  -d '{
  "reason": "O locatário desistiu da locação."
}'

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes/<id>/void", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "reason": "O locatário desistiu da locação."
  }),
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
id *string (uuid)
numberstring | null
title *string
documentTypeDocumentType | null
status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
statusChangedAtstring | null (date-time)
test *booleanNasceu pela chave de teste: fora da cota, e o PDF concluído leva a tarja «TESTE — SEM VALIDADE».
createdAtstring (date-time)
updatedAtstring (date-time)Muda a cada passo que a API mostra, do envelope ou de um destinatário.
sentAtstring | null (date-time)
expiresAtstring | null (date-time)
completedAtstring | null (date-time)
voidedAtstring | null (date-time)
voidReasonstring | null
externalReference *string | null
onBehalfOf *OnBehalfOf | null
templateobject | null
documents *object[]
documents[].idstring (uuid)
documents[].namestring
documents[].sha256string
documents[].sizeBytesinteger
completedDocumentobject
completedDocument.availableboolean
certificateobject
certificate.availableboolean
recipients *Recipient[]
  • 200 O envelope, cancelado.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
get/api/v1/envelopes/{id}/documents/completedescopo envelopes:download

Baixar o PDF concluído

O PDF concluído e selado: os documentos e a folha de assinaturas, com o selo PAdES do e-CNPJ ICP-Brasil da AUDITCHAIN LTDA e o carimbo do tempo RFC 3161 (o carimbo não é ICP-Brasil). Só existe depois que todos assinam e o selo sai (409 antes). Guarde-o junto do certificado: o certificado é a prova de quem assinou. O HEAD diz o tamanho e o SHA-256 sem baixar, e não conta no limite de downloads.

ParâmetroOndeO que é
id *pathO `id` do envelope (nunca o `number`). O de outra organização responde o mesmo 404 do que não existe.

curl

curl -X GET "https://app.arcsphera.com/api/v1/envelopes/<id>/documents/completed" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes/<id>/documents/completed", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
  • 200 O PDF.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
get/api/v1/envelopes/{id}/documents/certificateescopo envelopes:download

Baixar o certificado de conclusão

O certificado de conclusão, selado: cada passo, com hora, IP e navegador, o resumo de cada arquivo e o aceite do meio eletrônico. Só existe depois do selo (409 antes). O HEAD não conta no limite.

ParâmetroOndeO que é
id *pathO `id` do envelope (nunca o `number`). O de outra organização responde o mesmo 404 do que não existe.

curl

curl -X GET "https://app.arcsphera.com/api/v1/envelopes/<id>/documents/certificate" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes/<id>/documents/certificate", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
  • 200 O PDF do certificado.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
get/api/v1/envelopes/{id}/documents/{docId}escopo envelopes:download

Baixar um documento original

O documento como foi enviado, pelo id que o envelope lista em documents. O HEAD não conta no limite.

ParâmetroOndeO que é
id *pathO `id` do envelope (nunca o `number`). O de outra organização responde o mesmo 404 do que não existe.
docId *pathO id de um item de `documents` do envelope.

curl

curl -X GET "https://app.arcsphera.com/api/v1/envelopes/<id>/documents/<docId>" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/envelopes/<id>/documents/<docId>", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
  • 200 O PDF original.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.

Modelos

Os Modelos publicados no ArcSign, com campos e papéis.

get/api/v1/templatesescopo templates:read

Listar os Modelos publicados

Os Modelos publicados, com a versão em uso, os campos (a key de cada {{campo}} do Word, que vai em fields, e o type dele — a forma do valor) e os papéis (o roleName que vai em templateRoles). O modelo com o Selo da organização aparece com supportedByApi: false: a API ainda não põe o selo.

curl

curl -X GET "https://app.arcsphera.com/api/v1/templates" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/templates", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
data *Template[]
  • 200 Os modelos.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.

Webhooks

Avisos ao seu sistema quando o envelope muda de fase.

get/api/v1/webhooksescopo webhooks:manage

Listar os webhooks

Os webhooks da organização e do ambiente da chave (a de teste vê só os de teste; no máximo limits → webhooks ao todo), sem o segredo.

curl

curl -X GET "https://app.arcsphera.com/api/v1/webhooks" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
data *Webhook[]
  • 200 A lista.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/webhooksescopo webhooks:manage

Cadastrar um webhook

O endereço HTTPS (porta 443, endereço público) que recebe os eventos marcados. A resposta traz o secret UMA vez: guarde-o cifrado no seu servidor — é com ele que você confere o ArcSign-Signature. A repetição com a mesma Idempotency-Key devolve o webhook com secret: null: perdeu o segredo, troque-o (rotate-secret). Endereço de rede interna (localhost, 10.x, 192.168.x, os metadados da nuvem) é recusado.

O webhook é do ambiente da chave que o cadastra: o da chave de teste (test: true) recebe só os eventos dos envelopes de teste, e só a chave de teste o vê; o da chave de produção, o contrário.

ParâmetroOndeO que é
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

O corpo

CampoTipoO que é
url *stringHTTPS, porta 443, endereço público.
events *EventType[]
descriptionstring | null

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/webhooks" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: createWebhook-exemplo-1" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://crm.example.com/webhooks/arcsign",
  "events": [
    "envelope.completed",
    "envelope.declined",
    "envelope.voided",
    "envelope.expired",
    "recipient.signed"
  ],
  "description": "CRM Exemplo — produção"
}'

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "url": "https://crm.example.com/webhooks/arcsign",
    "events": [
      "envelope.completed",
      "envelope.declined",
      "envelope.voided",
      "envelope.expired",
      "recipient.signed"
    ],
    "description": "CRM Exemplo — produção"
  }),
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 201: os campos
CampoTipoO que é
id *string
url *string (uri)
description *string | null
events *EventType[]
enabled *boolean
test *booleanO ambiente: true = recebe só os eventos dos envelopes de teste, e só a chave de teste o vê. Vem da chave que o cadastrou.
disabledAt *string | null (date-time)
disabledReason *string | nullPor que se desligou — «Falhou por N dia(s) seguido(s)…», com o último erro, ou «Desligado à mão».
failingSince *string | null (date-time)A primeira falha depois do último sucesso. Os dias de `webhook_retry_days` assim desligam o webhook.
lastSuccessAt *string | null (date-time)
lastAttemptAt *string | null (date-time)
secretRotatedAt *string | null (date-time)
previousSecretExpiresAt *string | null (date-time)Até quando o segredo anterior assina junto, depois de uma troca.
createdAt *string (date-time)
updatedAt *string (date-time)
secret *string | nullO segredo do HMAC, «whsec_» + 32 bytes em base64. Mostrado uma vez: nulo na repetição com a mesma Idempotency-Key.
  • 201 Cadastrado. O `secret` só aparece aqui.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
get/api/v1/webhooks/{id}escopo webhooks:manage

Ler um webhook

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.

curl

curl -X GET "https://app.arcsphera.com/api/v1/webhooks/<id>" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
id *string
url *string (uri)
description *string | null
events *EventType[]
enabled *boolean
test *booleanO ambiente: true = recebe só os eventos dos envelopes de teste, e só a chave de teste o vê. Vem da chave que o cadastrou.
disabledAt *string | null (date-time)
disabledReason *string | nullPor que se desligou — «Falhou por N dia(s) seguido(s)…», com o último erro, ou «Desligado à mão».
failingSince *string | null (date-time)A primeira falha depois do último sucesso. Os dias de `webhook_retry_days` assim desligam o webhook.
lastSuccessAt *string | null (date-time)
lastAttemptAt *string | null (date-time)
secretRotatedAt *string | null (date-time)
previousSecretExpiresAt *string | null (date-time)Até quando o segredo anterior assina junto, depois de uma troca.
createdAt *string (date-time)
updatedAt *string (date-time)
  • 200 O webhook.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
patch/api/v1/webhooks/{id}escopo webhooks:manage

Mudar um webhook

Mude o endereço, os eventos, a descrição ou o enabled. enabled: true RELIGA o webhook que se desligou depois de falhar pelos dias seguidos de limits → webhook_retry_days — os dias de falha recomeçam do zero; o que falhou, você reenvia pelo /deliveries/{deliveryId}/retry. enabled: false pausa.

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.

O corpo

CampoTipoO que é
urlstring
eventsEventType[]
descriptionstring | null
enabledboolean

curl

curl -X PATCH "https://app.arcsphera.com/api/v1/webhooks/<id>" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "enabled": true
}'

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "enabled": true
  }),
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
id *string
url *string (uri)
description *string | null
events *EventType[]
enabled *boolean
test *booleanO ambiente: true = recebe só os eventos dos envelopes de teste, e só a chave de teste o vê. Vem da chave que o cadastrou.
disabledAt *string | null (date-time)
disabledReason *string | nullPor que se desligou — «Falhou por N dia(s) seguido(s)…», com o último erro, ou «Desligado à mão».
failingSince *string | null (date-time)A primeira falha depois do último sucesso. Os dias de `webhook_retry_days` assim desligam o webhook.
lastSuccessAt *string | null (date-time)
lastAttemptAt *string | null (date-time)
secretRotatedAt *string | null (date-time)
previousSecretExpiresAt *string | null (date-time)Até quando o segredo anterior assina junto, depois de uma troca.
createdAt *string (date-time)
updatedAt *string (date-time)
  • 200 O webhook, como ficou.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
delete/api/v1/webhooks/{id}escopo webhooks:manage

Apagar um webhook

Os eventos deixam de sair para ele na hora, e o que estava na fila não sai mais.

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.

curl

curl -X DELETE "https://app.arcsphera.com/api/v1/webhooks/<id>" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
  • 204 Apagado.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/webhooks/{id}/rotate-secretescopo webhooks:manage

Trocar o segredo

O segredo novo vale já, e aparece só nesta resposta (a repetição com a mesma Idempotency-Key devolve secret: null). Por 24 horas o anterior assina JUNTO (dois v1 no ArcSign-Signature): troque no seu sistema nesse prazo, sem perder aviso. O corpo é vazio, ou {}.

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/webhooks/<id>/rotate-secret" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: rotateWebhookSecret-exemplo-1"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>/rotate-secret", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
secret *string | nullNulo na repetição com a mesma Idempotency-Key.
previousSecretExpiresAt *string (date-time)
webhook *Webhook
  • 200 O segredo novo.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/webhooks/{id}/testescopo webhooks:manage

Enviar um teste

Manda um evento webhook.test AGORA, só para este endereço, e devolve a entrega com o que o seu endereço respondeu. Não há nova tentativa. Vale para o webhook desligado — é como se confere antes de religar. O corpo do teste é o TestEvent, e não o de um envelope: o seu recebedor responde 2xx a ele também.

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/webhooks/<id>/test" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: testWebhook-exemplo-1"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>/test", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
delivery *Delivery
  • 200 A entrega do teste, com o resultado.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
get/api/v1/webhooks/{id}/deliveriesescopo webhooks:manage

A fila de entrega

Cada evento que saiu (ou tenta sair) para este webhook, da mais nova para a mais velha: a situação, as tentativas, o último status e o erro. Cada entrega fica 30 dias. O parâmetro errado é 422, com errors[].pointer /query/<nome>.

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.
statusquery
limitquery
cursorqueryO `nextCursor` da página anterior.

curl

curl -X GET "https://app.arcsphera.com/api/v1/webhooks/<id>/deliveries" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>/deliveries", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
data *Delivery[]
nextCursor *string | null
  • 200 Uma página da fila.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.
post/api/v1/webhooks/{id}/deliveries/{deliveryId}/retryescopo webhooks:manage

Reenviar agora

Uma tentativa AGORA, com o MESMO corpo e o mesmo id de evento — o seu sistema descarta o repetido. Vale para a entrega que falhou e para a que já foi. No webhook desligado, 409 webhook_disabled: religue antes — e a nova tentativa com a mesma Idempotency-Key, depois de religar, reenvia (o 409 que passa não se guarda). O corpo é vazio, ou {}.

ParâmetroOndeO que é
id *pathO `id` do webhook. O de outro ambiente ou de outra organização responde o mesmo 404 do que não existe.
deliveryId *pathO `id` da entrega, como a fila a lista (e o cabeçalho `ArcSign-Delivery-Id` o traz).
Idempotency-Key *headerObrigatória em TODO POST. Escolhida por você, estável por registro (o id do contrato + a versão): de 1 a 255 caracteres ASCII visíveis, sem espaço. A mesma chave no mesmo endereço com o mesmo corpo devolve a resposta guardada, sem fazer nada de novo, com o cabeçalho `Idempotent-Replayed: true` (e o `Location`, no 201); com outro corpo ou noutro endereço, 422; com o primeiro pedido ainda rodando, 409. Guardada por chave de API, pelas horas de `limits` → `idempotency_key_hours`. A recusa de validação (o 400, o 422 que aponta campos) e o conflito que passa (`delivery_in_progress`, `webhook_disabled`) não se guardam: corrija e mande de novo com a mesma chave.

curl

# A Idempotency-Key é estável por registro: a nova tentativa usa a MESMA; um pedido novo, outra.
curl -X POST "https://app.arcsphera.com/api/v1/webhooks/<id>/deliveries/<deliveryId>/retry" \
  -H "Authorization: Bearer $ARCSIGN_API_KEY" \
  -H "Idempotency-Key: retryWebhookDelivery-exemplo-1"

Node

// Node 18 ou mais, como módulo ES (o await no topo): salve como exemplo.mjs e rode node exemplo.mjs.
import { randomUUID } from "node:crypto";

const idempotencyKey = randomUUID(); // guarde junto do seu registro

const resposta = await fetch("https://app.arcsphera.com/api/v1/webhooks/<id>/deliveries/<deliveryId>/retry", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ARCSIGN_API_KEY}`,
    // Estável por registro: a nova tentativa usa a MESMA chave e o MESMO corpo.
    "Idempotency-Key": idempotencyKey,
  },
});

if (!resposta.ok) {
  // RFC 9457: decida pelo `code`, mostre o `detail`, guarde o `requestId`.
  const problema = await resposta.json();
  throw new Error(`${problema.code}: ${problema.detail} (${problema.requestId})`);
}
const dados = await resposta.json();
A resposta 200: os campos
CampoTipoO que é
delivery *Delivery
  • 200 A entrega, com o resultado desta tentativa.
  • 400 Corpo que não é JSON (`malformed_request`), `Idempotency-Key` faltando ou inválida, ou a chave na URL (`api_key_in_url`).
  • 401 Sem chave, chave desconhecida, revogada ou vencida — sempre a mesma resposta, para não dizer qual dos casos é.
  • 403 A chave existe, mas não tem o escopo; ou o dono perdeu a permissão ou foi desativado; ou o IP não está na lista; ou a organização não contratou a ArcSign API; ou a chave de teste pediu o que o teste não faz (`not_available_in_test`).
  • 404 Não existe, ou é de outra organização — as duas respostas são iguais.
  • 409 A fase não permite (`envelope_not_in_circulation`, `envelope_not_draft`, `document_not_ready`), o webhook já existe ou está desligado, ou o primeiro pedido com esta `Idempotency-Key` ainda está rodando — esse, e a entrega que está saindo, voltam com `Retry-After`.
  • 413 O pedido passa do tamanho máximo (`payload_too_large`) — 25 MB no envelope, 16 KB no webhook; `limitBytes` diz o número.
  • 422 Campos a corrigir (`invalid_body`, e `errors[]` aponta cada um — no corpo, `/campo`; na URL, `/query/<nome>`), a `Idempotency-Key` já usada noutro pedido, ou a regra de negócio recusou (o `detail` diz por quê).
  • 423 A licença está só de leitura (`license_read_only`) — a API lê, e não grava.
  • 429 Um limite foi atingido. `Retry-After` diz quando voltar, e o corpo diz o número que valeu (`limit`, `window`, `retryAfter`; nos envelopes de teste, `resetsAt`).
  • 500 O erro que ninguém previu (`internal_error`). Nada de dentro vai no corpo; o `requestId` liga o pedido à linha do nosso log — mande-o ao suporte se repetir.
  • 503 O banco está só de leitura (a máquina cópia), o antivírus está fora do ar, a instalação não tem a configuração da API ou a do webhook.

Os avisos (webhooks)

O ArcSign avisa o seu endereço HTTPS quando o envelope muda de fase. O aviso é magro — o id do evento, o tipo, a hora e a fase, sem nome, e-mail nem CPF: busque o resto pelo GET do envelope. Só 2xx conta; redirecionamento é falha; as novas tentativas seguem por 3 dias, e o webhook que falha o tempo todo se desliga e avisa. O aviso pode chegar duas vezes e fora de ordem.

Confira a assinatura de cada aviso, e use um dos dois cabeçalhos: o ArcSign-Signature (t=<unix>,v1=<hex>, HMAC-SHA256 com o segredo whsec_… inteiro, sobre "{t}.{corpo bruto}"), ou o webhook-signature do padrão Standard Webhooks, que as bibliotecas prontas conferem sozinhas. Recuse o aviso com mais de 5 minutos — a tolerância do Standard Webhooks: quem confere a hora é o seu sistema. O «Enviar um teste» manda o webhook.test, sem envelope: responda 2xx a ele também.

Node — conferir o ArcSign-Signature

import { createHmac, timingSafeEqual } from "node:crypto";

// O corpo BRUTO, como chegou — conferir depois de JSON.parse não bate.
export function avisoDoArcSign(corpoBruto, cabecalho, segredo, agora = Date.now()) {
  const partes = cabecalho.split(",").map((p) => p.trim().split("="));
  const t = Number(partes.find(([k]) => k === "t")?.[1]);
  const assinaturas = partes.filter(([k]) => k === "v1").map(([, v]) => v);
  if (!Number.isFinite(t) || Math.abs(agora / 1000 - t) > 5 * 60) return null; // mais de 5 minutos: recuse
  const esperada = createHmac("sha256", segredo).update(`${t}.${corpoBruto}`).digest("hex");
  // Na troca do segredo vêm DOIS v1: vale se qualquer um conferir.
  // Compare os BYTES: com o tamanho do texto, um v1 com caractere acentuado lança no timingSafeEqual.
  const b = Buffer.from(esperada);
  const confere = assinaturas.some((v) => {
    const a = Buffer.from(v);
    return a.length === b.length && timingSafeEqual(a, b);
  });
  return confere ? JSON.parse(corpoBruto) : null;
}

// Depois de conferir: descarte o id de evento já processado, e ignore o
// evento mais velho que o último (compare data.envelope.statusChangedAt).

envelope.sent

Envelope enviado

O envelope saiu (Rascunho → Enviado).

O ArcSign chama o seu endereço com este corpo. COMO RECEBER: responda 2xx em até 10 s — só 2xx conta, e redirecionamento é falha. Confira o ArcSign-Signature e recuse o t com mais de 5 minutos (a tolerância do Standard Webhooks). O aviso pode chegar mais de uma vez (descarte pelo id) e fora de ordem (compare data.envelope.statusChangedAt). A falha volta em 1 min, 5 min, 30 min, 2 h, 6 h e de 24 em 24 h, pelos dias de limits → webhook_retry_days; falhar todos esses dias seguidos desliga o webhook, e avisamos quem o cadastrou.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "envelope.sent",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "sent",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": null
  }
}

envelope.delivered

Envelope aberto

A primeira pessoa abriu o envelope, com o código do e-mail conferido (Enviado → Entregue).

COMO RECEBER: o mesmo do envelope.sent — 2xx em até 10 s, a assinatura conferida, o t de até 5 minutos, o repetido descartado pelo id e o fora de ordem por data.envelope.statusChangedAt.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "envelope.delivered",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "delivered",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": null
  }
}

recipient.signed

Alguém assinou

Um Signatário assinou — ou o selo da organização assinou na vez dele (recipient.type SEAL). data.recipient diz qual destinatário, sem nome nem e-mail.

COMO RECEBER: o mesmo do envelope.sent — 2xx em até 10 s, a assinatura conferida, o t de até 5 minutos, o repetido descartado pelo id e o fora de ordem por data.envelope.statusChangedAt. O último Signatário manda este E o envelope.completed, com a mesma hora: ordene os recipient.* pelo timestamp.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "recipient.signed",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "delivered",
      "statusChangedAt": "2026-10-21T17:58:41.902Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": {
      "id": "2d8f4b6a-1c3e-4f5a-9b7d-0e2c4a6f8b13",
      "type": "SIGNER",
      "order": 1,
      "status": "signed"
    }
  }
}

recipient.declined

Alguém recusou

Um Signatário recusou. Chega junto do envelope.declined, que para o envelope para todos.

COMO RECEBER: o mesmo do envelope.sent.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "recipient.declined",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "declined",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": {
      "id": "2d8f4b6a-1c3e-4f5a-9b7d-0e2c4a6f8b13",
      "type": "SIGNER",
      "order": 1,
      "status": "declined"
    }
  }
}

envelope.completed

Envelope concluído

O último Signatário assinou. O PDF selado pode levar alguns segundos: se o download responder 409 document_not_ready, tente de novo em instantes.

COMO RECEBER: o mesmo do envelope.sent.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "envelope.completed",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "completed",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": null
  }
}

envelope.declined

Envelope recusado

A recusa de alguém parou o envelope para todos.

COMO RECEBER: o mesmo do envelope.sent.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "envelope.declined",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "declined",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": null
  }
}

envelope.voided

Envelope cancelado

Quem enviou cancelou o envelope em circulação, com o motivo (o GET do envelope traz o voidReason). Quem já tinha recebido o convite recebe o e-mail do cancelamento.

COMO RECEBER: o mesmo do envelope.sent.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "envelope.voided",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "voided",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": null
  }
}

envelope.expired

Envelope expirado

O prazo para assinar venceu.

COMO RECEBER: o mesmo do envelope.sent.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *stringO id do evento. Descarte o repetido por ele.
type *EventTypeOs eventos que se marcam no webhook. O `webhook.test` não se marca — só o «Enviar um teste» o manda.envelope.sent · envelope.delivered · recipient.signed · recipient.declined · envelope.completed · envelope.declined · envelope.voided · envelope.expired
timestamp *string (date-time)Quando o evento aconteceu.
data *object
data.envelope *object
data.envelope.id *string
data.envelope.status *EnvelopeStatusdraft (rascunho), sent (enviado), delivered (alguém abriu), completed (todos assinaram), declined (alguém recusou), voided (cancelado), expired (venceu).draft · sent · delivered · completed · declined · voided · expired
data.envelope.statusChangedAt *string (date-time)Para ordenar os envelope.*: ignore o aviso mais velho que o último que você processou. Os recipient.* se ordenam pelo `timestamp`.
data.envelope.externalReference *string | null
data.envelope.onBehalfOf *object | null
data.envelope.onBehalfOf.idstring | null
data.envelope.test *booleanO envelope nasceu pela chave de teste.
data.recipient *object | nullSó nos recipient.*.
data.recipient.idstring
data.recipient.typeRecipientTypeSIGNER (assina), CARBON_COPY (recebe cópia), SEAL (o selo da organização — a API ainda não o põe).SIGNER · CARBON_COPY · SEAL
data.recipient.orderinteger
data.recipient.statusRecipientStatuscreated (ainda não é a vez), sent (o convite saiu), delivered (abriu), signed (assinou), declined (recusou).created · sent · delivered · signed · declined

O corpo

{
  "id": "evt_4f1c2b9a7e3d5c0a8b6f2e1d",
  "type": "envelope.expired",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "envelope": {
      "id": "9b2e6c1d-4a7f-4e3b-8c5d-2f1a0e9b7c64",
      "status": "expired",
      "statusChangedAt": "2026-10-21T18:04:05.123Z",
      "externalReference": "crm:contrato:3f9a1c2e-7b4d-4e8a-9c1f-5d2b6a0e8f73",
      "onBehalfOf": {
        "id": "7c3e9a1b-2d4f-4a6b-8e0c-1f5d3b7a9e21"
      },
      "test": false
    },
    "recipient": null
  }
}

webhook.test

O teste do webhook

Só o «Enviar um teste» (POST /webhooks/{id}/test, ou o botão do painel) manda este: confere o endereço e a assinatura. Não se marca nos events do webhook, não tem nova tentativa e não conta para o desligamento. O corpo NÃO traz envelope — o seu recebedor responde 2xx a ele também.

Os cabeçalhos e a assinatura são os mesmos dos outros eventos.

  • ArcSign-Signature — t=<unix>,v1=<hex>: HMAC-SHA256 de "{t}.{corpo bruto}" com o segredo INTEIRO (whsec_…) como chave. Nas 24 h depois de trocar o segredo vêm dois v1: vale se qualquer um conferir. Recuse o t com mais de 5 minutos.
  • ArcSign-Event-Id — O id do evento, o mesmo do `id` do corpo. Descarte o repetido por ele.
  • ArcSign-Event-Type — O tipo do evento, o mesmo do `type` do corpo.
  • ArcSign-Delivery-Id — A entrega — é o `deliveryId` do reenvio pela API.
  • ArcSign-Delivery-Attempt — 1 na primeira tentativa, 2 na segunda…
  • webhook-id — Standard Webhooks: o id do evento.
  • webhook-timestamp — Standard Webhooks: o mesmo t, em segundos Unix.
  • webhook-signature — Standard Webhooks: v1,<base64> — HMAC-SHA256 de "{id}.{t}.{corpo}" com os bytes depois de «whsec_» (base64). Quem usa a biblioteca do padrão passa o segredo como está. Use UM dos dois cabeçalhos de assinatura.
CampoTipoO que é
id *string
type *stringwebhook.test
timestamp *string (date-time)
data *object
data.webhook *object
data.webhook.id *stringO webhook que mandou o teste.

O corpo

{
  "id": "evt_9a2c4e6b8d0f1a3c5e7b9d1f",
  "type": "webhook.test",
  "timestamp": "2026-10-21T18:04:05.123Z",
  "data": {
    "webhook": {
      "id": "3e5a7c9b-1d2f-4a6c-8e0b-2d4f6a8c0e13"
    }
  }
}

O que o seu sistema constrói

A lista do que funciona do lado de quem integra. É também o que a Arcsphera confere antes de liberar a produção — em especial os grupos 3 e 4, que, mal feitos, mandam contrato em dobro ou dão por assinado o que não foi.

1. Antes da primeira linha de código

  • Decidir de onde sai o PDF: gerado pelo seu sistema, ou um Modelo publicado no ArcSign. — A API recebe o PDF pronto, ou o modelo com os campos e os papéis.
  • Mapear as fases do seu registro às do envelope — em especial uma fase «aguardando assinatura». — Sem a fase de espera, o contrato fica «rascunho» enquanto circula.
  • Saber quem assina e quem só recebe cópia, com o e-mail de cada um (e o CPF, se tiver). — O destinatário sem e-mail é recusado; o CPF esperado é opcional.
  • Guardar, no seu registro, o id do envelope. — É por ele que você consulta, baixa e cancela.

2. A chave

  • Guardar a chave cifrada no servidor — nunca no código, no navegador ou na URL. — A chave vazada manda e-mail em nome da sua empresa.
  • Mandar a chave só no cabeçalho Authorization: Bearer. — A chave na URL é recusada (400), porque a URL vai para o log.
  • Uma chave por sistema, com o escopo mínimo. — Revogar uma não derruba as outras.
  • Saber trocar a chave sem parar: criar a nova, trocar no sistema, revogar a velha. — A chave vence ou vaza; o aviso chega 15 dias antes do vencimento.
  • Se a chave vazou (num commit, num log, num print): POST /api/v1/me/revoke, chamado com ela mesma. — Ela para na chamada seguinte, e todos os contatos da sua empresa recebem o aviso. Uma chave nunca revoga nem cria outra.
  • Chamar GET /api/v1/me na subida do seu sistema. — Confirma a chave, os escopos e os limites antes do primeiro envio.

3. Enviar

  • Idempotency-Key em TODO POST, estável por registro (o id do contrato + a versão). — A rede cai depois do envio; a nova tentativa com a mesma chave devolve o mesmo envelope em vez de mandar dois.
  • Na nova tentativa, a MESMA chave e o MESMO corpo; corpo novo pede chave nova. — A mesma chave com outro corpo é 422.
  • externalReference com o id do seu registro. — É como você acha o envelope sem guardar mais nada.
  • onBehalfOf com o seu cliente. — Quem assina lê os dois nomes — o do seu cliente e o da sua empresa.
  • Tratar o erro pelo code, e não pela frase; mostrar o detail ao seu usuário e guardar o requestId. — A frase pode mudar; o code, não. O requestId é o que o suporte pede.

4. Saber que assinou

  • Um endereço HTTPS que recebe o aviso e responde 2xx rápido. — Só 2xx conta; redirecionamento é falha; 3 dias falhando desligam o webhook.
  • Conferir a assinatura do aviso e recusar o que tiver mais de 5 minutos. — Sem isso, qualquer um finge que o contrato foi assinado.
  • Descartar o aviso repetido pelo id do evento, e ignorar o mais velho que o último já processado. — O aviso pode chegar duas vezes e fora de ordem.
  • Buscar os dados pelo GET do envelope depois do aviso. — O aviso é magro: não traz nome, e-mail nem CPF.
  • Uma conferência de reserva: GET /api/v1/envelopes?updatedSince=… de tempos em tempos, pedindo desde o último updatedAt visto MENOS 5 minutos e descartando o repetido. — Se o aviso não chegar, nada fica parado; e a mudança gravada numa transação longa pode aparecer com a hora de um pouco antes.
  • Não consultar o mesmo envelope mais de 1 vez por minuto, e respeitar o 429 e o Retry-After. — Consultar em ciclo é o que o limite recusa.

5. Depois de assinado, e o cancelamento

  • Baixar o PDF concluído E o certificado de conclusão, e guardar os dois. — O certificado é a prova de quem assinou; o PDF sozinho não conta a história.
  • Cancelar pela API com o motivo, só enquanto o envelope circula. — Cancelar o concluído é 409: assinado não se desfaz.
  • Não prometer aos seus clientes mais do que a seção «O que a assinatura é» permite. — A sua documentação não pode dizer mais do que o certificado diz.

6. Testar e ir para produção

  • Testar o caminho inteiro com a chave de teste: enviar, assinar pelo próprio celular, receber o aviso, baixar. — Sem custo, com a tarja «TESTE — SEM VALIDADE».
  • Testar as falhas: a rede que cai no envio (repetir com a mesma Idempotency-Key), o aviso repetido, o 429. — É onde a integração ingênua manda dois contratos.
  • Pedir a produção no painel, aceitando os termos de uso e o acordo de dados. — A chave arcs_live_ só nasce depois da liberação pela Arcsphera.

O que a assinatura do ArcSign é — e o que não é

A API herda as frases do certificado de conclusão, e a sua documentação não pode dizer aos seus clientes mais do que ele diz.

  • Assinatura eletrônica, válida entre as partes que a admitem (MP 2.200-2/2001, art. 10, § 2º).
  • O PDF concluído é selado em PAdES com o e-CNPJ ICP-Brasil da AUDITCHAIN LTDA e o carimbo do tempo RFC 3161 (o carimbo não é ICP-Brasil); o validar.iti.gov.br mostra o selo.
  • O certificado de conclusão registra cada passo, o IP, o navegador, o resumo do arquivo e o aceite.
  • O código por e-mail prova o acesso à caixa de e-mail; o CPF é conferido pelos dígitos, ou na Receita quando pedido.
  • Não é assinatura «avançada» nem «qualificada» das pessoas.
  • Não «vale como a manuscrita» — essa frase saiu do aceite, e a redação nova espera o advogado.
  • Não serve aos atos em que a lei exige a assinatura qualificada.
  • Não tem validação de longo prazo (LTV), e não promete que o PDF «abre verde» em qualquer leitor.
  • Não tem SMS, WhatsApp, selfie, biometria nem Pix.
  • Não diz que o contrato assinado é título executivo sem testemunhas: isso espera o advogado (CPC, art. 784, § 4º).