| name | devonthink |
|---|---|
| description | Automate DEVONthink on macOS via JXA (JavaScript for Automation) and Python. Use this skill when: - Creating, searching, organizing, or managing records in DEVONthink databases - Working with documents, notes, bookmarks, groups, PDFs, or web archives - Adding/removing tags, moving/duplicating/replicating records - Converting documents between formats (markdown, PDF, HTML, rich text) - Importing files/URLs or exporting records - Using DEVONthink's AI features (classification, similarity, summarization) - Querying record metadata, content, or properties - Looking up citation keys from bibliography JSON exports - Finding DEVONthink records by citation key Triggers: "DEVONthink", "DT3", "DEVONthink database", "organize documents", "document management", "citation key", "bibliography", "Zotero" |
Automate DEVONthink via JXA scripts executed with osascript -l JavaScript.
osascript -l JavaScript << 'EOF'
(() => {
const app = Application("DEVONthink");
app.includeStandardAdditions = true;
// Your code here
return JSON.stringify({ success: true });
})();
EOFGiven a DEVONthink record, find its Zotero citation key:
# Using the record's file path
python3 scripts/bib_lookup.py --path "~/Library/Mobile Documents/com~apple~CloudDocs/Zotero/Pastoralism/journalArticle/Gkartzios - 2023 - Editorial. Counterurbanisation, Again.pdf"Output:
{
"success": true,
"citationKey": "gkartzios2023",
"title": "Editorial. Counterurbanisation, again..."
}Given a citation key (e.g., @gkartzios2023), find the matching DEVONthink record:
python3 scripts/bib_lookup.py --citation-key "gkartzios2023" --find-devonthinkOutput:
{
"success": true,
"citationKey": "gkartzios2023",
"title": "Editorial. Counterurbanisation, again...",
"devonthinkRecords": [
{
"uuid": "E1DF0C33-D4C8-4ECA-9890-81B012EFDD49",
"name": "Gkartzios - 2023 - Editorial. Counterurbanisation, Again",
"path": "~/Library/Mobile Documents/com~apple~CloudDocs/Zotero/Pastoralism/journalArticle/Gkartzios - 2023 - Editorial....pdf",
"recordType": "PDF document"
}
]
}Read the full text of any record (PDF, markdown, HTML, etc.). DEVONthink automatically extracts/OCRs text from PDFs.
osascript -l JavaScript << 'EOF'
(() => {
const app = Application("DEVONthink");
try {
const record = app.getRecordWithUuid("E1DF0C33-D4C8-4ECA-9890-81B012EFDD49");
if (!record) {
return JSON.stringify({ success: false, error: "Record not found" });
}
const result = {};
result["name"] = record.name();
result["recordType"] = record.recordType();
result["plainText"] = record.plainText();
return JSON.stringify(result);
} catch (e) {
return JSON.stringify({ success: false, error: e.toString() });
}
})();
EOFTip: Combine workflows 2 and 3 to read a document by citation key:
bib_lookup.py --citation-key "gkartzios2023" --find-devonthink→ get UUID- Use UUID in JXA script → get full text content
const results = app.search("invoice 2024", { in: app.currentDatabase() });
// Filter: kind:pdf, kind:markdown, kind:!group, name:~keyword
// Dates: created:Yesterday, created:#3days, created>=2026-01-01const record = app.createRecordWith({
name: "New Note",
type: "markdown", // or: group, bookmark, formatted note, txt, rtf
content: "# Hello\n\nContent here"
}, { in: app.currentGroup() });const record = app.getRecordWithUuid("UUID-HERE");
// Or by ID (requires database context):
const db = app.databases().find(d => d.name() === "MyDB");
const record = db.getRecordWithId(12345);app.move({ record: theRecord, to: destinationGroup });
app.duplicate({ record: theRecord, to: destinationGroup });
app.replicate({ record: theRecord, to: destinationGroup }); // Same DB onlyconst tags = record.tags(); // Get tags
record.tags = ["tag1", "tag2"]; // Set tags (replaces all)
// Add a tag
const current = record.tags();
record.tags = [...current, "newTag"];
// Remove a tag
record.tags = record.tags().filter(t => t !== "oldTag");app.delete({ record: theRecord });record.name = "New Name";const text = record.plainText(); // Plain text content
const rich = record.richText(); // Rich text content
const html = record.source(); // HTML source (for HTML/webarchive)record.plainText = "New content"; // For text-based records
record.source = "<html>...</html>"; // For HTML recordsconst selected = app.selectedRecords();
selected.forEach(r => {
// Process each selected record
});const group = app.getRecordWithUuid("GROUP-UUID");
const children = group.children();
children.forEach(child => {
// Process each child record
});| Property | Type | R/W | Description |
|---|---|---|---|
id |
Number | R | Database-specific ID |
uuid |
String | R | Globally unique identifier |
name |
String | RW | Record name |
path |
String | R | Filesystem path |
location |
String | R | DEVONthink location path |
recordType |
String | R | group, markdown, PDF document, etc. |
plainText |
String | RW | Plain text content |
richText |
RichText | RW | Rich text content |
source |
String | RW | HTML/XML source |
tags |
Array | RW | Record tags |
creationDate |
Date | RW | Creation date |
modificationDate |
Date | RW | Last modified |
size |
Number | R | Size in bytes |
comment |
String | RW | Record comment |
label |
Number | RW | Label index (0-7) |
rating |
Number | RW | Rating (0-5) |
flag |
Boolean | RW | Flagged state |
unread |
Boolean | RW | Unread state |
group, smart group, markdown, txt, rtf, rtfd, formatted note, HTML, webarchive, PDF document, picture, multimedia, bookmark, feed, sheet, XML, unknown
app.convert({ record: theRecord, to: "markdown" });
// Formats: simple, rich, note, markdown, HTML, webarchive,
// PDF document, single page PDF documentapp.createMarkdownFrom("https://example.com", { in: app.currentGroup() });
app.createPDFDocumentFrom("https://example.com", { in: app.currentGroup() });
app.createFormattedNoteFrom("https://example.com", { in: app.currentGroup() });
app.createWebDocumentFrom("https://example.com", { in: app.currentGroup() });// Classification suggestions
const proposals = app.classify({ record: theRecord });
// Find similar records
const similar = app.compare({ record: theRecord });
// Chat/summarize (requires AI config in DEVONthink)
const response = app.getChatResponseForMessage("Summarize this", {
record: theRecord, temperature: 0
});const dbs = app.databases(); // All open databases
const db = app.currentDatabase(); // Current database
const root = db.root(); // Root group
const incoming = db.incomingGroup(); // Inbox
const trash = db.trashGroup(); // Trashapp.lookupRecordsWithFile("report.pdf", { in: db });
app.lookupRecordsWithTags(["important", "work"], { in: db });
app.lookupRecordsWithComment("review needed", { in: db });
app.lookupRecordsWithURL("https://example.com", { in: db });
app.lookupRecordsWithPath("/path/to/file.pdf", { in: db });
app.lookupRecordsWithContentHash("hash-value", { in: db });app.addCustomMetaData("John Doe", { for: "author", to: record });
const author = app.getCustomMetaData({ for: "author", from: record });Look up citation keys from Zotero CSL JSON exports using the bundled Python script.
python3 scripts/bib_lookup.py --path "/path/to/document.pdf"python3 scripts/bib_lookup.py --citation-key "smith2024"python3 scripts/bib_lookup.py --citation-key "smith2024" --find-devonthinkOptions:
--path: File path to look up--citation-key: Citation key to look up--bib-json: Override path to Zotero JSON export (or setBIBLIOGRAPHY_JSONenv var)--find-devonthink: Also search DEVONthink for matching records--full: Include complete bibliography item in output
- Build objects with bracket notation (avoid inline object literals in returns):
const result = {};
result["success"] = true;
result["data"] = someData;
return JSON.stringify(result);-
Never use console.log - causes stdio errors
-
Wrap in try-catch and return JSON:
try {
// operations
return JSON.stringify({ success: true, data: result });
} catch (e) {
return JSON.stringify({ success: false, error: e.toString() });
}- DEVONthink paths vs filesystem paths: Use
location()for DEVONthink internal paths,path()for filesystem paths.
kind:pdf # By type
kind:!group # Exclude groups
name:~invoice # Name contains
created:Yesterday # Date shortcuts
created:#3days # Last 3 days
created>=2026-01-01 # Date range
kind:pdf created:#1week # Combined
- Full JXA API: See references/jxa-api.md for complete command and property reference