Skip to Content
GuidesBulk Signing

Bulk Signing

Bulk signing signs hundreds of documents in one API call. Use it when one signer (e.g., a CEO) needs to sign many documents of the same type.

Overview

The bulk signing flow has three steps:

  1. Save a signature: Capture and store the signer’s signature image
  2. Create documents: Create documents from a template with signers and fields
  3. Bulk sign: Sign all documents at once using the saved signature

Step 1: Save a Signature

Before bulk signing, you need a saved signature for the signer. You can capture signatures using the <KoralaSignaturePad> React component or any other method that produces a base64 PNG data URL.

Format requirement: Signature images must be base64 PNG data URLs (data:image/png;base64,...). The API rejects JPEG, SVG, and regular HTTP URLs. The maximum size is 500KB.

Using the React Signature Pad

import { KoralaSignaturePad } from '@korala/react'; function SignatureCapture() { return ( <KoralaSignaturePad onSignatureCreated={({ imageDataUrl }) => { // Send to your backend to save via Korala API fetch('/api/save-signature', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ signatureImageUrl: imageDataUrl, email: '[email protected]', name: 'Jane Smith', }), }); }} /> ); }

Saving via the API

import { KoralaClient } from '@korala/api-client'; const korala = new KoralaClient({ apiKeyId: process.env.KORALA_API_KEY_ID!, apiSecret: process.env.KORALA_API_SECRET!, }); const signature = await korala.signatures.create({ email: '[email protected]', name: 'Jane Smith', signatureImageUrl: 'data:image/png;base64,...', isDefault: true, }); console.log(`Saved signature: ${signature.id}`);

Managing Saved Signatures

// List signatures for a specific email const signatures = await korala.signatures.list({ email: '[email protected]' }); // Get a specific signature const signature = await korala.signatures.get(signatureId); // Delete a signature await korala.signatures.delete(signatureId);

Step 2: Create Documents

Create your documents using templates. See the Documents guide for details on creating and sending documents.

// Create 200 NDAs from a template const documentIds: string[] = []; for (const recipient of recipients) { const doc = await korala.templates.createDocument(templateId, { signerRoles: { 'Company Representative': { email: '[email protected]', name: 'Jane Smith', }, 'Recipient': { email: recipient.email, name: recipient.name, }, }, }); documentIds.push(doc.id); // Send for signing await korala.documents.send(doc.id); }

Step 3: Bulk Sign

Sign all documents at once using the saved signature:

const result = await korala.documents.bulkSign({ documentIds, signerEmail: '[email protected]', autoFillDateValue: '2026-02-18', // optional, defaults to today }); console.log(`Signed: ${result.signed}`); console.log(`Failed: ${result.failed}`); // Check individual results for (const doc of result.results) { if (doc.success) { console.log(`${doc.documentId}: signed (allSigned: ${doc.allSigned})`); } else { console.log(`${doc.documentId}: failed - ${doc.error}`); } }

How Auto-Fill Works

During bulk signing, Korala fills fields by type:

Field TypeAuto-Fill Behavior
SignatureUses the signer’s default saved signature
InitialsUses the signer’s default saved signature
DateUses autoFillDateValue (defaults to today)
TextRequires a prefilled value (e.g., from template variables)
CheckboxRequires a prefilled value (e.g., from template variables)

Important: If no saved signature exists for the signer, any required Signature or Initials fields will fail with an error. You must save a signature via POST /signatures (Step 1) before calling bulk sign.

Per-Document Overrides

You can override specific field values for individual documents:

const result = await korala.documents.bulkSign({ documentIds: ['doc-1', 'doc-2', 'doc-3'], signerEmail: '[email protected]', documents: [ { documentId: 'doc-3', fields: [ // Override a text field value { fieldId: 'field-xyz', value: 'Special terms apply' }, // Override a signature field with a different signature image { fieldId: 'field-sig', value: 'Signed', signatureImageUrl: 'data:image/png;base64,...', }, ], }, ], });

Per-document overrides take precedence over auto-fill.

Note: Signature images must be base64 PNG data URLs (data:image/png;base64,...). The API rejects other formats (JPEG, SVG, regular URLs).

Handling Partial Success

Bulk sign always returns HTTP 200 with per-document results. Some documents may fail while others succeed:

const result = await korala.documents.bulkSign({ documentIds, signerEmail: '[email protected]', }); if (result.failed > 0) { const failures = result.results.filter((r) => !r.success); console.error('Failed documents:', failures); // Retry failed documents const retryIds = failures .filter((f) => f.error !== 'Signer has already signed') .map((f) => f.documentId); if (retryIds.length > 0) { await korala.documents.bulkSign({ documentIds: retryIds, signerEmail: '[email protected]', }); } }

Identifying Signers

You can identify the target signer by email or external ID:

// By email await korala.documents.bulkSign({ documentIds, signerEmail: '[email protected]', }); // By external ID await korala.documents.bulkSign({ documentIds, signerExternalId: 'user_123', });

Provide exactly one of signerEmail or signerExternalId.

When one person holds several roles

A packet can legitimately list the same person more than once. An officer who signs for a company, for its parent guarantor and for a subsidiary appears as three signers sharing an email at signing orders 1, 2 and 3.

signerEmail matches all three. Korala takes the lowest signing order that has not signed or declined, so repeated calls walk the roles in order:

// Call 1 signs signing order 1 await korala.documents.bulkSign({ documentIds, signerEmail: '[email protected]' }); // Call 2 signs signing order 2, and so on await korala.documents.bulkSign({ documentIds, signerEmail: '[email protected]' });

Each call signs one signer per document, and the signing order rules below still apply.

To name one role instead of taking whichever comes next, give each signer a distinct externalId when you create the document, then address it with signerExternalId:

await korala.templates.createDocument(templateId, { name: 'Supply Agreement — Acme', signers: { Company: { name: 'J. Officer', email: '[email protected]', externalId: 'company' }, Guarantor: { name: 'J. Officer', email: '[email protected]', externalId: 'guarantor' }, Subsidiary: { name: 'J. Officer', email: '[email protected]', externalId: 'subsidiary' }, }, }); await korala.documents.bulkSign({ documentIds, signerExternalId: 'guarantor' });

externalId belongs to one signer, so it still identifies a single role when emails repeat, and it holds its meaning across every document you generate from the template.

IP Attribution

When your server calls the bulk sign API on behalf of end users, the audit trail records your server’s IP address. To preserve the end-user IP for compliance, pass it via the X-Client-IP header. Korala stores the value as claimedIpAddress in the audit trail alongside the verified network IP.

const result = await korala.documents.bulkSign( { documentIds, signerEmail: '[email protected]', }, { clientIp: endUserIpAddress }, );

Note: The X-Client-IP value must be a valid IP address (IPv4 or IPv6). Korala ignores invalid values. Korala validates the header’s format without verifying it; the recorded value is the partner’s assertion of the end-user’s IP.

Limits

  • Maximum 500 documents per bulk sign request
  • Documents must be in Pending status
  • The target signer must not have already signed or declined
  • Every signer at a lower signing order must have signed first, otherwise the document fails with Waiting for prior signers: ...

Response Format

{ "signed": 198, "failed": 2, "results": [ { "documentId": "doc-1", "success": true, "allSigned": true }, { "documentId": "doc-2", "success": true, "allSigned": false }, { "documentId": "doc-x", "success": false, "error": "Document not found" } ] }

When allSigned is true, Korala has queued the document for completion (cryptographic signing and timestamping). Listen for the document_completed webhook to know when the signed PDF is ready.

Last updated on