Guias

Adicionar automaticamente campos de assinatura com etiquetas âncora: Posicionamento de campos baseado em texto

Gráfico abstrato com fundo escuro apresenta um retângulo dividido, linhas pontilhadas rotuladas "X=?" e "Y=?", e uma seta apontando para a direita, sugerindo transformação.

Se alguma vez posicionou campos de assinatura calculando coordenadas x/y num PDF, sabe como isso se torna moroso à escala. Altere o layout do documento e cada coordenada deixa de funcionar. Adicione uma segunda página e terá de recalcular os deslocamentos de metade dos seus campos.

As etiquetas âncora resolvem isto. Incorpore marcadores de texto como {{SIGN_HERE}} ou {{DATE}} diretamente no seu modelo de PDF, e a Firma.dev deteta-os automaticamente e coloca os tipos de campo corretos nesses locais. Os marcadores são removidos do documento final, para que os signatários nunca os vejam. Uma chamada à API, sem cálculos de coordenadas.

As etiquetas âncora foram lançadas na v1.11.0 e suportam todos os tipos de campo, regras de correspondência flexíveis e até 100 etiquetas por pedido de assinatura.

Início Rápido

A forma mais rápida de pôr as etiquetas âncora a funcionar: incorpore um marcador no seu PDF e depois crie um pedido de assinatura com uma matriz anchor_tags.

Passo 1: Adicione o texto {{SIGN_HERE}} algures no seu PDF onde pretende que o campo de assinatura apareça. Pode fazê-lo em qualquer ferramenta que gere os seus documentos, quer seja um modelo do Word, uma biblioteca de PDF ou um serviço de geração de documentos.

Passo 2: Crie um pedido de assinatura baseado em documento com a definição da etiqueta âncora:

const response = await fetch(
  'https://api.firma.dev/functions/v1/signing-request-api/signing-requests',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspace_id: workspaceId,
      document: documentBase64,
      name: 'NDA - Acme Corp',
      anchor_tags: [
        {
          anchor_string: '{{SIGN_HERE}}',
          type: 'signature',
          recipient_id: 'temp_1',
          required: true
        }
      ],
      recipients: [
        {
          id: 'temp_1',
          first_name: 'Jane',
          last_name: 'Smith',
          email: 'jane@example.com',
          designation: 'Signer',
          order: 1
        }
      ]
    })
  }
);

const signingRequest = await response.json();
const response = await fetch(
  'https://api.firma.dev/functions/v1/signing-request-api/signing-requests',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspace_id: workspaceId,
      document: documentBase64,
      name: 'NDA - Acme Corp',
      anchor_tags: [
        {
          anchor_string: '{{SIGN_HERE}}',
          type: 'signature',
          recipient_id: 'temp_1',
          required: true
        }
      ],
      recipients: [
        {
          id: 'temp_1',
          first_name: 'Jane',
          last_name: 'Smith',
          email: 'jane@example.com',
          designation: 'Signer',
          order: 1
        }
      ]
    })
  }
);

const signingRequest = await response.json();
const response = await fetch(
  'https://api.firma.dev/functions/v1/signing-request-api/signing-requests',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspace_id: workspaceId,
      document: documentBase64,
      name: 'NDA - Acme Corp',
      anchor_tags: [
        {
          anchor_string: '{{SIGN_HERE}}',
          type: 'signature',
          recipient_id: 'temp_1',
          required: true
        }
      ],
      recipients: [
        {
          id: 'temp_1',
          first_name: 'Jane',
          last_name: 'Smith',
          email: 'jane@example.com',
          designation: 'Signer',
          order: 1
        }
      ]
    })
  }
);

const signingRequest = await response.json();

A Firma.dev analisa o PDF à procura de {{SIGN_HERE}}, coloca um campo de assinatura nesse local, atribui-o ao destinatário e remove o texto do marcador do documento. E é tudo.

Note que as etiquetas âncora só funcionam com a criação de pedidos de assinatura baseados em documento, e não com base em modelos. Está a enviar o PDF bruto e a deixar o sistema de âncoras tratar do posicionamento dos campos.

Tipos de Campo Suportados

As etiquetas âncora suportam todos os tipos de campo disponíveis na Firma.dev. Defina a propriedade type em cada etiqueta âncora para um destes valores:

signature para campos de assinatura, initials para campos de iniciais, text para texto de uma linha, textarea para texto de várias linhas, date para campos de data, checkbox para caixas de seleção, radio para grupos de botões de opção, dropdown para menus de seleção, url para campos de URL, stamp para campos de carimbo e file para campos de carregamento de ficheiros.

Cada tipo apresenta o widget de campo adequado na experiência de assinatura. Uma etiqueta âncora date cria um seletor de data, uma dropdown cria um menu de seleção, e assim por diante.

Opções de Correspondência e Posicionamento

O comportamento de correspondência predefinido funciona na maioria dos casos, mas pode ajustar finamente como a Firma.dev encontra e posiciona os campos criados por âncoras.

Sensibilidade a maiúsculas/minúsculas: Defina case_sensitive como true ou false (o valor predefinido é false). Com correspondência sem distinção entre maiúsculas e minúsculas, {{sign_here}} e {{SIGN_HERE}} correspondem ambas.

Correspondência de palavra inteira: Defina whole_word como true para impedir correspondências parciais. Se a sua string âncora for SIGN e whole_word for false, também corresponderia a SIGNATURE ou COSIGN. Defini-lo como true garante que só corresponde à palavra exata.

Direcionar ocorrências específicas: Se o seu PDF contiver a mesma string âncora várias vezes, pode apontar a ocorrências específicas com occurrence. Defina-o como 1 para a primeira correspondência, 2 para a segunda, e assim sucessivamente. Omita-o ou defina match_all como true para colocar campos em todas as ocorrências.

Posicionamento por deslocamento: Ajuste finamente onde o campo fica em relação ao texto âncora usando offset_x e offset_y. Pode especificar deslocamentos em percentagens (relativas às dimensões da página) ou em píxeis. Isto é útil quando quer que o campo fique ligeiramente abaixo ou à direita do marcador, em vez de diretamente sobre ele.

Tratamento elegante de âncoras em falta: Defina ignore_if_not_present como true se a string âncora puder não existir em todos os documentos. Sem isto, uma âncora em falta devolve um erro. Com isto ativado, a Firma.dev ignora silenciosamente essa etiqueta e processa o resto.

Configuração Avançada

Aqui está um exemplo mais completo que combina várias etiquetas âncora com diferentes tipos de campo, dimensões personalizadas e deslocamentos de posicionamento:

const response = await fetch(
  'https://api.firma.dev/functions/v1/signing-request-api/signing-requests',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspace_id: workspaceId,
      document: documentBase64,
      name: 'Employment Agreement',
      anchor_tags: [
        {
          anchor_string: '{{EMPLOYEE_SIGNATURE}}',
          type: 'signature',
          recipient_id: 'temp_1',
          required: true,
          width: 30,
          height: 8,
          offset_y: 2
        },
        {
          anchor_string: '{{EMPLOYEE_DATE}}',
          type: 'date',
          recipient_id: 'temp_1',
          required: true
        },
        {
          anchor_string: '{{EMPLOYEE_INITIALS}}',
          type: 'initials',
          recipient_id: 'temp_1',
          required: true,
          match_all: true
        },
        {
          anchor_string: '{{MANAGER_SIGNATURE}}',
          type: 'signature',
          recipient_id: 'temp_2',
          required: true
        },
        {
          anchor_string: '{{BENEFITS_OPT_IN}}',
          type: 'checkbox',
          recipient_id: 'temp_1',
          required: false,
          ignore_if_not_present: true
        }
      ],
      recipients: [
        {
          id: 'temp_1',
          first_name: 'Sarah',
          last_name: 'Chen',
          email: 'sarah@example.com',
          designation: 'Signer',
          order: 1
        },
        {
          id: 'temp_2',
          first_name: 'Mike',
          last_name: 'Torres',
          email: 'mike@example.com',
          designation: 'Signer',
          order: 2
        }
      ]
    })
  }
);
const response = await fetch(
  'https://api.firma.dev/functions/v1/signing-request-api/signing-requests',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspace_id: workspaceId,
      document: documentBase64,
      name: 'Employment Agreement',
      anchor_tags: [
        {
          anchor_string: '{{EMPLOYEE_SIGNATURE}}',
          type: 'signature',
          recipient_id: 'temp_1',
          required: true,
          width: 30,
          height: 8,
          offset_y: 2
        },
        {
          anchor_string: '{{EMPLOYEE_DATE}}',
          type: 'date',
          recipient_id: 'temp_1',
          required: true
        },
        {
          anchor_string: '{{EMPLOYEE_INITIALS}}',
          type: 'initials',
          recipient_id: 'temp_1',
          required: true,
          match_all: true
        },
        {
          anchor_string: '{{MANAGER_SIGNATURE}}',
          type: 'signature',
          recipient_id: 'temp_2',
          required: true
        },
        {
          anchor_string: '{{BENEFITS_OPT_IN}}',
          type: 'checkbox',
          recipient_id: 'temp_1',
          required: false,
          ignore_if_not_present: true
        }
      ],
      recipients: [
        {
          id: 'temp_1',
          first_name: 'Sarah',
          last_name: 'Chen',
          email: 'sarah@example.com',
          designation: 'Signer',
          order: 1
        },
        {
          id: 'temp_2',
          first_name: 'Mike',
          last_name: 'Torres',
          email: 'mike@example.com',
          designation: 'Signer',
          order: 2
        }
      ]
    })
  }
);
const response = await fetch(
  'https://api.firma.dev/functions/v1/signing-request-api/signing-requests',
  {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FIRMA_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      workspace_id: workspaceId,
      document: documentBase64,
      name: 'Employment Agreement',
      anchor_tags: [
        {
          anchor_string: '{{EMPLOYEE_SIGNATURE}}',
          type: 'signature',
          recipient_id: 'temp_1',
          required: true,
          width: 30,
          height: 8,
          offset_y: 2
        },
        {
          anchor_string: '{{EMPLOYEE_DATE}}',
          type: 'date',
          recipient_id: 'temp_1',
          required: true
        },
        {
          anchor_string: '{{EMPLOYEE_INITIALS}}',
          type: 'initials',
          recipient_id: 'temp_1',
          required: true,
          match_all: true
        },
        {
          anchor_string: '{{MANAGER_SIGNATURE}}',
          type: 'signature',
          recipient_id: 'temp_2',
          required: true
        },
        {
          anchor_string: '{{BENEFITS_OPT_IN}}',
          type: 'checkbox',
          recipient_id: 'temp_1',
          required: false,
          ignore_if_not_present: true
        }
      ],
      recipients: [
        {
          id: 'temp_1',
          first_name: 'Sarah',
          last_name: 'Chen',
          email: 'sarah@example.com',
          designation: 'Signer',
          order: 1
        },
        {
          id: 'temp_2',
          first_name: 'Mike',
          last_name: 'Torres',
          email: 'mike@example.com',
          designation: 'Signer',
          order: 2
        }
      ]
    })
  }
);

Algumas coisas a notar neste exemplo. A etiqueta {{EMPLOYEE_INITIALS}} usa match_all: true, por isso, se o PDF tiver marcadores de iniciais nas páginas 1, 5 e 12, todos os três recebem campos. A caixa de seleção {{BENEFITS_OPT_IN}} usa ignore_if_not_present: true porque nem todas as versões do contrato de trabalho incluem essa secção. E o offset_y: 2 no campo de assinatura desloca-o ligeiramente para baixo a partir da posição do texto âncora.

As dimensões (width e height) seguem o mesmo sistema de coordenadas baseado em percentagens usado por campos posicionados manualmente. As etiquetas âncora e os campos manuais funcionam em conjunto, por isso pode usar âncoras para a maior parte dos seus campos e ainda adicionar campos manuais baseados em coordenadas no mesmo pedido para casos especiais.

Migração de Outras Plataformas

Se está a migrar de outro fornecedor de assinaturas eletrónicas que usa posicionamento de campos baseado em texto, os conceitos mapeiam-se diretamente. A sintaxe da string âncora e os nomes das propriedades diferem, mas o mecanismo subjacente é o mesmo: incorpore um marcador, defina o tipo de campo, deixe a API tratar do posicionamento.

A partir do Auto-Place do DocuSign: DocuSign usa anchorString, anchorXOffset, anchorYOffset e tipos específicos de tabs, como signHereTabs. Na Firma.dev, isto passa a ser anchor_string, offset_x, offset_y e o campo universal type. O anchorIgnoreIfNotPresent do DocuSign corresponde a ignore_if_not_present. O anchorCaseSensitive do DocuSign corresponde a case_sensitive. A principal diferença estrutural é que a Firma.dev usa uma única matriz anchor_tags com uma propriedade type, em vez de matrizes separadas para cada tipo de tab.

Dos Smart Anchors da Yousign: O conceito de "smart anchors" da Yousign, com deteção do tipo de campo, traduz-se diretamente. As opções de correspondência e posicionamento são comparáveis, embora os nomes das propriedades usem a convenção snake_case da Firma.dev.

O limite é de 100 etiquetas âncora por pedido de assinatura, o que cobre confortavelmente a maioria dos fluxos de trabalho de documentos. Se estiver a processar documentos que realmente precisem de mais de 100 campos, considere dividir por vários pedidos de assinatura ou usar uma combinação de etiquetas âncora e definições de campos baseadas em modelos.

Referência da API

A matriz anchor_tags está disponível no endpoint de criação de pedidos de assinatura baseados em documentos (POST /signing-requests). Cada item na matriz é um objeto AnchorTag com cerca de 25 propriedades configuráveis.

As principais propriedades:

anchor_string (obrigatório) é o marcador de texto a procurar no PDF. type (obrigatório) é o tipo de campo a criar. recipient_id atribui o campo a um destinatário específico usando o respetivo ID ou ID temporário. required marca o campo como obrigatório ou opcional. offset_x e offset_y ajustam a posição do campo em relação à localização da âncora. width e height definem dimensões personalizadas do campo. case_sensitive controla se a correspondência é sensível a maiúsculas/minúsculas. whole_word impede correspondências parciais de cadeias. occurrence aponta para uma instância específica quando a âncora aparece várias vezes. match_all coloca campos em todas as ocorrências. ignore_if_not_present ignora a etiqueta sem erro se o texto âncora não for encontrado no documento.

Para o esquema completo com todas as propriedades e respetivos tipos, consulte o registo de alterações da API da v1.11.0.

Próximos Passos

As etiquetas âncora combinam bem com a geração programática de documentos. Se a sua aplicação gera contratos, NDAs ou documentos de onboarding a partir de modelos, pode incorporar strings âncora durante a geração e deixar a Firma.dev tratar automaticamente do posicionamento dos campos em cada pedido.

Comece a usar a Firma.dev gratuitamente, sem necessidade de cartão de crédito.

  1. Cabeçalho

Imagem de Fundo

Pronto para adicionar assinaturas eletrónicas à sua aplicação?

Comece gratuitamente. Não é necessário cartão de crédito. Pague apenas 0,049 € por envelope quando estiver pronto para começar.

Imagem de Fundo

Pronto para adicionar assinaturas eletrónicas à sua aplicação?

Comece gratuitamente. Não é necessário cartão de crédito. Pague apenas 0,049 € por envelope quando estiver pronto para começar.

Imagem de Fundo

Pronto para adicionar assinaturas eletrónicas à sua aplicação?

Comece gratuitamente. Não é necessário cartão de crédito. Pague apenas 0,049 € por envelope quando estiver pronto para começar.