Skip to Content
SourcesCustom Connector

Custom Connector

Custom Connector

Write the connector yourself in a Python notebook, for a system nothing else covers.

Category
Other
Source type
CUSTOM
Produces
recorddocumentpagefiletable

Every organisation has the one system nothing supports — an internal API, a mainframe export, a vendor portal, a homegrown store. A custom connector is a small Python notebook you write in the app that turns that system into a normal source: it produces assets, and everything downstream treats them like any other.

Start here: Custom Connectors walks through writing one, and the notebook reference documents every function and field.

What you need to connect

Nothing but the notebook. Whatever your connector needs — a base URL, an account id — goes in variables; whatever must stay secret goes in secrets, encrypted exactly like any other source credential and redacted from logs and cell output.

The notebook defines a handful of plain functions: one to test the connection, one to yield assets, and optionally one to declare relationships.

What Classifyre reads

Whatever you yield. Assets can be records, documents, pages, files or tables, with free-form metadata. Return file bytes and you get the whole file pipeline for free — format detection, text extraction, OCR, archive expansion.

Files your connector yields are read with the shared file pipeline: see Supported File Formats for everything it can open, and OCR & Transcription for reading text out of images, audio and video.

Metadata on every asset

Asset kind · record

FieldTypeAlways presentWhat it is
external_idstringNoIdentifier of the record in the system the notebook read from
collectionstringNoContainer the record came from (table, index, folder, board, ...)
authorstringNoWho created the record, if known

Asset kind · document

FieldTypeAlways presentWhat it is
external_idstringNoIdentifier of the record in the system the notebook read from
collectionstringNoContainer the record came from (table, index, folder, board, ...)
authorstringNoWho created the record, if known
encodingstringNoText encoding of the content
word_countintegerNoWords in the extracted text

Asset kind · page

FieldTypeAlways presentWhat it is
external_idstringNoIdentifier of the record in the system the notebook read from
collectionstringNoContainer the record came from (table, index, folder, board, ...)
authorstringNoWho created the record, if known
encodingstringNoText encoding of the content
titlestringNoPage title

Asset kind · file

FieldTypeAlways presentWhat it is
external_idstringNoIdentifier of the record in the system the notebook read from
collectionstringNoContainer the record came from (table, index, folder, board, ...)
authorstringNoWho created the record, if known
size_bytesintegerNoSize of the file in bytes
mime_typestringNoContent type of the file

Asset kind · table

FieldTypeAlways presentWhat it is
external_idstringNoIdentifier of the record in the system the notebook read from
collectionstringNoContainer the record came from (table, index, folder, board, ...)
authorstringNoWho created the record, if known
row_countintegerNoRows in the result set
columnsstring[]NoColumns as [{name, type}] objects

Lineage

Lineage

Custom connectors can declare every relationship class — lineage, containment, identity, reference and usage — including column-level field mappings, and including edges that point at objects in systems this connector doesn’t scan. That last part is how a connector for your in-house pipeline can wire its outputs to the warehouse tables another source ingests.

See the lineage section of the notebook reference and Lineage & Relationships.

Worth knowing

  • Your code runs isolated. The notebook executes in a separate process with a scrubbed environment, so it never sees the platform’s own credentials.
  • Packages are declared, not installed by hand. List the Python distributions your connector needs and they’re installed before it runs.
  • Executions are bounded by a timeout, an asset cap, and an output-size limit, so a runaway loop can’t consume a cluster.
  • Local folders can be exposed to the notebook by name on desktop deployments.

Configuration

Beyond the fields below, every source also has the settings shared by all of them: the sampling strategy, the detectors to run, the scan schedule, and the compute limits for its scan jobs.

Required

Without these, the source will not save.

FieldTypeRequiredWhat it doesDefault
requiredobjectYesThe Python notebook that implements this connector.no extra properties
notebookobjectYesCells run in document order in one fresh process per execution. There is no persistent kernel: state is rebuilt from the current cell sources every time.no extra properties
notebook.cellsarrayYesOrdered notebook cells. The assembled code cells must define test_connection() and extract().min items 1, max items 200
notebook.cells[]objectYes
notebook.revisionintegerNoMonotonic revision, bumped on every save. Used for optimistic locking so two editors cannot silently overwrite each other.min 11

Secrets

Stored encrypted and never shown again after you save them. See Configuration & Fields.

FieldTypeRequiredWhat it doesDefault
maskedobjectNoEncrypted key/value pairs, stored the same way as any other source credential.no extra properties
secretsobjectNoSecret values the notebook reads with ctx.secret("name"). Encrypted at rest and redacted from logs and cell output. Keys must be valid Python identifiers.{}

Optional

Everything you can tune. Sensible defaults apply when you leave them alone.

FieldTypeRequiredWhat it doesDefault
optionalobjectNono extra properties
limitsobjectNoBounds applied to every notebook execution and scan.no extra properties
limits.max_assetsintegerNoStop a scan after this many assets. Unset means no cap.min 1, max 1000000
limits.max_output_bytesintegerNoTotal serialized cell output kept per execution. Larger outputs are truncated rather than stored.min 1024, max 524288002097152
limits.timeout_secondsintegerNoKill an execution that runs longer than this. Cells cannot be interrupted from inside Python, so this is the real stop button.min 10, max 86400900
local_foldersarrayNoDesktop only. Folders on this machine the notebook reads with ctx.folder("name"). Not available in Kubernetes deployments, where files are uploaded to the source instead. This is a convenience, not a sandbox: the notebook process runs as you and can open any path you can.max items 10[]
local_folders[]objectNoA folder the notebook can read on the machine that runs the scan: this computer on desktop, or a path mounted into CLI jobs by the chart's `api.localFolders` in Kubernetes.no extra properties
local_folders[].namestringYesHow the notebook refers to it: ctx.folder("name"). Must be a valid Python identifier.pattern ^[A-Za-z_][A-Za-z0-9_]{0,62}$
local_folders[].pathstringYesAbsolute path to the folder, as seen by the machine that runs the scan — for example /Users/me/dumps on desktop, or /mnt/corpora/dumps for a folder mounted by the chart.min length 1, max length 4096
packagesarrayNoPython packages installed into the run environment before any cell executes. Installed with uv; the base image's own dependencies are always present and do not need listing.max items 50[]
packages[]objectNoA Python distribution installed before the notebook runs.no extra properties
packages[].namestringYesDistribution name as published on the index (for example 'pandas').pattern ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$
packages[].versionstringNoOptional version or specifier: '2.2.0', '>=2.0', '~=1.4'. Empty means latest.max length 64, pattern ^$|^(==|>=|<=|~=|!=|>|<).+$|^[0-9][A-Za-z0-9._*+!-]*$
variablesobjectNoNon-secret values the notebook reads with ctx.var("name"), such as a base URL or an account id. Stored in clear text. Keys must be valid Python identifiers.{}
Last updated on