Skip to main content

Toolkit overview

The toolkit is a self-contained zip. Once unzipped, this is what you'll find:

nuntiq-connector-toolkit-<version>/
├── nuntiq.py <- entrypoint you run
├── AGENTS.md <- the whole picture for AI coding tools (and humans)
├── README.md
├── requirements.txt
│
├── settings/ <- one .json per connector instance
│ ├── example.json <- template; copy to <your_instance>.json (gitignored)
│ └── ...
│
├── connectors/ <- your code goes here: one file per entrypoint, shared_<name>.py = shared module
│ ├── 01_use_settings.py
│ ├── 03_pull_invoices.py
│ ├── 04_poll_lifecycle_messages.py
│ ├── 06_export_invoices_csv.py
│ ├── get_custom_1.py
│ ├── get_fx_rates.py
│ ├── get_supplier.py
│ └── push_invoice.py
│
├── templates/ <- 21 ready connectors, one folder each (README.md, settings.example.json)
│
├── fixtures/ <- dummy data tables (JSON, edit freely)
│ ├── supplier.json
│ ├── invoice.json
│ ├── user.json
│ ├── ...
│ ├── _seed/ <- canonical state used by `r` (reset)
│ ├── attachment_files/ <- raw bytes served by download_attachment()
│ └── sftp/ <- create it: the SFTP home folder of every run (context.files())
│
├── docs/ <- SDK reference (also on this site)
│
├── lib/ <- the SDK you import in your connectors
│ ├── helpers/ <- Settings, HttpClient, logins, SFTP/S3 files, run_push, ...
│ ├── objects/ <- SupplierLoad, InvoiceLoad, IngestionLoad, ...
│ ├── api_client.py <- ApiError (the client itself is injected at runtime)
│ ├── delta.py
│ └── work_dir.py
│
└── _toolkit/ <- sandbox internals (you can ignore this)
├── runner.py
├── runtime.py
├── fake_api_client.py
├── fake_engine.py
└── json_store.py

The pieces​

connectors/: your code​

One .py per entrypoint. The file name is the entrypoint key: a file named after a standard key (get_supplier.py, push_invoice.py, get_custom_1.py) runs with that key's permissions, as in production. A file with any other name may only read invoices. Eight examples come with the toolkit:

FilePatternUsed by
01_use_settings.pyDiagnostic: logs the settings it findsChecking config wiring
03_pull_invoices.pyInbound delta sync, numeric_id cursorPulling invoices into Nuntiq
04_poll_lifecycle_messages.pyOutbound lifecycle event pollTelling external systems about status changes
06_export_invoices_csv.pyRead-only export with attachmentsReporting, archival, ad-hoc exports
get_custom_1.pyFull sync run into a customer data tableCost centres and other reference lists
get_fx_rates.pyExchange rates with a date cursor and a restate optionThe customer's own exchange rates
get_supplier.pyDelta sync or full sync run, patch_all, delete_keysMaster-data sync (suppliers, organizations, POs)
push_invoice.pyProcess engine call or claim with leases, then resultsPosting invoices to a customer's ERP

Read them. They are commented and cover every framework primitive you will touch.

templates/: ready connectors​

One folder per connector template: every entrypoint, the shared modules, a README.md with every setting and secret, and a settings.example.json. Copy that file to settings/ and run the template in place (its connector_dir points at the folder), or copy the folder and adapt your copy.

fixtures/: your fake Nuntiq​

Each *.json file represents one table. The fake API loads them into memory on startup and reads/writes against the in-memory copy. Edit freely; the next run picks up your changes.

fixtures/supplier.json
[
{
"supplier_number": "SUP-001",
"supplier_name": "Acme Corp",
"is_active": true,
"locations": [
{"location_code": "MAIN", "addresses": [...]}
]
}
]

Press r at the menu to reset everything from fixtures/_seed/. Run a destructive entrypoint, then r to start clean.

SFTP files locally​

The folder fixtures/sftp/ is the home folder of the SFTP user of every local run. Create fixtures/sftp/in/ and put a file there, and context.files().list('in') lists it; what your code writes, moves or deletes happens in that folder. backup() moves the file to fixtures/sftp_backup/. Without fixtures/sftp/, context.files() raises ConfigError, as a run without an SFTP user does in production.

Locally there is no malware scan and no file type list; the 50 MB limit of read_bytes and write_bytes applies as in production. Test the scan wait and the SFTP user's file types on a real environment. r leaves fixtures/sftp/ as it is.

lib/: the SDK​

This is identical to what production has. Same module paths, same class names, same method signatures. Imports you write here continue to work when your code is deployed:

from lib.objects.supplier import SupplierLoad # same in prod
from lib.helpers import Settings, run_push # same in prod

You never edit lib/. The toolkit ships the SDK frozen at the production version it was built against.

_toolkit/: sandbox internals​

The fake API client, JSON store, and local runner. You can ignore this folder: the fact that production uses HTTP and the toolkit uses dicts is invisible to your code.

What the toolkit simulates​

  • The full JobContext shape and every method.
  • Token operation scoping: a run may only call the operations its entrypoint key allows; anything else gets a 403, as in production.
  • The integration engine: keys, full records on POST, PATCH merges, delete by key, ownership by config.source_system / config.ownership_mode, per-record results, cursor reads and sync runs.
  • Invoice leases: claim, re-offer after the TTL, lease_token_required and lease_mismatch, release and the bulk calls.
  • Idempotency-Key replay, payment merges, and the file type and duplicate checks on uploads.
  • Delta state (delta_run, delta_get_state, delta_begin, ...).
  • Attachment downloads (fixtures/attachment_files/{token}.bin → bytes).
  • Nuntiq SFTP files (context.files() over fixtures/sftp/), without the malware scan and the file type checks.
  • Logger output (the same text format as production).

What it doesn't simulate​

  • File processing. ingest_pdf / upload_source_documents check the file type and duplicates and store a source document, but nothing is OCR-ed and no invoice comes out of it. invoice_import stores a minimal invoice you can claim afterwards, without template matching or enrichment. upload_files('einvoice', ...) stores inbound upload rows; nothing is mapped.
  • Lifecycle status validation. The fake accepts more transitions on integration_result and lifecycle posts than production does. Always test the posting loop on a real environment before releasing.
  • Change history, organization-scoped keys, webhooks, e-mails.
  • Queues, scheduling, rate limits, external HTTP. The toolkit makes no outbound requests except the ones your code makes.
  • Lifecycle delta pagination. Returns one page, no next_cursor.
  • Multi-tenant isolation. customer_number is informational only; your one fixture folder is the only tenant.

When to leave the toolkit​

The toolkit catches typos, wrong field names, broken control flow, missing imports, type errors, and most logic bugs. It is not a substitute for an end-to-end test against dev before shipping a connector.

The deploy flow when you're ready:

  1. Paste connectors/<key>.py into the connector template (or the customer connector) in the admin portal, under the same entrypoint key, and every shared_<name>.py on the Shared code tab.
  2. Enable the entrypoint on the customer connector.
  3. Run it once through a scheduler task (connector_dispatch).
  4. Read the run's log in the task history.

The full setup is in the Guide for coding assistants.

What's next​