Collections
Use collections to group documents and templates by client, project, or matter. You can leave a resource unassigned or add it to several collections. You keep the same resource when you add or remove a membership.
Collections belong to your organization and are separate in live and test mode.
API requests use the key’s mode. Dashboard requests can select
sandbox=true; omitting it selects live mode. Collections grant no additional
access and are not visible to signers.
Find collections in the dashboard
Open Documents → Collections to organize related documents and templates. Select prepared documents and choose Send for signing to prepare a request. Use Documents → Signing requests to track individual and grouped requests. A collection shows its documents, templates, and related signing requests. Use its actions menu to edit, archive, or restore it.
The All collections control above the Documents and Templates tables filters by collection or shows unassigned work. Select All collections inside the control to clear the filter.
On a document or template, open Details and use Edit in the Collections panel to change its memberships. Removing a membership keeps the resource. During creation, choose collections alongside the name after selecting a file.
Create or retrieve a collection
Use an external reference to reconcile a retried creation request. Choose a case-sensitive external ID and preserve its whitespace. You cannot
change it after creation or reuse it within the same organization and mode,
even after archiving the collection. A duplicate creation returns 409 Conflict; retrieve and inspect
the existing record rather than overwriting it.
import { KoralaClient, KoralaError, DocumentStatus } from '@korala/api-client';
const client = new KoralaClient({
apiKeyId: process.env.KORALA_API_KEY_ID!,
apiSecret: process.env.KORALA_API_SECRET!,
});
const externalId = 'crm:client:acme';
const collection = await client.collections.create({
name: 'Acme onboarding',
externalId,
metadata: { project: 'onboarding' },
}).catch(async error => {
if (error instanceof KoralaError && error.status === 409) {
return client.collections.byExternalId(externalId);
}
throw error;
});The equivalent API operations are POST /api/v1/collections and
GET /api/v1/collections/by-external-id?externalId=crm%3Aclient%3Aacme.
Korala trims surrounding whitespace from names and requires 1–255 characters. Descriptions allow up to
2,000 characters; external IDs allow 1–255. Metadata must be a JSON object of
at most 16 KiB when encoded as UTF-8 JSON. Updating metadata replaces the object.
Put new work in a collection
Supply collectionIds when requesting a document or template upload URL, or
when creating a document from a template. Resource creation and assignment
commit together. An invalid or archived destination fails creation.
const upload = await client.documents.createUploadUrl({
filename: 'agreement.pdf',
contentType: 'application/pdf',
collectionIds: [collection.id],
});
// PUT the PDF bytes to upload.uploadUrl, then confirm the upload.
await client.documents.confirmUpload(upload.documentId);
const document = await client.templates.createDocument(templateId, {
name: 'Acme offer',
signers: { Employee: { name: 'Alex', email: '[email protected]' } },
collectionIds: [collection.id],
});Choose destinations for each generated document; template memberships do not
carry over. Omitting collectionIds or supplying
[] creates an unassigned resource. A reused template can have a different mode
from the generated document; destinations must match the document’s mode.
In the dashboard, creation flows started from a collection preselect it. Clear or change the selection before submitting to choose another destination.
Organize and find existing resources
await client.collections.addDocuments(collection.id, [document.id]);
await client.collections.addTemplates(collection.id, [templateId]);
const page = await client.documents.listPage({
collectionId: collection.id,
status: DocumentStatus.Pending,
search: 'offer',
includeCollections: true,
page: 1,
limit: 20,
});
const unassigned = await client.templates.list({ unassigned: true });
await client.collections.removeDocuments(collection.id, [document.id]);Membership operations accept 1–100 distinct resource IDs. Repeated add/remove requests have the same result; an invalid resource rejects the whole request. Creation accepts up to 100 distinct destination collection IDs. These are request limits, not lifetime membership limits.
Collection lists default to active records. Use
archiveStatus=active|archived|all, search, page, and limit. Collection and
member endpoints default to 20 rows and cap pages at 100. Search is a literal,
case-insensitive substring match; filters run before counting and pagination.
Document and template lists support collectionId, collectionIds, or
unassigned=true; these filters cannot be combined. collectionIds accepts
1–100 distinct UUIDs and matches membership in any selected collection.
Each resource appears once, including in pagination totals. All collection IDs
must belong to the authenticated organization and mode; an invalid or
inaccessible collection rejects the request. Archived memberships are included.
const page = await client.documents.listPage({
collectionIds: [clientCollection.id, projectCollection.id],
search: 'offer',
status: DocumentStatus.Pending,
});HTTP clients can send ?collectionIds=UUID1,UUID2 or repeated
collectionIds=UUID1&collectionIds=UUID2 parameters. Search, status, and other
filters still apply to the resulting documents. The SDK serializes arrays for you.
Use client.documents.listPage({ collectionIds: [...] }) or
client.templates.listPage({ collectionIds: [...] }) to search several collections.
client.collections.listDocuments(id, query) and listTemplates(id, query)
search the one collection named by id; their query accepts resource filters
such as search and status/activity, but no collection assignment filters.
Authenticated detail responses include collectionIds. List responses include
it when includeCollections=true; an empty array means unassigned. Member lists
use /collections/:id/documents and /collections/:id/templates.
Create a signing packet
Prepare and send individual documents for signing before selecting them for a packet. Collection templates must first generate documents with signer records.
const eligible = await client.signingPackets.listEligibleDocuments({
collectionId: collection.id,
recipientEmail: '[email protected]',
});
// Review the documents and resolve the exact signer record for each one.
const reviewedItems = eligible.data.map(document => ({
documentId: document.id,
signerId: document.signers[0].id,
}));
const packet = await client.signingPackets.create({
name: 'Acme onboarding signatures',
sourceCollectionId: collection.id,
recipient: { name: 'Alex', email: '[email protected]' },
items: reviewedItems,
});
const history = await client.signingPackets.list({
sourceCollectionId: collection.id,
});Review the final selection before creating the packet. Existing packet rules apply, including mode, recipient, eligibility, and the 50-document limit. The server revalidates membership at creation. Changes require another review; the server does not silently remove selected documents. Send the packet through the existing packet workflow.
Use sourceCollectionId to trace a packet to its collection. Later membership,
archive, or name changes leave the packet’s selected items unchanged. Source
revisions still freeze at send. Only authenticated management responses expose
the origin link.
Archive and restore
await client.collections.archive(collection.id);
await client.collections.restore(collection.id);Archiving preserves members and packet history, and existing signing continues. You can read and edit archived collections and remove their members. Restore before adding members or creating a new packet. Permanent collection deletion is not available.
A template’s mode cannot change while it has any collection memberships, including archived ones. Remove memberships first or duplicate the template into the other mode.