Português · Read in English
SDK Ruby para a API Assinafy v1 — plataforma brasileira de assinatura eletrônica de documentos.
O SDK expõe todas as operações da API Assinafy v1, incluindo o fluxo OAuth 2.1, e o ciclo de
vida completo de templates suportado. O spec/api_coverage_spec.rb,
versionado no repositório, valida que cada rota mapeia de forma única para um método público do
SDK — se uma operação deixar de ter cobertura, a suíte falha.
Este documento acompanha uma integração do início ao fim. Para consulta rápida por recurso, veja README.en.md; para consulta por operação, docs/API_REFERENCE.md.
- Requisitos e instalação
- Autenticação
- Início rápido
- O fluxo completo, passo a passo
- OAuth 2.1
- Métodos de verificação do signatário
- Templates, campos e tags
- Webhooks
- Paginação
- Erros
- Artefatos e verificação pública
- Recursos do cliente
- Ambientes
- Assinaturas RBS
- Ruby 3.2+ (suporte mantido: 3.3+; 3.2 é compatibilidade legada/EOL)
- Bundler
- TLS 1.2 ou superior (o SDK recusa TLS 1.0 e 1.1)
Do RubyGems.org:
# Gemfile
gem 'assinafy'bundle installDo GitHub Packages (mirror):
source 'https://rubygems.pkg.github.com/assinafy' do
gem 'assinafy'
endVocê vai precisar de um personal access token com escopo read:packages:
bundle config https://rubygems.pkg.github.com/assinafy USUARIO:TOKENA Assinafy aceita três credenciais. Escolha pela pergunta "quem está agindo?".
| Credencial | Quem age | Quando usar |
|---|---|---|
Chave de API (api_key:) |
a própria workspace | integrações de back-end. Permanente. Enviada como X-Api-Key. |
Token de sessão (token:) |
o usuário que fez login | depois de client.auth.login. JWT, expira em ~1 hora. |
OAuth 2.1 (token:) |
um aplicativo, em nome de um usuário | apps de marketplace, integrações de terceiros, assistentes de IA. Veja a seção 5. |
require 'assinafy'
client = Assinafy::Client.new(
api_key: ENV.fetch('ASSINAFY_API_KEY'),
account_id: ENV.fetch('ASSINAFY_ACCOUNT_ID'),
base_url: 'https://api.assinafy.com.br/v1', # padrão
webhook_secret: ENV['ASSINAFY_WEBHOOK_SECRET'],
timeout: 30,
logger: Logger.new($stdout)
)- Configure exatamente uma credencial. Se
api_key:etoken:forem informados, o SDK envia apenasX-Api-Key. - Um cliente também pode ser criado sem credenciais, para login, OAuth, e endpoints públicos/de signatário.
base_url:precisa ser uma URLhttp/httpsabsoluta. Qualquer outra coisa — um host sem esquema, um caminho relativo, outro esquema — levantaAssinafy::ValidationErrorem vez de anexar suas credenciais a ela. Barra ao final é removida.- Métodos com escopo de conta aceitam uma sobrescrita por chamada, para tenants com múltiplos workspaces.
- As requisições enviam
User-Agent: Assinafy-Ruby-SDK/v<Assinafy::VERSION>.
Client.from_config(hash) aceita hashes com chaves string ou símbolo (por exemplo, YAML já
interpretado).
client.auth.create_api_key(password: 'senha-atual') # => { 'api_key' => '...' } — exibida uma única vez
client.auth.get_api_key # => { 'api_key' => '****...9Jdr' } — mascarada
client.auth.delete_api_key # => nilGerar uma chave nova invalida a anterior. Nunca exponha a chave em um front-end.
O caminho mais curto do PDF até o pedido de assinatura:
resultado = client.upload_and_request_signatures(
source: './contrato.pdf',
signers: [{ full_name: 'Ana Silva', email: 'ana@exemplo.com.br' }],
message: 'Por favor, assine o contrato em anexo.'
)
resultado[:document]['id'] # => "1032009d72b364f377ff270405cc"
resultado[:assignment]['id'] # => "19e99aa0633e32ac13f845c08db"
resultado[:signer_ids] # => ["19e6b92e7895332ed9708535d8c"]Esse helper faz upload, espera o processamento, cria os signatários e abre um assignment
virtual. Ele valida o payload inteiro antes de enviar qualquer coisa, então um
expires_at malformado não deixa um documento órfão para trás.
Não é transacional. Se uma chamada posterior falhar, o documento enviado e os signatários já criados continuam existindo. Em caso de erro,
e.context[:document]ee.context[:signer_ids]trazem o que foi criado, para você limpar.
A seção seguinte abre esse helper passo a passo — use-a quando precisar de controle sobre qualquer etapa.
documento = client.documents.upload('./contrato.pdf', name: 'contrato-acme.pdf')
documento['id'] # => "1032009d72b364f377ff270405cc"
documento['status'] # => "uploaded"Aceita um caminho, ou um Hash com :file_path, ou :buffer + :file_name para bytes em
memória:
client.documents.upload(buffer: pdf_bytes, file_name: 'contrato.pdf')Somente PDF, no máximo 25 MB. O SDK confere a extensão, o tamanho e o cabeçalho %PDF- antes de
enviar.
A Assinafy extrai páginas e metadados de forma assíncrona. Só é possível abrir um assignment depois disso:
documento = client.documents.wait_until_ready(
documento['id'],
max_wait_seconds: 30,
poll_interval_seconds: 2
)
documento['status'] # => "metadata_ready"
documento['pages'] # => [{ 'id' => '...', 'number' => 1, 'height' => 1651, 'width' => 1275 }]Erros de rede durante a espera são tolerados e reprocessados; um status terminal
(failed, expired, rejected_by_*) interrompe imediatamente com Assinafy::Error.
signatario = client.signers.create(
full_name: 'Ana Silva',
email: 'ana@exemplo.com.br',
whatsapp_phone_number: '+5511999999999', # obrigatório para verificação por WhatsApp
government_id: '00000000000' # obrigatório para certificado digital
)
signatario['id'] # => "19e6b92e7895332ed9708535d8c"Signatários pertencem à conta e podem ser reaproveitados entre documentos:
existente = client.signers.find_by_email('ana@exemplo.com.br') # paginação percorrida pelo SDK
signatario = existente || client.signers.create(full_name: 'Ana Silva', email: 'ana@exemplo.com.br')Antes de enviar — principalmente com WhatsApp ou certificado digital, que são cobrados:
client.assignments.estimate_cost(
documento['id'],
signers: [{ verification_method: 'DigitalCertificate' }]
)Aqui os signatários podem ser descritos só pelo método, sem id.
Um assignment é o convite para assinar um documento. Dois métodos:
virtual — sem campos posicionados; o signatário aceita o documento inteiro.
assignment = client.assignments.create(
documento['id'],
method: 'virtual',
signers: [
{ id: signatario['id'], verification_method: 'Email', notification_methods: ['Email'], step: 1 }
],
message: 'Por favor, assine o contrato em anexo.',
expires_at: '2026-12-31T23:59:00Z',
copy_receivers: [] # IDs que só recebem cópia
)
assignment['signing_urls']
# => [{ 'signer_id' => '19e6b...', 'url' => 'https://.../sign/...' }]step define a ordem: todos do passo 1 assinam antes do passo 2. Omita para assinatura
simultânea.
collect — campos posicionados página a página.
client.assignments.create(
documento['id'],
method: 'collect',
signers: [{ id: signatario['id'] }],
entries: [{
page_id: documento['pages'].first['id'],
fields: [{
signer_id: signatario['id'],
field_id: campo['id'],
display_settings: { left: 100, top: 100, width: 240, height: 48, fontSize: 16 }
}]
}]
)O SDK aceita signers: ['id1', 'id2'] (IDs puros), signers: [{ id: ... }] (descritores
completos) e o formato legado signer_ids:. Tudo é normalizado para o corpo que a API espera.
Essas chamadas usam o código de acesso do signatário, não suas credenciais de workspace — o
SDK remove X-Api-Key/Authorization delas automaticamente. Um cliente sem credenciais basta:
signatario_client = Assinafy::Client.new
codigo = 'codigo-de-acesso-do-link-de-assinatura'
# 1. Carregar o documento a assinar
doc = signatario_client.assignments.signer_document(
signer_access_code: codigo,
has_accepted_terms: true
)
# 2. Aceitar os termos e confirmar a identidade
signatario_client.signers.accept_terms(signer_access_code: codigo)
signatario_client.signers.confirm_data(
doc['id'], { full_name: 'Ana Silva', government_id: '00000000000' },
signer_access_code: codigo
)
# 3. Verificar o código de uso único (OTP) recebido por e-mail ou WhatsApp
signatario_client.signers.verify_email(verification_code: '123456', signer_access_code: codigo)
# 4. Enviar a imagem da assinatura
signatario_client.signers.upload_signature(
File.binread('assinatura.png'), signer_access_code: codigo, type: 'signature'
)Para um assignment virtual:
signatario_client.signer_documents.sign_multiple([doc['id']], signer_access_code: codigo)Para um assignment collect, envie cada item posicionado:
itens = doc.fetch('assignment').fetch('items').map do |item|
{
item_id: item.fetch('id'),
field_id: item.dig('field', 'id'),
page_id: item.dig('page', 'id'),
value: 'Aceito'
}
end
signatario_client.assignments.sign(
doc['id'], doc.dig('assignment', 'id'), itens, signer_access_code: codigo
)Para recusar:
signatario_client.assignments.decline(
doc['id'], doc.dig('assignment', 'id'),
decline_reason: 'Valores divergentes da proposta',
signer_access_code: codigo
)client.documents.signing_progress(documento['id'])
# => { signed: 1, total: 3, pending: 2, percentage: 33.33 }
client.documents.fully_signed?(documento['id']) # => false
client.documents.activities(documento['id']) # trilha completa de eventosReenviar a notificação de um signatário (também é cobrado — estime antes):
client.assignments.estimate_resend_cost(documento['id'], assignment['id'], signatario['id'])
client.assignments.resend_notification(documento['id'], assignment['id'], signatario['id'])Estender o prazo:
client.assignments.reset_expiration(documento['id'], assignment['id'], '2027-01-31T23:59:00Z')Para acompanhar sem polling, use webhooks.
File.binwrite('assinado.pdf', client.documents.download(documento['id'], 'certificated'))
File.binwrite('original.pdf', client.documents.download(documento['id'], 'original'))
File.binwrite('pacote.zip', client.documents.download(documento['id'], 'bundle'))
client.documents.verify('hash-da-assinatura') # verificação pública, sem autenticaçãoclient.documents.rename(documento['id'], 'Contrato Acme — assinado')
client.documents.append_tags(documento['id'], [tag['id']])
client.documents.delete(documento['id'])
client.signers.delete(signatario['id'])Apague só depois de concluir o que depende do recurso. Alguns respondem 409 enquanto ainda
estão em processamento ou referenciados.
Use OAuth quando um aplicativo age em nome de um usuário, com a permissão dele — apps de marketplace, integrações de terceiros, assistentes de IA. Diferente da chave de API, o token vale para uma workspace e carrega apenas os escopos que o usuário aprovou.
Fluxo authorization code com PKCE obrigatório (o servidor aceita apenas S256).
| Escopo | Concede |
|---|---|
documents:read |
ler documentos, páginas, tags, signatários, assignments e atividades |
documents:write |
criar, alterar e excluir documentos e gerenciar seus signatários e assignments |
templates:read |
ler templates, páginas, papéis, campos e tags |
templates:write |
criar, alterar e excluir templates |
account:read |
ler perfil, tema e logo da workspace |
webhooks:write |
configurar e desativar a assinatura de webhooks da workspace |
openid |
identificar o usuário (claim sub) e habilitar /oauth/userinfo |
profile |
incluir o nome do usuário nas claims |
email |
incluir o e-mail e seu status de verificação nas claims |
offline_access |
receber um refresh token |
Um token OAuth nunca alcança faturamento, ciclo de vida da conta, gerenciamento de credenciais ou superfícies administrativas — independentemente do escopo.
verificador = Assinafy::OAuth.generate_code_verifier
state = Assinafy::OAuth.generate_state
session[:assinafy_code_verifier] = verificador # guarde os três na sessão
session[:assinafy_state] = state
session[:assinafy_issuer] = Assinafy::OAuth::AUTHORIZATION_SERVER # emissor do servidor usado nesta tentativa
redirect_to Assinafy::OAuth.authorization_url(
client_id: ENV.fetch('ASSINAFY_CLIENT_ID'),
redirect_uri: 'https://app.exemplo.com.br/oauth/callback',
code_verifier: verificador,
scope: %w[documents:read documents:write offline_access],
state: state
)O code_challenge é derivado do verificador (S256) — o verificador em si nunca vai para a URL.
Assinafy::OAuth::AUTHORIZATION_SERVER é o emissor de produção. O sandbox tem o seu: passe
authorization_endpoint: 'https://auth-sandbox.assinafy.com.br/oauth/authorize' e guarde
https://auth-sandbox.assinafy.com.br.
unless params[:state] == session.delete(:assinafy_state) &&
params[:iss] == session.delete(:assinafy_issuer)
raise 'resposta de autorização inválida'
end
raise "autorização não concedida: #{params[:error]}" if params[:error] # access_denied, invalid_scope, ...
tokens = Assinafy::Client.new.oauth.exchange_code(
code: params.fetch(:code),
client_id: ENV.fetch('ASSINAFY_CLIENT_ID'),
code_verifier: session.delete(:assinafy_code_verifier),
redirect_uri: 'https://app.exemplo.com.br/oauth/callback'
)
tokens['access_token'] # => "..."
tokens['expires_in'] # => 3600
tokens['refresh_token'] # => presente apenas com offline_access
tokens['scope'] # => "documents:read documents:write"Confira state e iss contra os valores guardados nesta tentativa antes de qualquer outra
coisa, inclusive num retorno com error= — é a proteção contra CSRF e contra respostas que não
são suas. O código vale uma vez e expira 60
segundos depois da aprovação: troque-o na hora, sem repetir.
access_token = tokens.fetch('access_token')
# O token só vale na workspace que o usuário escolheu; guarde o id junto dos tokens.
workspace_id = Assinafy::Client.new(token: access_token).accounts.list[:data].first.fetch('id')
usuario = Assinafy::Client.new(token: access_token, account_id: workspace_id)
usuario.documents.list
usuario.oauth.userinfo # => { 'sub' => ..., 'name' => ..., 'email' => ... }conexao abaixo é o registro que você guarda para o usuário: tokens, validade e id da workspace.
novos = Assinafy::Client.new.oauth.refresh(
refresh_token: conexao.refresh_token,
client_id: ENV.fetch('ASSINAFY_CLIENT_ID')
)
# O refresh token enviado foi aposentado: guarde os tokens novos antes de qualquer outra coisa.
conexao.update!(
refresh_token: novos.fetch('refresh_token'),
access_token: novos.fetch('access_token'),
expires_at: Time.now + novos.fetch('expires_in')
)
usuario = Assinafy::Client.new(token: conexao.access_token, account_id: conexao.workspace_id)Quando o usuário desconectar, revogue o refresh token guardado agora — o mais recente — e só depois apague os tokens:
Assinafy::Client.new.oauth.revoke(
token: conexao.reload.refresh_token,
client_id: ENV.fetch('ASSINAFY_CLIENT_ID'),
token_type_hint: 'refresh_token'
)
conexao.destroy!Sem offline_access não há refresh token: quando o access token expirar, mande o usuário pelo
fluxo de autorização de novo. Revogar um refresh token invalida também os access tokens emitidos
a partir dele. A revogação responde 200 inclusive para um token já aposentado, então revogar uma
cópia antiga parece dar certo enquanto a conexão continua ativa.
O SDK não renova o token automaticamente. Guarde
expires_ine renove antes de expirar. Cada renovação devolve um refresh token novo, válido por mais 30 dias, e aposenta o anterior: a conexão só expira se passar 30 dias sem renovar. Reutilizar um refresh token aposentado encerra a conexão inteira, então guarde os tokens novos antes de usá-los e faça uma renovação por vez por conexão.refreshlevantaAssinafy::Errorem vez de devolver um sucesso sem refresh token novo; trate comoinvalid_grant.O SDK envia cada pedido de token uma única vez. Se a renovação falhar sem resposta clara — timeout, conexão interrompida,
5xx—, o servidor pode ter trocado o token sem que a resposta chegasse. Releia o refresh token guardado: se ainda for o que você enviou, nunca o reenvie; peça ao usuário para conectar de novo. Siga em frente só se outro processo já tiver guardado um token diferente. Só é seguro repetir uma falha que comprovadamente aconteceu antes do envio: DNS, conexão recusada, handshake TLS. Num401da API, renove uma vez; se falhar, ou eminvalid_grant, peça ao usuário para conectar de novo.
Em vez de fixar endpoints no código:
recurso = client.oauth.protected_resource_metadata
recurso['authorization_servers'] # => ["https://auth.assinafy.com.br"]
servidor = client.oauth.authorization_server_metadata
servidor['token_endpoint'] # => "https://api.assinafy.com.br/v1/oauth/token"
servidor['code_challenge_methods_supported'] # => ["S256"]Os endpoints OAuth respondem com o objeto plano da RFC 6749, não com o envelope da API. O SDK
levanta Assinafy::OAuthError (subclasse de Assinafy::ApiError):
begin
client.oauth.exchange_code(...)
rescue Assinafy::OAuthError => e
e.error # => "invalid_grant"
e.error_description # => "The authorization code is invalid or has expired."
e.status_code # => 400
endNum 403 de qualquer recurso, e.context[:www_authenticate] traz o desafio que nomeia o escopo
faltante: peça ao usuário para conectar de novo incluindo esse escopo, sem repetir a chamada. Um
403 sem esse desafio indica outra workspace, o papel do usuário ou uma área que tokens OAuth não
alcançam.
Definidos por signatário ao criar o assignment. Verificação e notificação são acopladas:
envie um, os dois, ou nenhum — o lado que faltar é inferido. Sem nenhum dos dois, ambos assumem
Email.
| Método | Como funciona | Custo por signatário |
|---|---|---|
Email (padrão) |
Código de uso único (OTP) por e-mail, exigido antes de assinar | Gratuito |
Whatsapp |
Código de uso único (OTP) por WhatsApp | Verificação gratuita; notificação 0,45 crédito, só em planos pagos |
DigitalCertificate |
O signatário assina com o próprio certificado ICP-Brasil (A1/A3), pela extensão de navegador Web PKI, gerando uma assinatura PAdES qualificada | 2 créditos |
Os valores aceitos estão publicados em
Assinafy::Resources::AssignmentResource::VERIFICATION_METHODS e ::NOTIFICATION_METHODS. O SDK
valida localmente: um valor fora do enum levanta ValidationError antes de a requisição
sair — e portanto antes de qualquer signatário ser criado para aquele assignment.
Combinações permitidas: Email → notifica por Email; Whatsapp → notifica por Whatsapp;
DigitalCertificate → notifica por Email ou Whatsapp.
Exige o recurso Certificado Digital na conta (planos Standard e Pro), CPF ou CNPJ em
government_id do signatário, e exatamente um signatário por certificado naquele passo. Um
CPF exige o certificado daquela pessoa (e-CPF, ou e-CNPJ que a nomeie como representante legal);
um CNPJ exige um e-CNPJ da empresa, de qualquer um de seus representantes.
client.assignments.create(
documento['id'],
method: 'virtual',
signers: [{ id: signatario['id'], verification_method: 'DigitalCertificate', step: 1 }]
)Antes de abrir o assignment, o signatário precisa confirmar os dados de identidade e aceitar os termos. O endpoint comum de assinatura rejeita signatários por certificado — a assinatura deles é produzida por um handshake de dois passos com a extensão Web PKI:
POST /v1/signers/certificate/start → data.token (token da operação Web PKI)
↓ o navegador assina o token com o certificado do signatário
POST /v1/signers/certificate/complete → data.signerName
Essas duas rotas são extensões que não constam do documento OpenAPI publicado: a autenticação e os esquemas de requisição/resposta delas não são documentados. Como envolveria adivinhar o payload, o SDK não expõe essas duas chamadas — fale com a Assinafy antes de habilitar o fluxo em produção.
Concluído o fluxo, baixar o artefato pades devolve a assinatura PAdES qualificada.
Um template é um PDF reutilizável com papéis e campos definidos:
template = client.templates.create('./contrato-modelo.pdf', name: 'Contrato padrão')
client.templates.list
client.templates.get(template['id'])
client.templates.update(template['id'], name: 'Contrato padrão v2')
client.templates.download_page(template['id'], pagina_id)
client.templates.delete(template['id'])Gerar um documento a partir de um template:
client.documents.estimate_cost_from_template(
template['id'], [{ full_name: 'Ana Silva', email: 'ana@exemplo.com.br' }]
)
documento = client.documents.create_from_template(
template['id'], [{ full_name: 'Ana Silva', email: 'ana@exemplo.com.br' }]
)Definições de campo reutilizáveis, com validação opcional por regex:
campo = client.fields.create(name: 'CPF', type: 'text', regex: '\A\d{11}\z')
client.fields.types # tipos de campo disponíveis
client.fields.list
client.fields.validate(campo['id'], '12345678901')
client.fields.validate_multiple([{ field_id: campo['id'], value: '12345678901' }])
client.fields.delete(campo['id'])tag = client.tags.create(name: 'Contratos 2026', color: '2072b9')
client.tags.list
client.tags.update(tag['id'], name: 'Contratos')
client.tags.delete(tag['id'], force: true)
client.documents.list_tags(documento['id'])
client.documents.append_tags(documento['id'], [tag['id']])
client.documents.replace_tags(documento['id'], [tag['id']])
client.documents.detach_tag(documento['id'], tag['id'])Registrar a assinatura de eventos da conta:
client.webhooks.list_event_types # eventos disponíveis
client.webhooks.register(
url: 'https://app.exemplo.com.br/webhooks/assinafy',
email: 'ops@exemplo.com.br', # para avisos de falha de entrega
events: %w[document.completed signer.declined],
is_active: true
)
client.webhooks.get # nil quando não há assinatura
client.webhooks.inactivate
client.webhooks.list_dispatches
client.webhooks.retry_dispatch(dispatch_id)Verificar a assinatura HMAC-SHA256 do payload recebido:
verificador = client.webhook_verifier # usa o webhook_secret do cliente
post '/webhooks/assinafy' do
corpo = request.body.read
assinatura = request.env['HTTP_X_ASSINAFY_SIGNATURE']
halt 401 unless verificador.verify(corpo, assinatura)
evento = verificador.extract_event(corpo)
case verificador.event_type(evento)
when 'document.completed' then processar(verificador.event_data(evento))
end
200
endverify usa comparação de tempo constante e devolve false — nunca levanta — para segredo
ausente, assinatura ausente ou corpo inválido.
Os métodos *.list* devolvem { data: [...], meta: { ... } } quando a API envia cabeçalhos de
paginação:
pagina = client.documents.list(page: 1, per_page: 50)
pagina[:data] # => [Documento, ...]
pagina[:meta] # => { current_page: 1, per_page: 50, total: 128, last_page: 3 }O per_page: em estilo Ruby é convertido para o parâmetro per-page documentado. Valores acima
do máximo são limitados pelo servidor (o sandbox limita em 50). Alguns endpoints não paginam e
devolvem meta: nil.
def cada_documento(client)
return to_enum(:cada_documento, client) unless block_given?
pagina = 1
loop do
resultado = client.documents.list(page: pagina, per_page: 50)
resultado[:data].each { |documento| yield documento }
meta = resultado[:meta]
break unless meta && meta[:last_page] && pagina < meta[:last_page]
pagina += 1
end
endbegin
client.documents.details(documento_id)
rescue Assinafy::ValidationError => e
# entrada inválida — detectada ANTES de qualquer requisição
warn e.errors.inspect
rescue Assinafy::OAuthError => e
# falha em endpoint OAuth (subclasse de ApiError)
warn "#{e.error}: #{e.error_description}"
rescue Assinafy::ApiError => e
# a API respondeu com erro
warn "Assinafy respondeu #{e.status_code}: #{e.message}"
warn e.response_data.inspect
rescue Assinafy::NetworkError => e
# conexão, timeout ou TLS
warn "Falha de rede: #{e.message}"
rescue Assinafy::Error => e
# qualquer outra falha do SDK
warn e.context.inspect
endToda exceção do SDK deriva de Assinafy::Error e carrega um #context com detalhes úteis para
depuração. ApiError trata as duas formas de corpo de erro da API — o erro de framework
({"name":..., "code":..., "status":...}) e o envelope de aplicação
({"status":..., "data":null, "message":...}) — inclusive quando um 200 traz um status
interno de falha.
As atividades de um documento devolvem todos os eventos registrados, cada um com um snapshot do
payload do evento e a origin da requisição (ip, user-agent).
| Artefato | Conteúdo |
|---|---|
original |
O PDF enviado, como recebido |
certificated |
O documento assinado, com a certificação da plataforma |
certificate-page |
Apenas a página de certificação |
pades |
Assinaturas ICP-Brasil dos signatários + caixa de certificação — só existe em documentos que tiveram signatários por certificado digital |
bundle |
Zip com original, certificated e certificate-page, mais o pades quando houver |
A verificação pública confere um documento assinado pelo hash da assinatura, sem autenticação:
Assinafy::Client.new.documents.verify('hash-da-assinatura')Ela devolve o resultado da Assinafy; não valida independentemente a assinatura do PDF nem a cadeia de certificação.
| Acessor | Cobre |
|---|---|
client.auth |
login, login social, senha, chaves de API |
client.oauth |
OAuth 2.1: token, refresh, revoke, userinfo, descoberta |
client.accounts |
workspaces, tema, KPIs, logo |
client.users |
perfil próprio, KPIs, preferências de notificação |
client.documents |
upload, listagem, download, ciclo de vida, tags, verificação |
client.signers |
CRUD de signatários e todo o autoatendimento do signatário |
client.signer_documents |
documentos do signatário, assinar/recusar em lote |
client.assignments |
pedidos de assinatura, custos, reenvios, assinatura, recusa |
client.templates |
ciclo de vida de templates |
client.tags |
CRUD de tags |
client.fields |
definições de campo e validação |
client.webhooks |
assinatura, entregas, reenvio |
client.webhook_verifier |
verificação HMAC do payload recebido |
client.faraday_connection expõe a conexão Faraday para middleware ou inspeção em testes.
| Produção | https://api.assinafy.com.br/v1 |
| Sandbox | https://sandbox.assinafy.com.br/v1 |
O sandbox é gratuito e espelha a produção 1 para 1 — mesmas rotas, mesmos contratos, incluindo
OAuth 2.1 e certificado digital. Troque apenas a base_url — e, no OAuth, o servidor de
autorização (https://auth-sandbox.assinafy.com.br) — para testar a integração de ponta a ponta
antes de ir para produção.
sig/assinafy.rbs acompanha a gem, então consumidores que usam RBS têm as assinaturas
publicadas. Desde a remoção do Steep elas não são verificadas por um type checker a cada build;
em vez disso, spec/rbs_signature_spec.rb garante que todo método
público de recurso, do Client e de Assinafy::OAuth tenha uma assinatura declarada — nenhum
método novo chega à gem sem uma.
- README.en.md — referência completa por recurso, em inglês
- docs/API_REFERENCE.md — referência por operação
- CHANGELOG.md — histórico de versões
- Documentação da API
Distribuído sob a licença MIT.