Intake scripts run server-side JavaScript on every document uploaded through an intake endpoint. They execute after metadata is merged but before the document is stored, giving you a chance to inspect the content, enrich metadata, reject invalid uploads, and control which tasks are created.
How It Works
When a document is uploaded to an intake:
- Metadata is merged (intake config + document metadata + upload params)
- Your script runs with access to filename, content, and metadata
- Based on the script return value, the upload is accepted or rejected
- If accepted, the document is stored and tasks are created per the script’s instructions
Scripts run in a sandboxed Goja JavaScript runtime with a 5-second timeout. They cannot make network requests, access the filesystem, or call platform APIs — they operate purely on the data provided to them.
Available Variables
| Variable | Type | Description |
|---|
filename | string | Original uploaded filename |
fileSize | number | File size in bytes |
mimeType | string | Detected MIME type of the file |
metadata | object | Mutable metadata object. Merged from: intake config < document metadata < upload params |
document.text | string | Extracted text content (first 5 pages for PDFs) |
document.pageCount | number | Number of pages (if detectable) |
document.metadata | object | Read-only document-level metadata from the file |
log(level, msg) | function | Write to server logs. Levels: debug, info, warn, error |
labels | string | Comma-separated label names from the upload request |
statusId | string | Document status ID from the upload request |
externalData | string | Raw external data JSON string from the upload request. After intake processing, this data is injected into the KDDB document under key "default" and accessible via doc.get_external_data(). |
documentVersion | string | Document version from the upload request |
Return Value
The script must return an object. All fields are optional:
({
metadata: metadata, // Modified metadata object
reject: false, // Reject the upload (HTTP 400)
rejectReason: "", // Reason shown to caller
taskTemplates: [] // Tasks to create (see below)
})
If the script returns nothing (no return statement), the upload proceeds with the original metadata and falls back to the static task template.
Task Templates
The taskTemplates array gives scripts full control over which tasks are created for each document.
Behavior
| Script returns | Result |
|---|
taskTemplates: [{slug: "..."}] | Creates tasks from those templates |
taskTemplates: [] | Creates no tasks (overrides static template) |
No taskTemplates field | Falls back to static task template |
Template Reference Fields
| Field | Type | Required | Default |
|---|
slug | string | Yes | — |
priority | number | No | Template default |
teamSlug | string | No | Template default |
assigneeEmail | string | No | Unassigned |
title | string | No | Filename or AI naming |
description | string | No | Empty |
metadata | object | No | {} |
Example: Route by Document Type
var templates = [];
if (document.text.indexOf("INVOICE") >= 0) {
templates.push({
slug: "invoice-review",
priority: 2,
teamSlug: "finance-team",
title: "Invoice: " + filename
});
} else if (document.text.indexOf("CONTRACT") >= 0) {
templates.push({
slug: "legal-review",
teamSlug: "legal-team"
});
}
({
metadata: metadata,
taskTemplates: templates
})
Example: Multiple Tasks from One Document
// Every document gets a processing task
var templates = [
{ slug: "document-processing" }
];
// High-priority documents also get a review task
if (metadata.priority === "urgent") {
templates.push({
slug: "urgent-review",
priority: 1,
title: "URGENT: " + filename
});
}
({
metadata: metadata,
taskTemplates: templates
})
Example: Conditional Skip
// Only create tasks for PDFs over 1MB
if (mimeType === "application/pdf" && fileSize > 1024 * 1024) {
({
metadata: metadata,
taskTemplates: [{ slug: "large-pdf-review" }]
});
} else {
// No task needed — just store the document
({
metadata: metadata,
taskTemplates: []
});
}
Validation and Rejection
Return reject: true to refuse the upload before it is stored:
// Reject files over 100MB
if (fileSize > 100 * 1024 * 1024) {
({
metadata: metadata,
reject: true,
rejectReason: "File exceeds 100MB limit"
});
}
// Reject non-PDF files
if (!filename.endsWith(".pdf")) {
({
metadata: metadata,
reject: true,
rejectReason: "Only PDF files are accepted"
});
}
({
metadata: metadata,
reject: false
})
The caller receives an HTTP 400 with the rejection reason:
{
"error": "SCRIPT_REJECTED",
"rejectReason": "File exceeds 100MB limit",
"filename": "huge-file.bin"
}
The metadata object is mutable. Any changes are persisted on the document family:
metadata.source = "intake";
metadata.receivedAt = new Date().toISOString();
metadata.classification = document.text.indexOf("CONFIDENTIAL") >= 0
? "confidential"
: "general";
({
metadata: metadata,
reject: false
})
API Response
When tasks are created, the upload response includes task IDs:
{
"id": "doc-family-id",
"path": "invoice.pdf",
"taskId": "task-id-1",
"taskIds": ["task-id-1", "task-id-2"]
}
| Field | Description |
|---|
taskId | ID of the first task created (backward compatible) |
taskIds | Array of all task IDs created |
taskId is included for backward compatibility when exactly one task is created. Always use taskIds for new integrations.
Shared Modules
Load reusable JavaScript modules using the Module Refs picker in the Script tab. Selected modules execute before your script, making their functions available in global scope:
// Assuming "my-org/doc-classifier" module provides classifyDocument()
var docType = classifyDocument(document.text);
var templates = [];
if (docType.templates) {
docType.templates.forEach(function(t) {
templates.push({ slug: t });
});
}
({
metadata: metadata,
taskTemplates: templates
})
Limitations
- 5-second timeout — scripts that exceed this are terminated and the upload fails
- No network access — scripts cannot make HTTP requests or call external APIs
- No filesystem access — scripts operate only on the provided variables
- Text extraction — only the first 5 pages of PDFs are extracted; other file types may not have text available
- Goja runtime — supports ES5.1 JavaScript with some ES6+ features available