TypeScript API Client
The official TypeScript API client provides a type-safe interface for the Korala API.
Installation
npm install @korala/api-client
# or
yarn add @korala/api-client
# or
pnpm add @korala/api-clientQuick Start
import { KoralaClient } from '@korala/api-client';
const korala = new KoralaClient({
apiKeyId: process.env.KORALA_API_KEY_ID!,
apiSecret: process.env.KORALA_API_SECRET!,
});
// Create a document
const { documentId, uploadUrl } = await korala.documents.createUploadUrl({
filename: 'contract.pdf',
contentType: 'application/pdf',
});
// Upload the file to S3
await fetch(uploadUrl, {
method: 'PUT',
body: pdfBuffer,
headers: { 'Content-Type': 'application/pdf' },
});
// Confirm the upload
await korala.documents.confirmUpload(documentId);
// Add a signer
const signer = await korala.signers.create(documentId, {
email: '[email protected]',
name: 'John Doe',
});
// Add a signature field
await korala.fields.create(documentId, {
signerId: signer.id,
fieldType: 'signature',
pageNumber: 1,
xPosition: 100,
yPosition: 500,
width: 200,
height: 50,
});
// Send for signing
await korala.documents.send(documentId);Configuration
interface KoralaClientConfig {
// Required
apiKeyId: string; // API key ID
apiSecret: string; // API secret for HMAC signing
// Optional
baseUrl?: string; // API base URL (default: https://api.korala.ai/api/v1)
timeout?: number; // Request timeout in ms (default: 30000)
}Documents
Create Upload URL
const { documentId, uploadUrl, key } = await korala.documents.createUploadUrl({
filename: 'contract.pdf',
contentType: 'application/pdf',
});
// Upload the file to the presigned URL
await fetch(uploadUrl, {
method: 'PUT',
body: pdfBuffer,
headers: { 'Content-Type': 'application/pdf' },
});
// Confirm upload
await korala.documents.confirmUpload(documentId);Get Document
const document = await korala.documents.get(documentId);
console.log(document.status);
console.log(document.originalFileUrl);
console.log(document.signedFileUrl);List Documents
const documents = await korala.documents.list();Send Document
await korala.documents.send(documentId);Void Document
await korala.documents.void(documentId);Get Audit Trail
const events = await korala.documents.getAuditTrail(documentId);Bulk Sign
Sign multiple documents at once using a saved signature:
const result = await korala.documents.bulkSign({
documentIds: ['doc-1', 'doc-2', 'doc-3'],
signerEmail: '[email protected]',
autoFillDateValue: '2026-02-18', // optional, defaults to today
});
console.log(`Signed: ${result.signed}, Failed: ${result.failed}`);
for (const doc of result.results) {
if (!doc.success) {
console.error(`${doc.documentId}: ${doc.error}`);
}
}IP Attribution
When calling bulk sign on behalf of an end user, pass their IP for audit trail attribution:
const result = await korala.documents.bulkSign(
{
documentIds: ['doc-1', 'doc-2'],
signerEmail: '[email protected]',
},
{ clientIp: '203.0.113.42' },
);See the Bulk Signing guide for the complete workflow.
Signing Packets
Create an authenticated packet-management client, then send one recipient to Korala to review and authorize up to 50 documents:
const packet = await korala.signingPackets.create({
name: 'Series A closing',
externalId: 'closing-2026-0142',
recipient: { name: 'Alex Manager', email: '[email protected]' },
items: [
{ documentId: subscription.id, signerId: subscriptionSigner.id },
{ documentId: sideLetter.id, signerId: sideLetterSigner.id },
],
redirectUrl: 'https://fund.example.com/closing/complete',
});
const sent = await korala.signingPackets.send(packet.id, {
delivery: 'email',
});
console.log(sent.signingUrl, sent.deliveryStatus);The API key can prepare, send, inspect, remind, void, and retrieve audit data. The recipient must verify their email and complete the packet in Korala. See the Signing Packets guide for state handling, webhooks, audit evidence, and limits.
Signatures
Manage saved signatures for signers. Bulk sign uses saved signatures to auto-fill Signature and Initials fields.
Create Signature
const signature = await korala.signatures.create({
email: '[email protected]',
name: 'Jane Smith',
signatureImageUrl: 'data:image/png;base64,...',
isDefault: true, // optional, defaults to true
});List Signatures
// List all
const signatures = await korala.signatures.list();
// Filter by email
const ceoSignatures = await korala.signatures.list({ email: '[email protected]' });Get Signature
const signature = await korala.signatures.get(signatureId);Delete Signature
await korala.signatures.delete(signatureId);Signers
Add Signer
const signer = await korala.signers.create(documentId, {
email: '[email protected]',
name: 'John Doe',
signingOrder: 1, // optional
});List Signers
const signers = await korala.signers.list(documentId);Remove Signer
await korala.signers.delete(documentId, signerId);Fields
Add Field
const field = await korala.fields.create(documentId, {
signerId: signer.id,
fieldType: 'signature', // 'signature' | 'initials' | 'date' | 'text' | 'checkbox'
pageNumber: 1,
xPosition: 100,
yPosition: 500,
width: 200,
height: 50,
});List Fields
const fields = await korala.fields.list(documentId);Remove Field
await korala.fields.delete(documentId, fieldId);Webhooks
Create Webhook
const webhook = await korala.webhooks.create({
url: 'https://your-app.com/webhooks/korala',
events: ['document_completed', 'document_signed'],
});
// Save the secret for signature verification!
console.log(webhook.secret);List Webhooks
const webhooks = await korala.webhooks.list();Get Webhook
const webhook = await korala.webhooks.get(webhookId);Update Webhook
await korala.webhooks.update(webhookId, {
events: ['document_completed'],
isActive: true,
});Delete Webhook
await korala.webhooks.delete(webhookId);Get Delivery History
const deliveries = await korala.webhooks.getDeliveries(webhookId);Send Test Event
const delivery = await korala.webhooks.sendTestEvent(webhookId);Error Handling
import { KoralaError, KoralaValidationError } from '@korala/api-client';
try {
await korala.documents.send(documentId);
} catch (error) {
if (error instanceof KoralaValidationError) {
console.error('Validation failed:', error.errors);
} else if (error instanceof KoralaError) {
console.error(`API error ${error.status}: ${error.message}`);
} else {
throw error;
}
}TypeScript Types
The client exports types for all responses:
import type {
DocumentDto,
DocumentStatus,
SignerDto,
SignerStatus,
FieldDto,
FieldType,
WebhookDto,
WebhookEventType,
WebhookPayloadDto,
// Event-specific data types
DocumentCreatedEventData,
DocumentSentEventData,
DocumentViewedEventData,
DocumentSignedEventData,
DocumentCompletedEventData,
DocumentFailedEventData,
DocumentVoidedEventData,
DocumentDeclinedEventData,
TestEventData,
} from '@korala/api-client';
function handleDocument(doc: DocumentDto) {
if (doc.status === 'completed') {
console.log(`Completed at: ${doc.completedAt}`);
}
}
function handleWebhook(payload: WebhookPayloadDto) {
switch (payload.event) {
case 'document_signed': {
const data = payload.data as DocumentSignedEventData;
console.log(`Signer ${data.signerId} signed, all done: ${data.allSigned}`);
break;
}
case 'document_completed': {
const data = payload.data as DocumentCompletedEventData;
console.log(`Document completed at ${data.completedAt}`);
break;
}
}
}Webhook Verification
The client includes a helper for verifying webhook signatures:
import { verifyWebhookSignature } from '@korala/api-client';
app.post('/webhooks/korala', express.raw({ type: 'application/json' }), (req, res) => {
const isValid = verifyWebhookSignature({
secret: process.env.WEBHOOK_SECRET!,
signature: req.headers['x-signature'] as string,
timestamp: req.headers['x-timestamp'] as string,
body: req.body.toString(),
});
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body.toString());
// Process event...
res.json({ received: true });
});Collections and paginated lists
Use client.collections to create, find, archive, restore, and organize
collections. See the collections workflow for external-ID
reconciliation, explicit creation destinations, and packet preparation.
Use client.documents.listPage(query) for the pagination envelope
{ data, total, page, limit, totalPages }. The deprecated documents.list()
now returns the first page’s items, correcting its previous mismatch between
the declared array and the server’s envelope. It does not fetch every page.
templates.list(query) already returns an envelope; templates.listPage(query)
is an alias. Both resources accept collection and unassigned filters.