Formularios PDF: del widget al documento firmado
Llenar es parte de firmar. Con la API v3 subes un PDF que ya trae su formulario (AcroForm), sus widgets se vuelven campos con nombre, tipo y posición, le pones a cada campo el rol que lo llena, y al emitir el documento decides qué va prellenado y bloqueado. El firmante llena lo suyo sobre el mismo PDF y firma; tú lees después campo por campo quién lo llenó, cuándo y con qué valor. Seis pasos, todos por API, ninguno en el panel.
Los ejemplos usan $ALLSIGN_API_KEY. Defínela una vez con tu key de sandbox: export ALLSIGN_API_KEY="allsign_test_sk_…" (en PowerShell: $env:ALLSIGN_API_KEY="allsign_test_sk_…").
1 · Subir el PDF y leer los campos detectados
Crea la plantilla con el PDF en base64. Si trae formulario, cada widget entra como campo con
source: "acroform"; la respuesta ya dice cuántos campos hay y cuántos siguen sin rol
(readiness: "pending" hasta que todos tengan uno). Una caja de firma cuyo nombre sugiere
el rol (firma_cliente) nace con el rol Cliente.
curl -X POST "https://api.allsign.io/v3/templates" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-H "Content-Type: application/json" \
-d '{"name":"Contrato de compraventa","file":{"content":"<base64 del PDF>","fileType":"pdf","name":"contrato-compraventa.pdf"}}'201 — la plantilla:
{
"object": "template",
"id": "tmpl_3f9c1a2b4d5e6f70",
"livemode": true,
"name": "Contrato de compraventa",
"description": null,
"fileType": "pdf",
"originalFilename": "contrato-compraventa.pdf",
"aiEditable": true,
"variableCount": 0,
"fieldCount": 5,
"unassignedFieldCount": 4,
"roleCount": 1,
"pendingCandidates": 0,
"readiness": "pending",
"tags": [],
"category": null,
"usageCount": 0,
"lastUsedAt": null,
"currentVersion": 1,
"previewUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/preview",
"downloadUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/download",
"createdAt": "2026-09-20T17:58:03Z",
"updatedAt": "2026-09-20T17:58:03Z"
}Lee los campos: name es la clave estable (la del widget), type es
text, date, checkbox, radio, select,
signature, initials o stamp, y areas trae página y
rectángulo en % del papel (varias áreas = el mismo dato repetido en el PDF).
curl "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70/fields" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11"200:
{
"object": "list",
"templateId": "tmpl_3f9c1a2b4d5e6f70",
"pageCount": 1,
"data": [
{
"object": "template_field",
"name": "nombre",
"type": "text",
"role": null,
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"areas": [
{
"page": 1,
"rect": {
"x": 12.5,
"y": 18.2,
"width": 40,
"height": 3.1
}
}
]
},
{
"object": "template_field",
"name": "rfc",
"type": "text",
"role": null,
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"areas": [
{
"page": 1,
"rect": {
"x": 12.5,
"y": 24.6,
"width": 40,
"height": 3.1
}
}
]
},
{
"object": "template_field",
"name": "fecha",
"type": "date",
"role": null,
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"areas": [
{
"page": 1,
"rect": {
"x": 12.5,
"y": 31,
"width": 20,
"height": 3.1
}
}
]
},
{
"object": "template_field",
"name": "acepta",
"type": "checkbox",
"role": null,
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"areas": [
{
"page": 1,
"rect": {
"x": 12.5,
"y": 70.4,
"width": 2.4,
"height": 1.6
}
}
]
},
{
"object": "template_field",
"name": "firma_cliente",
"type": "signature",
"role": "Cliente",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"areas": [
{
"page": 1,
"rect": {
"x": 12.5,
"y": 80,
"width": 30,
"height": 8
}
}
]
}
],
"unassignedCount": 4,
"hasMore": false,
"livemode": true
}2 · Asignar roles y comprobar que la plantilla está lista
El rol es el dueño del campo; el firmante se deriva del rol al emitir. Declara los roles en orden y
asigna cada campo con PATCH …/fields/{name} (también puedes fijar
required y una label para el firmante). Cuando unassignedFieldCount
llega a 0, la plantilla queda readiness: "ready".
curl -X PUT "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70/roles" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-H "Content-Type: application/json" \
-d '{"roles":["Cliente"]}'
for name in nombre rfc fecha acepta; do
curl -X PATCH "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70/fields/$name" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-H "Content-Type: application/json" \
-d '{"role":"Cliente"}'
done
curl "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11"200 — lista para emitir:
{
"object": "template",
"id": "tmpl_3f9c1a2b4d5e6f70",
"livemode": true,
"name": "Contrato de compraventa",
"description": null,
"fileType": "pdf",
"originalFilename": "contrato-compraventa.pdf",
"aiEditable": true,
"variableCount": 0,
"fieldCount": 5,
"unassignedFieldCount": 0,
"roleCount": 1,
"pendingCandidates": 0,
"readiness": "ready",
"tags": [],
"category": null,
"usageCount": 0,
"lastUsedAt": null,
"currentVersion": 1,
"previewUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/preview",
"downloadUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/download",
"createdAt": "2026-09-20T17:58:03Z",
"updatedAt": "2026-09-20T18:01:40Z"
}3 · Revisar el PDF con los widgets vivos, o aplanado
Antes de emitir, baja el PDF tal como lo verá el firmante. Con format=form conserva los
widgets del formulario para revisarlos en Acrobat o en tu propio visor; con format=flat
los hornea (sin campos editables).
curl "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70/file?format=form" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-o contrato-form.pdf
curl "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70/file?format=flat" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-o contrato-flat.pdf200 — application/pdf con Content-Disposition: attachment.
4 · Crear el documento con valores prellenados y bloqueados por firmante
Cada firmante toma un rol por roleName y hereda sus campos. values prellena
campos por nombre (texto, fecha YYYY-MM-DD o true/false en
casillas) y readOnly lista los que el firmante puede ver pero no cambiar: el caso del
asesor que manda el contrato con el RFC ya puesto. Manda Idempotency-Key
(Idempotencia).
curl -X POST "https://api.allsign.io/v3/documents" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d @crear.jsonCuerpo (crear.json):
{
"name": "Contrato de compraventa — Ana López",
"templateId": "tmpl_3f9c1a2b4d5e6f70",
"signers": [
{
"roleName": "Cliente",
"email": "ana@cliente.mx",
"name": "Ana López",
"values": {
"rfc": "XAXX010101000",
"fecha": "2026-09-20"
},
"readOnly": [
"rfc"
]
}
]
}Los campos del documento se materializan en segundos. Los prellenados traen su valor y
filledBy: "owner"; el resto espera al firmante (filledBy: null).
curl "https://api.allsign.io/v3/documents/doc_8f2c1e0a9b7d4c6e5f3a2b1c0d9e8f7a/fields" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11"200:
{
"object": "list",
"documentId": "doc_8f2c1e0a9b7d4c6e5f3a2b1c0d9e8f7a",
"data": [
{
"id": "fie_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
"object": "document_field",
"name": "nombre",
"type": "text",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 18.2,
"width": 40,
"height": 3.1
},
"position": 1,
"status": "pending",
"value": {
"text": null,
"checked": null
},
"filledBy": null,
"filledAt": null
},
{
"id": "fie_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d",
"object": "document_field",
"name": "rfc",
"type": "text",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": true,
"page": 1,
"rect": {
"x": 12.5,
"y": 24.6,
"width": 40,
"height": 3.1
},
"position": 1,
"status": "pending",
"value": {
"text": "XAXX010101000",
"checked": null
},
"filledBy": "owner",
"filledAt": "2026-09-20T18:02:11Z"
},
{
"id": "fie_1a2b3c4d5e6f708192a3b4c5d6e7f809",
"object": "document_field",
"name": "fecha",
"type": "date",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 31,
"width": 20,
"height": 3.1
},
"position": 1,
"status": "pending",
"value": {
"text": "2026-09-20",
"checked": null
},
"filledBy": "owner",
"filledAt": "2026-09-20T18:02:11Z"
},
{
"id": "fie_2b3c4d5e6f708192a3b4c5d6e7f8091a",
"object": "document_field",
"name": "acepta",
"type": "checkbox",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 70.4,
"width": 2.4,
"height": 1.6
},
"position": 1,
"status": "pending",
"value": {
"text": null,
"checked": null
},
"filledBy": null,
"filledAt": null
},
{
"id": "fie_3c4d5e6f708192a3b4c5d6e7f8091a2b",
"object": "document_field",
"name": "firma_cliente",
"type": "signature",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 80,
"width": 30,
"height": 8
},
"position": 1,
"status": "pending",
"value": {
"text": null,
"checked": null
},
"filledBy": null,
"filledAt": null
}
],
"unassignedCount": 0,
"hasMore": false,
"livemode": true
}5 · Leer el estado: quién lo llenó, cuándo y con qué valor
El firmante llena sus campos sobre el PDF y firma. Cuando el documento llega a
completed (o en cualquier momento antes), vuelve a leer los campos: value
trae lo capturado, status pasa a signed, y filledBy /
filledAt dicen quién puso cada valor y cuándo — "owner" lo que prellenaste
tú, "signer" lo que escribió el firmante dueño (signerId).
curl "https://api.allsign.io/v3/documents/doc_8f2c1e0a9b7d4c6e5f3a2b1c0d9e8f7a/fields" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11"200:
{
"object": "list",
"documentId": "doc_8f2c1e0a9b7d4c6e5f3a2b1c0d9e8f7a",
"data": [
{
"id": "fie_0f1e2d3c4b5a69788796a5b4c3d2e1f0",
"object": "document_field",
"name": "nombre",
"type": "text",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 18.2,
"width": 40,
"height": 3.1
},
"position": 1,
"status": "signed",
"value": {
"text": "Ana López",
"checked": null
},
"filledBy": "signer",
"filledAt": "2026-09-21T10:15:42Z"
},
{
"id": "fie_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d",
"object": "document_field",
"name": "rfc",
"type": "text",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": true,
"page": 1,
"rect": {
"x": 12.5,
"y": 24.6,
"width": 40,
"height": 3.1
},
"position": 1,
"status": "signed",
"value": {
"text": "XAXX010101000",
"checked": null
},
"filledBy": "owner",
"filledAt": "2026-09-20T18:02:11Z"
},
{
"id": "fie_1a2b3c4d5e6f708192a3b4c5d6e7f809",
"object": "document_field",
"name": "fecha",
"type": "date",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 31,
"width": 20,
"height": 3.1
},
"position": 1,
"status": "signed",
"value": {
"text": "2026-09-20",
"checked": null
},
"filledBy": "owner",
"filledAt": "2026-09-20T18:02:11Z"
},
{
"id": "fie_2b3c4d5e6f708192a3b4c5d6e7f8091a",
"object": "document_field",
"name": "acepta",
"type": "checkbox",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 70.4,
"width": 2.4,
"height": 1.6
},
"position": 1,
"status": "signed",
"value": {
"text": null,
"checked": true
},
"filledBy": "signer",
"filledAt": "2026-09-21T10:16:03Z"
},
{
"id": "fie_3c4d5e6f708192a3b4c5d6e7f8091a2b",
"object": "document_field",
"name": "firma_cliente",
"type": "signature",
"role": "Cliente",
"signerId": "sgr_1b2c3d4e5f60718293a4b5c6d7e8f901",
"required": true,
"label": null,
"options": null,
"group": null,
"source": "acroform",
"readOnly": false,
"page": 1,
"rect": {
"x": 12.5,
"y": 80,
"width": 30,
"height": 8
},
"position": 1,
"status": "signed",
"value": {
"text": null,
"checked": null
},
"filledBy": null,
"filledAt": null
}
],
"unassignedCount": 0,
"hasMore": false,
"livemode": true
}6 · Descargar el documento
Antes de enviar, GET /v3/documents/{id}/file?format=form devuelve el PDF con los widgets
vivos y lo prellenado (para que el emisor lo revise) y format=flat lo hornea con los
valores actuales. Después de la firma, el PDF con validez legal es la
evidencia (GET /v3/documents/{id}/evidence): todos los widgets van
horneados con lo capturado, más la constancia NOM-151.
curl "https://api.allsign.io/v3/documents/doc_8f2c1e0a9b7d4c6e5f3a2b1c0d9e8f7a/file?format=flat" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-o contrato-flat.pdf200 — application/pdf con Content-Disposition: attachment.
Cuando el PDF cambia: reemplazarlo sin perder los campos
Meses después el contrato se actualiza —una cláusula nueva, otro logo— y la plantilla ya tiene sus
campos colocados y sus roles asignados. POST /v3/templates/{id}/file cambia el archivo y
los conserva: las áreas se guardan en porcentaje de página, así que un PDF con otro
tamaño de hoja no las mueve. El archivo viaja en base64, igual que al crear la plantilla, y solo se
acepta PDF sobre PDF (este endpoint no convierte DOCX↔PDF).
curl -X POST "https://api.allsign.io/v3/templates/tmpl_3f9c1a2b4d5e6f70/file" \
-H "Authorization: Bearer $ALLSIGN_API_KEY" \
-H "AllSign-Version: 2026-07-11" \
-H "Content-Type: application/json" \
-d '{"file":{"content":"<base64 del PDF nuevo>","fileType":"pdf","name":"contrato-compraventa-2026.pdf"}}'200 — el archivo nuevo tiene al menos las mismas páginas, así que nada se podó:
{
"object": "template_file_replace",
"template": {
"object": "template",
"id": "tmpl_3f9c1a2b4d5e6f70",
"livemode": true,
"name": "Contrato de compraventa",
"description": null,
"fileType": "pdf",
"originalFilename": "contrato-compraventa-2026.pdf",
"aiEditable": true,
"variableCount": 0,
"fieldCount": 5,
"unassignedFieldCount": 0,
"roleCount": 1,
"pendingCandidates": 0,
"readiness": "ready",
"tags": [],
"category": null,
"usageCount": 0,
"lastUsedAt": null,
"currentVersion": 2,
"previewUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/preview",
"downloadUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/download",
"createdAt": "2026-09-20T17:58:03Z",
"updatedAt": "2026-10-02T09:12:55Z"
},
"pageCount": 2,
"prunedFields": [],
"livemode": true
}Lo único que sí puede romperse es el número de páginas: un campo que vivía en una
página que el archivo nuevo ya no tiene se poda —se borra esa área, nunca la plantilla ni el resto de
las áreas de ese campo— y la respuesta lo dice en prunedFields. Revísalo siempre:
removed: true significa que el campo se quedó sin ninguna área y hay que volver a
colocarlo.
200 — el PDF nuevo trae una página menos y la casilla que vivía en la segunda se cayó:
{
"object": "template_file_replace",
"template": {
"object": "template",
"id": "tmpl_3f9c1a2b4d5e6f70",
"livemode": true,
"name": "Contrato de compraventa",
"description": null,
"fileType": "pdf",
"originalFilename": "contrato-compraventa-corto.pdf",
"aiEditable": true,
"variableCount": 0,
"fieldCount": 4,
"unassignedFieldCount": 0,
"roleCount": 1,
"pendingCandidates": 0,
"readiness": "ready",
"tags": [],
"category": null,
"usageCount": 0,
"lastUsedAt": null,
"currentVersion": 3,
"previewUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/preview",
"downloadUrl": "/templates/tmpl_3f9c1a2b4d5e6f70/download",
"createdAt": "2026-09-20T17:58:03Z",
"updatedAt": "2026-10-09T11:40:18Z"
},
"pageCount": 1,
"prunedFields": [
{
"name": "acepta",
"pages": [
2
],
"removed": true
}
],
"livemode": true
}Qué sigue
- Campos y roles de plantilla: crear, mover, reemplazar el layout, importar desde un documento.
- Reemplazar el PDF y las versiones guardadas para volver atrás.
- Campos de documento: prellenar, bloquear, reasignar en lote, descargar.
- Webhooks para saber cuándo un documento se completó sin consultar.