Dazr Sign API: documentación para desarrolladores

Inicio rápido

  1. Activa la Dazr Sign API. En el portal de Sign, abre Desarrolladores y elige Activar Dazr Sign API para tu app. Cada app de tu organización aparece allí y en la consola de Dazr Identity, con el mismo ID de cliente y el mismo secreto; cada API se activa por separado. Allí también puedes registrar una app nueva.
  2. Pide el scope sign. Lleva a las personas por el flujo habitual de Iniciar sesión con Dazr Identity con scope=openid sign (añade offline_access para un token de actualización). La pantalla de consentimiento indica que tu app puede preparar y enviar documentos para firmar en su nombre y ver su estado. Basta con la Dazr Sign API; para pedir también datos de las personas (profile, email y los demás) hace falta activar también la Dazr Identity API.
  3. Llama a la API con el token de acceso. La dirección base es https://sign.dazr.eu/api/sign/v1. Envía Authorization: Bearer <access token> y JSON.
  4. Crea y envía un documento. Sube un PDF con etiquetas de texto y una lista de destinatarios y envíalo, o abre la ventana de preparación para que la persona coloque los campos.
// Node 18+: create a document from a PDF with text tags, then send it
import fs from 'node:fs';
const api = 'https://sign.dazr.eu/api/sign/v1';
const headers = { Authorization: 'Bearer ' + accessToken, 'Content-Type': 'application/json' };

let res = await fetch(api + '/documents', {
  method: 'POST',
  headers: { ...headers, 'Idempotency-Key': 'order-1042' },
  body: JSON.stringify({
    title: 'Service agreement',
    file: fs.readFileSync('agreement.pdf').toString('base64'),
    recipients: [
      { name: 'Alex Example', email: 'alex@example.eu' },
      { name: 'Sam Client', email: 'sam@client.example' },
    ],
  }),
});
const { document } = await res.json();

res = await fetch(api + '/documents/' + document.id + '/send', { method: 'POST', headers });
console.log((await res.json()).document.status); // "sent"

Etiquetas de texto

Escribe los campos en el PDF como texto. Cada etiqueta se sustituye por un campo del mismo tamaño o mayor, y el texto de la etiqueta se cubre, así quienes firman nunca lo ven.

EtiquetaCampo
{{signature:1}}Firma del primer destinatario
{{initials:2}}Iniciales del segundo destinatario
{{name:1}} {{date:1}}Nombre y fecha de la firma, rellenados por Dazr Sign
{{text:2:Company}}Un campo de texto con la etiqueta Company
{{checkbox:2:Terms:optional}}Una casilla opcional
{{company:1}}Nombre de la empresa (se rellena cuando la persona firma en nombre de una organización)

El número es la posición del destinatario en recipients, empezando por 1. Una etiqueta para un destinatario que no indicaste añade un destinatario vacío, que se completa en la ventana de preparación. Envía "text_tags": false para ignorar las etiquetas. También puedes enviar fields con posiciones en puntos (1/72 de pulgada) desde la esquina superior izquierda de la página: { "recipient": 1, "type": "signature", "page": 1, "x": 72, "y": 600, "width": 180, "height": 48 }. Un value en un campo de texto, empresa o casilla lo fija: quien firma lo ve y no puede cambiarlo.

Crea un borrador con "prepare": { "origin": "https://app.example.eu" } y obtendrás una prepare_url. Ábrela en una ventana emergente: la persona coloca destinatarios y campos en un editor compacto y luego pulsa Enviar o Guardar como plantilla. Para quien firma y está en su turno, POST /documents/{id}/recipients/{recipient_id}/signing_url devuelve una ventana de firma. Ambos enlaces valen de 10 a 15 minutos y funcionan una sola vez. Si hace falta, la persona inicia sesión en Dazr Identity dentro de la ventana, con la dirección a la que se envió el documento.

Al terminar, la ventana envía { type: 'dazr-sign', event, document_id, template_id, recipient_id, state } con postMessage a la página que la abrió, solo si esa página está en uno de los orígenes de ventana de tu app (por defecto, los orígenes de tus URI de redirección), y se cierra. event es sent, saved, cancelled, signed o declined. Con redirect_uri (una de tus URI de redirección registradas) y state, una ventana que ha perdido su página de origen vuelve allí con los mismos valores como parámetros de consulta.

<script src="https://sign.dazr.eu/sdk.js"></script>
<script>
document.querySelector('#sign').addEventListener('click', async () => {
  // Ask your server for the URL after the click: the popup opens at once.
  const url = fetch('/my-server/prepare-url').then(r => r.json()).then(j => j.prepare_url);
  const result = await DazrSign.open(url);
  if (result.event === 'sent') showSent(result.document_id);
  // 'closed': the window was closed; check the status with the API or a webhook
});
</script>

Si tu página envía Cross-Origin-Opener-Policy: same-origin, usa same-origin-allow-popups en su lugar o la ventana no podrá devolver el resultado.

Firmas cualificadas

Cada firmante tiene un level: "advanced" (por defecto: la dirección de correo se confirma al iniciar sesión con Dazr Identity) o "qualified". Quien firma con firma cualificada descarga el PDF en Dazr Sign, lo firma con su propia firma electrónica cualificada (un DNI electrónico, una app de firma como itsme, un certificado cualificado en Adobe Acrobat Reader) y sube el archivo firmado. Dazr Sign lo comprueba con las listas de confianza de la UE y solo lo acepta si es una firma cualificada exactamente sobre el archivo entregado. Dazr no emite firmas cualificadas.

Define el nivel por destinatario: { "name": "Mario Rossi", "email": "mario@example.eu", "level": "qualified" }. Quien firma con firma cualificada no tiene campos de iniciales. name_rule en el documento indica qué ocurre si el nombre del certificado no coincide con el del destinatario: "warn" (por defecto: se acepta, con name_matches en false) o "block" (se rechaza). Las plantillas guardan el nivel de cada rol, y from_template puede subir un rol a "qualified".

Todas las firmas siguen siendo válidas: las firmas posteriores y el sello final de Dazr se añaden como actualizaciones incrementales, y el registro de auditoría de estos documentos es un PDF sellado aparte en GET /documents/{id}/audit.pdf. Los destinatarios en documentos y webhooks tienen level y, tras la firma cualificada, certificate: subject (common_name, given_name, surname, serial_number, country, organization, organization_identifier), issuer, serial, sha256, trust_service_provider, trusted_list_country, signing_time, trusted_timestamp, format (PAdES o CAdES) y name_matches. Cualquier otro level devuelve 400 invalid_level.

Webhooks

Configura una URL de webhook para tu app en el portal de Sign, en Desarrolladores. Verás su secreto de firma una sola vez. Dazr Sign envía sign.document.sent, viewed, signed, completed, declined, voided y expired para los documentos que creó tu app, con el estado del documento y de sus destinatarios, nunca su contenido. Los envíos fallidos se reintentan tras 5 minutos, 30 minutos, 2, 6, 12 y 24 horas; el registro de envíos muestra cada intento y permite reenviar uno.

// Node: check the Dazr-Signature header (t=<unix time>,v1=<HMAC-SHA256 hex>)
import crypto from 'node:crypto';

export function verify(rawBody, header, secret) {
  const m = /^t=(\d+),v1=([0-9a-f]{64})$/.exec(header || '');
  if (!m || Math.abs(Date.now() / 1000 - Number(m[1])) > 300) return false;
  const want = crypto.createHmac('sha256', secret).update(m[1] + '.' + rawBody).digest();
  return crypto.timingSafeEqual(want, Buffer.from(m[2], 'hex'));
}
// The body: { id, type: 'sign.document.completed', created, data: { document: { id, status, recipients: [...] } } }

Referencia

Todas las rutas empiezan por https://sign.dazr.eu/api/sign/v1. Las horas están en ISO 8601, en UTC.

SolicitudQué hace
POST /documentsCrea un borrador a partir de un PDF: file en base64, una subida multipart (file más data como JSON) o un upload_id. Opcional: title, message, recipients, fields, signing_order, deadline_days, reminder_days, send, prepare.
POST /documents/from_templateCrea un borrador a partir de una plantilla: template_id, recipients como { role, name, email }, values por ID o etiqueta del campo.
GET /documentsLista los documentos de tu app para esta persona, del más reciente al más antiguo: limit (hasta 100), cursor, status.
GET /documents/{id}El documento con su estado, destinatarios y campos.
POST /documents/{id}/sendEnvía un borrador.
POST /documents/{id}/remindRecuerda a quienes deben firmar en su turno (como máximo una vez por hora).
POST /documents/{id}/voidCancela un documento pendiente de firma, con un reason opcional.
DELETE /documents/{id}Elimina un borrador o un documento cerrado.
POST /documents/{id}/prepare_urlUna nueva ventana de preparación para un borrador: origin, redirect_uri, state.
POST /documents/{id}/recipients/{recipient_id}/signing_urlUna ventana de firma para quien firma y está en su turno: origin, redirect_uri, state.
GET /documents/{id}/original.pdfEl PDF tal como se envió.
GET /documents/{id}/signed.pdfLa copia sellada, cuando todos han firmado.
GET /documents/{id}/audit.jsonEl registro de auditoría: eventos, hashes y firmantes.
GET /documents/{id}/audit.pdfEl registro de auditoría aparte y sellado de un documento con firmas cualificadas, cuando todos han firmado.
POST /uploadsInicia una subida por partes para PDF de más de 4 MB: size. Después PUT /uploads/{upload_id}/parts/{n} con los bytes de cada parte.
GET /templatesLas plantillas de la persona, con sus roles y campos.
GET /templates/{id}Una plantilla.
POST /templatesGuarda como plantilla uno de los documentos de tu app: document_id, name.
DELETE /templates/{id}Elimina una plantilla.

Envía una cabecera Idempotency-Key al crear un documento: la misma clave y el mismo cuerpo en 24 horas devuelven el mismo documento (cabecera Idempotent-Replayed: true) en lugar de uno nuevo.

Errores y límites

Los errores tienen un estado HTTP y un cuerpo como { "error": { "code": "not_found", "message": "..." } }, a veces con detalles como index.

EstadoCódigos
400invalid_field, invalid_recipients, invalid_email, not_pdf, pdf_unreadable, pdf_encrypted, invalid_origin, invalid_redirect_uri, unknown_role, unknown_field
401unauthenticated, invalid_token (caducado o revocado, o la persona quitó tu app)
403insufficient_scope, sign_api_disabled
404not_found (también para documentos que tu app no creó), template_not_found
409not_draft, not_sent, not_your_turn, not_signable, signer_without_fields, no_signers, void_first, idempotency_in_progress
413pdf_too_large, too_large, quota
422idempotency_key_reused
429rate_limited, con una cabecera Retry-After

Transferir una app

Una app puede pasar a otra organización registrada en Dazr, con Transferir app en sus ajustes. Con la API de Dazr Sign activada, solo puede pasar a una organización verificada, y quien la posee o la administra debe aceptar en 14 días. La documentación para desarrolladores de Dazr Identity describe cada paso.