Converter

The Converter node converts data and files between formats without writing code. It is technology-neutral so more formats can be added over time.

The Converter node converts data and files between formats without writing code. It is technology-neutral so more formats can be added over time. It converts CSV, JSON, and XML data in both directions, runs Tesseract OCR to pull text out of images and PDFs, and rewrites a stored file in another format.

Overview

PropertyValue
Inputs1
Outputs1
Output$nodeLabel.result (parsed data for csvToJson / xmlToJson, text for jsonToCsv / jsonToXml, extracted text for imageToText / pdfToText, new file metadata for fileConvert)

Parameters

ParameterTypeDescription
labelstringNode identifier (camelCase)
conversionstringcsvToJson (CSV text → array of row objects), jsonToCsv (array of objects/rows → CSV text), xmlToJson (XML text → object), jsonToXml (object → XML text), imageToText (OCR an image), pdfToText (OCR every selected PDF page), or fileConvert (rewrite a stored file in another format)
sourceexpressionThe data to convert. Leave empty to use the node's first input
delimiterstringSingle-character field separator (default ,)
hasHeaderbooleancsvToJson only — treat the first row as the header (default true)
trimValuesbooleancsvToJson only — strip whitespace around header names and cell values (default true)
includeHeaderbooleanjsonToCsv only — write a header row (default true)
converterColumnsstringjsonToCsv only — optional comma-separated column order
converterFileIdexpressionFile conversions — the Heym Drive file to read, e.g. $Upload.file.id
converterTargetFormatstringfileConvert only — output format (pdf, docx, html, md, txt, csv, epub, jpg, png, bmp, webp)
ocrLanguagestringOCR only — auto (default), a Tesseract code such as tur, or custom
ocrLanguageCustomstringOCR only — language codes used when ocrLanguage is custom, e.g. eng+tur
ocrEncodingstringOCR only — charset the text is normalized to (default utf-8)
ocrNormalizeUnicodebooleanOCR only — apply NFC normalization (default true)
ocrPsmstringOCR only — Tesseract page segmentation mode (default 3)
ocrDpinumberpdfToText only — page rasterization DPI (default 300)
ocrPageRangeexpressionpdfToText only — 3 or 2-5. Empty means every page

Behavior

  • csvToJson parses the source CSV text. With hasHeader: true each row becomes an object keyed by the header values; with hasHeader: false each row becomes an array of cell values. Quoted fields, embedded delimiters, and embedded newlines are handled per RFC 4180. A leading UTF-8 BOM (common in Excel exports) is stripped, and duplicate header names are made unique without overwriting a real column (a, a, a_2a, a_3, a_2). Set delimiter to \t to parse tab-separated values.
  • trimValues (default true) strips surrounding whitespace from header names and cell values, including inside quoted fields (" padded "padded). RFC 4180 treats quoted whitespace as data, so set trimValues: false to preserve it exactly.
  • jsonToCsv builds CSV text from an array of objects (or arrays). Column order is taken from converterColumns when provided, otherwise inferred from the first object's keys. Values containing the delimiter, quotes, or newlines are quoted automatically.
  • xmlToJson parses XML into an object. Attributes use @ prefixes and text mixed with attributes uses #text; repeated sibling elements become arrays. XML entity expansion is disabled.
  • jsonToXml serializes an object to an XML document. The object must have exactly one top-level key, which becomes the XML root element. Use the same @attribute and #text convention as xmlToJson to produce attributes and text alongside child elements.
  • imageToText runs Tesseract over a single image (PNG, JPEG, TIFF, WebP, BMP).
  • pdfToText rasterizes each selected page at ocrDpi and recognizes it. Every page goes through OCR, so a digital PDF with a perfectly good text layer is still re-read from pixels. That keeps scanned and digital documents behaving identically, at the cost of speed, so narrow ocrPageRange on long files.
  • fileConvert rewrites the source file in another format and stores the result as a new Drive file. The original is never modified. Documents go through pandoc, images through Pillow, and JSON to CSV through the Python csv writer. An image cannot become a document and a document cannot become an image; to read text out of an image use imageToText.

Getting the file in

File conversions read from Heym Drive, not from a path or a URL, so the file has to be stored first. Three common chains:

  • File upload trigger → Converter — point converterFileId at $Upload.file.id.
  • Drive (downloadUrl) → Converter — fetch a remote file into Drive, then pass $Download.id.
  • Agent → Converter — a skill that writes a file exposes it as $reportAgent._generated_files[0].id.

converterFileId also accepts a whole file object ($Upload.file) or a Heym download URL; the file id is pulled out of either. The file must be one you own or one a teammate shared with you.

Languages

auto (the default) runs Tesseract's orientation-and-script detection first, then picks the best installed model for the detected script. Script models such as Latin or Cyrillic read every language written in that script, which is why auto handles a Turkish invoice and an English one without being told which is which.

Naming the language is still more accurate when you know it. Use a single code (tur), or join several with + (eng+tur) for mixed documents — more languages means slower and slightly noisier recognition, so keep the list short. Codes only work when the matching language data is installed on the backend; the node lists what is available when you pick a missing one.

Every Heym image ships osd, English, Turkish, German, French, Spanish, Italian, Portuguese, Dutch, Russian, Arabic, Simplified Chinese, Japanese, Korean, and the Latin, Cyrillic, Arabic, HanS, Japanese, and Hangul script models.

Encoding

Recognized text is UTF-8 and keeps every character Tesseract produced. ocrNormalizeUnicode (on by default) applies NFC normalization, so a letter written as s plus a combining cedilla becomes a single ş — worth leaving on, because the two forms are not equal in comparisons or database lookups.

ocrEncoding matters only when something downstream cannot store the full Unicode range. Picking cp1254 or iso-8859-9 keeps Turkish text intact while guaranteeing the result fits that charset; latin-1 and ascii are narrower and replace what they cannot represent with ?. Leave it at utf-8 unless a target system forces your hand.

Output

csvToJson, jsonToCsv, xmlToJson, and jsonToXml return $label.result and $label.conversion. OCR conversions add:

FieldDescription
$label.resultThe full extracted text, pages joined by a blank line
$label.languageThe model that actually ran (useful to see what auto chose)
$label.encodingThe charset the text was normalized to
$label.page_countNumber of pages recognized
$label.pagesArray of { page, text } for per-page handling
$label.file{ id, filename, mime_type, size_bytes } of the source file

fileConvert returns the new file instead:

FieldDescription
$label.idUUID of the newly stored file
$label.filenameConverted filename, base name kept and extension swapped
$label.mime_typeMIME type of the new file
$label.size_bytesSize of the new file
$label.download_urlDrive download URL for the new file
$label.source_fileMetadata of the file that was converted

Example

{
  "type": "converter",
  "data": {
    "label": "toRows",
    "conversion": "csvToJson",
    "source": "$userInput.body.text",
    "delimiter": ",",
    "hasHeader": true
  }
}

For input name,age\nAda,36, downstream nodes access the parsed rows via $toRows.result ([{ "name": "Ada", "age": "36" }]).

Converting XML to JSON:

{
  "type": "converter",
  "data": {
    "label": "xmlToObject",
    "conversion": "xmlToJson",
    "source": "$webhook.body"
  }
}

For <person id="1"><name>Ada</name></person>, $xmlToObject.result is { "person": { "@id": "1", "name": "Ada" } }. To create XML, pass that object to a second Converter node with conversion: "jsonToXml".

Reading a scanned invoice uploaded through a file upload trigger:

{
  "type": "converter",
  "data": {
    "label": "readInvoice",
    "conversion": "pdfToText",
    "converterFileId": "$Upload.file.id",
    "ocrLanguage": "tur",
    "ocrEncoding": "utf-8",
    "ocrDpi": 300,
    "ocrPageRange": "1-3"
  }
}

An Agent or LLM node downstream can then pull structured fields out of $readInvoice.result.

Turning an agent's markdown report into a PDF:

{
  "type": "converter",
  "data": {
    "label": "convertDoc",
    "conversion": "fileConvert",
    "converterFileId": "$reportAgent._generated_files[0].id",
    "converterTargetFormat": "pdf"
  }
}

Downstream nodes read $convertDoc.download_url to share the PDF, or $convertDoc.id to keep working with it in Drive.

Requirements

OCR shells out to tesseract and poppler's pdftoppm, and document conversion uses pandoc. All three ship with every way of running Heym, including run.sh, docker-compose, and the single release image, so there is nothing to configure. Page limits, timeouts, and the maximum DPI are fixed platform values rather than settings.