Documents
Documents are the core resource in Korala. This guide covers creating documents, managing their lifecycle, and retrieving signed copies.
Document Lifecycle
| Status | Description |
|---|---|
draft | Document created but not sent for signing |
pending | Sent to signers, awaiting signatures |
completed | All signatures collected, document sealed |
voided | Cancelled before completion |
expired | Expiration date passed before completion |
Creating a Document
Document creation is a two-step process:
- Get an upload URL - Request a pre-signed URL for uploading your PDF
- Confirm the upload - Tell Korala the upload is complete
TypeScript
import { KoralaClient } from '@korala/api-client';
import fs from 'fs';
const korala = new KoralaClient({
apiKeyId: 'your-api-key-id',
apiSecret: 'your-api-secret',
});
// Step 1: Get upload URL
const { documentId, uploadUrl, key } = await korala.documents.createUploadUrl({
filename: 'contract.pdf',
contentType: 'application/pdf',
});
// Step 2: Upload the file
const pdfBuffer = fs.readFileSync('contract.pdf');
await fetch(uploadUrl, {
method: 'PUT',
body: pdfBuffer,
headers: { 'Content-Type': 'application/pdf' },
});
// Step 3: Confirm the upload
const document = await korala.documents.confirmUpload(documentId);
console.log(`Document created: ${document.id}`);
console.log(`Status: ${document.status}`); // 'draft'Sending for Signatures
Once you’ve added signers and fields, send the document:
TypeScript
// Add signers first
const signer = await korala.signers.create(documentId, {
email: '[email protected]',
name: 'John Doe',
});
// Add signature fields
await korala.fields.create(documentId, {
signerId: signer.id,
fieldType: 'signature',
pageNumber: 1,
xPosition: 100,
yPosition: 500,
width: 200,
height: 50,
});
// Send for signing
const document = await korala.documents.send(documentId, {
electronicDisclosure: {
id: 'acme-electronic-signing-terms',
version: '2026-09-01',
title: 'Electronic signature disclosure',
contentHtml: '<p>Contact Acme Legal for a paper copy.</p>',
},
});
console.log(`Status: ${document.status}`); // 'pending'electronicDisclosure is optional. Use it when your organization needs custom
paper-copy, withdrawal, fee, or contact terms. Korala sanitizes the HTML and
freezes the profile when you send the document. The final signing action links
to the stored content; later edits cannot change it. Korala records the profile
ID, version, and content hash in the audit trail and Certificate of Completion.
Saving a reusable signature
In both the single-document and packet signing viewers, a signer can draw or type a signature using their signing link. To reuse a saved signature or select Save for future, they must first verify their email through the signer portal. A forwarded signing link does not grant access to saved signatures.
Signatures and initials are saved separately. A signature saved while signing one document is available in packets from the same organization, and vice versa. Changing a saved signature does not change an authorized packet action.
Reminders and Expiration
Korala can nudge idle signers and close out stale documents automatically, so you don’t have to chase people by hand.
Expiration. Pass expiresInDays when sending to set a deadline; if unsigned by then, the document moves to expired (a terminal state, like voided), signer links stop working, and a document.expired webhook fires. Omit it to fall back to your organization’s default expiry (defaultExpirationDays in org settings), or to never expire if neither is set.
// Expire this document if unsigned after 14 days.
const document = await korala.documents.send(documentId, { expiresInDays: 14 });Reminders. Enable reminders once at the organization level; every unsigned signer whose turn it is (sequential order is respected) is then emailed on a cadence, up to a cap.
await korala.organizations.updateSettings({
reminderEnabled: true,
reminderIntervalDays: 3, // default 3
maxReminders: 3, // default 3
defaultExpirationDays: 30,
});Reminders and expiry are evaluated by a background scheduler, so they take effect within minutes of becoming due rather than exactly on the second. Documents sent with suppressNotifications (or in sandbox) are never reminded. Each reminder and the expiry are recorded in the audit trail.
Retrieving Documents
Get a Single Document
TypeScript
const document = await korala.documents.get(documentId);
console.log(`Name: ${document.name}`);
console.log(`Status: ${document.status}`);
console.log(`Original PDF: ${document.originalFileUrl}`);
if (document.status === 'completed') {
console.log(`Signed PDF: ${document.signedFileUrl}`);
console.log(`Certificate: ${document.certificateFileUrl}`);
console.log(`Completed: ${document.completedAt}`);
}List All Documents
TypeScript
// List all documents
const documents = await korala.documents.list();
// Filter by status
const pendingDocs = await korala.documents.list({ status: 'pending' });
// Pagination
const page2 = await korala.documents.list({ limit: 10, offset: 10 });Downloading Files
Completed documents have three files:
| File | Description |
|---|---|
originalFileUrl | The original uploaded PDF |
signedFileUrl | PDF with visual signatures and cryptographic seal |
certificateFileUrl | Certificate of Completion (audit trail) |
TypeScript
const document = await korala.documents.get(documentId);
if (document.status === 'completed') {
// Download signed PDF
const signedPdf = await fetch(document.signedFileUrl);
fs.writeFileSync('signed-contract.pdf', await signedPdf.buffer());
// Download certificate
const certificate = await fetch(document.certificateFileUrl);
fs.writeFileSync('certificate.pdf', await certificate.buffer());
}Voiding a Document
Cancel a document before it’s completed:
TypeScript
const document = await korala.documents.void(documentId);
console.log(`Status: ${document.status}`); // 'voided'Voiding a document is permanent. Korala notifies signers that you cancelled the document.
Response Format
{
"id": "doc_abc123",
"name": "contract.pdf",
"status": "completed",
"originalFileUrl": "https://storage.example.com/original.pdf",
"signedFileUrl": "https://storage.example.com/signed.pdf",
"certificateFileUrl": "https://storage.example.com/certificate.pdf",
"expiresAt": null,
"completedAt": "2024-01-15T10:30:00Z",
"createdAt": "2024-01-14T09:00:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}Organize with collections
Documents can belong to several optional collections. Supply collectionIds
when requesting an upload URL, or organize existing documents in bulk. Filter
document lists by collectionId, unassigned, search, and status.
See Collections for scope, archive behavior, request limits, and API and SDK examples.