Downloads
Nuntiq Connector Toolkit
The toolkit is a self-contained Python sandbox for writing and testing connectors on your own machine. Everything runs against in-memory dummy data backed by JSON files, so there is no Nuntiq account, no network and no AWS involved. Your connector talks to your real external system while Nuntiq is simulated.
nuntiq-connector-toolkit-0.6.4.zip
v0.6.4 · ~700 KB · Python 3.10+
Download zip
SHA-256: 7e34f2e47cbd0d82b11ce67c265cd4de9394237ec1ab2995282f3b326ca19284
Verify after download:
- Windows (PowerShell)
- macOS / Linux
(Get-FileHash nuntiq-connector-toolkit-0.6.4.zip -Algorithm SHA256).Hash.ToLower()
shasum -a 256 nuntiq-connector-toolkit-0.6.4.zip
What's inside
nuntiq-connector-toolkit-0.6.4/
├── 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 is the template)
├── connectors/ <- 8 example entrypoints + your code: one file per entrypoint, shared_<name>.py = shared module
├── templates/ <- 21 ready connectors (ERP over REST, SOAP and files; invoices in; Zoho Books)
├── fixtures/ <- dummy data tables (JSON, edit freely); sftp/ = the run's SFTP home folder
├── docs/ <- SDK reference (also on this site)
├── lib/ <- the SDK you import (lib/helpers: the building blocks of the templates)
└── _toolkit/ <- sandbox internals (you can ignore)
See Toolkit overview for what each
folder does, and Guide for coding assistants
for the text of AGENTS.md: the production runtime contract, where every
context value comes from, the entrypoint keys, and how a connector is set
up in the admin portal.
Quick start
- Unzip somewhere convenient.
python -m venv .venv && .\.venv\Scripts\activate(Windows) orsource .venv/bin/activate(macOS/Linux).pip install -r requirements.txt.cp settings/example.json settings/my_instance.jsonand edit it, or copy a template'stemplates/<key>/settings.example.json.python nuntiq.py(menus) orpython nuntiq.py my_instance get_supplier(one run).
Full walkthrough: Installation → Your first connector.
Changelog
0.6.4 (2026-11-04)
- Nuntiq SFTP files, one SFTP user per run: a scheduler task or an
integration rule names at most one Data Integration SFTP user, and
context.files()(no argument) returns that user's whole home folder, read and write. This replaces the named mounts of 0.6.3: mount names, subfolders, read-only access and the_mountsdeclaration are gone. A call with a mount name now raisesTypeError; drop the argument. Without a usable SFTP usercontext.files()raisesConfigErrorwith the reason. See Nuntiq SFTP files. - In the sandbox
fixtures/sftp/itself is the SFTP home folder (no folder per mount). Move files fromfixtures/sftp/<name>/up one level. See SFTP files locally.
0.6.3 (2026-11-04)
- Nuntiq SFTP mounts:
context.files('<mount>')returns aNuntiqFileStoreover a folder of a Data Integration SFTP user on Nuntiq's SFTP server, bound on the integration rule (tab SFTP Mounts) or the scheduler task (Connector Settings). The same methods asSftpStoreandS3Store(soInboxandOutboxwork on it), plusstat,list_folders,copy,rmdirandbackup. Entrypoints declare the mounts they need in their configuration (_mounts). See Nuntiq SFTP files. - In the sandbox every folder under
fixtures/sftp/<name>/is a read-and-write mount of the run. See SFTP files locally. requirements.txt: authlib 1.9.2.
0.6.2 (2026-10-23)
- Record ownership, four modes: the sandbox follows the API's
ownership_mode(in the settings'config):system_of_record(all records),all_integrated(all records that came from an integration),managed(the default: records with your source system, and records entered in Nuntiq may be taken over by updating them) andown_only(only records with your source system). A write or delete outside the mode is refused withowned_by_other_source. - The source system travels in the records, not on the URL (as the API
since 2026-10-22). The SDK puts the connector's source system in every
record that carries its own source record id and names none; a record found
by its id or natural key keeps the source system of the stored row, so an
update never moves another system's record to your connector. Another
source system in a record is accepted only under
system_of_recordorall_integrated. Asource_systemon the URL that differs from the connector's is refused (source_system_override_not_allowed). - Claiming invoices needs
write_invoiceorlease_invoice, as in the API.
0.6.1 (2026-10-14)
- Invoice deductions and amount due in the SDK:
invoice.deductions(InvoiceDeduction: typeretainage,withholding,depositorother, description, rate, base amount, amount) andinvoice.amount_dueon an invoice read from the API;new_deduction(type)andheader.amount_dueon a structured import (IngestionDeduction);InvoiceChargeis exported. The sandbox returns and stores them; invoice 5003 in the fixtures has a 5% retainage. See Invoice. - Templates 1.1.0: every template that pushes, exports or ingests
invoices carries charges, discounts, deductions and the amount due, with a
deductions_modesetting on the push templates. See Connector templates. inbound_rest_oauth2_refresh_tokenkeeps the access token between runs.requirements.txtin step with the connector base image of 2026-10-12 (paramiko 5.0.0: no SHA-1 in SSH).
0.6.0 (2026-10-11)
- Connector templates in
templates/: 21 complete connectors, one folder each with every entrypoint, the shared modules, aREADME.mdand asettings.example.json. ERP over REST (API key, Basic, OAuth 2 client credentials, refresh token, JWT, OAuth 1 TBA, mTLS, session), ERP over SOAP, ERP over files (SFTP with password, key or certificate; S3), invoices in over REST, SFTP and S3, and Zoho Books. See Connector templates. - Helpers in
lib/helpers: typed settings, an HTTP client with retries, re-login and paging, every login method, SFTP, S3 and local file stores with inboxes and atomic writes, PGP, CSV, Excel and safe XML reading, the delta cursor around a pull, the wholepush_invoice(run_push), the status run for async pushes (ResultReporter) and invoice intake. See Helpers. - A settings file may name a
connector_dir: the runner uses that folder's entrypoints instead ofconnectors/(the templates' settings files do). python nuntiq.py <settings> <entrypoint>runs one entrypoint without the menus; the exit code is 1 when the run fails.- A handler that returns
success: falseis reported as failed, as in production. - Acknowledging an invoice stores external ids and messages, an engine invoice can be acknowledged (it waits on its integration step), and the invoice list returns the lease to the connector that holds it. See Async push and status.
requirements.txtin step with production: requests 2.32.5, authlib 1.6.5, msal, boto3, cryptography, defusedxml, python-gnupg added. PGP needs thegpgprogram on your PC.AGENTS.mdrewritten around the templates and the helpers.
0.5.4 (2026-10-11)
AGENTS.md: every connector run has 4 GB of disk; handle files one at a time (download, send, delete) instead of collecting them first, and page large pulls over several runs with the delta cursor. A run that goes over the limit fails with "The run ran out of disk space (limit 4g per run)".
0.5.3 (2026-10-11)
context.api.upload_files(process, files, ...): send files with a role each.'capture'(one source document):'document'is captured,'attachment'is kept with the source document and attached to its invoices (a PDF too).'einvoice'(one batch):'data'files are mapped by the customer's file formats,'attachment'PDFs and images are linked to the invoice that names them. A missing or wrong role, or a file whose bytes do not fit its role, raisesValueErrorbefore anything is sent. See Sending files.from lib.file_types import classify_file: what a file is, read from its bytes exactly as the API does. The sandbox's own type checks use it.upload_source_documents()andupload_inbound_files()callupload_files(); next to the PDFs, images, Office documents, CSV, TXT, XML, JSON, EML and MSG files are kept as attachments, and an archive inside a ZIP is refused with 415.- The sandbox stores e-invoice files in
fixtures/inbound_upload.json(nothing is mapped) and source document files with their role. - 0.5.2 was not published; its changes are part of 0.5.3.
0.5.1 (2026-10-11)
context.update_secret(name, value)andcontext.update_secrets({...}): store a secret the connector rotated itself, such as an OAuth refresh token. Only names in the settings file'srotatable_secretsare accepted (in production: Secrets the connector may update on the connector); others raiseSecretUpdateError. The sandbox writes the value into thesecretssection of the settings file in use and reads the file again before every run. See OAuth 2.0.
0.5.0 (2026-10-02)
- The SDK and the sandbox follow the integration API contract. Supplier,
organization, purchase order, PO line, receipt, customer data table and
user loads:
save_all()sends full records and answers per record,save_all(full_load=True)is a sync run (records of your source that the run did not send are removed; a rejected record fails the run),patch_all()merges,delete_keys()deletes by key,delete_where()is refused, reads follow a cursor. Rejected records raiseRecordsRejected. SupplierLoad(context, source_system='...'): a load writes as one source system, by default the connector'sconfig.source_system.- Objects read from the API keep their id, so
obj.save()andobj.delete()work after a read.obj.deactivate()sets the active flag to false; purchase orders, PO lines and receipts have none (usedelete()). - Assigning a child list sends it, also when empty:
loc.addresses = []removes your addresses on a full save. An unset list keeps them. - Every POST, PATCH and DELETE carries an
Idempotency-Keyand is retried with the same key on a connection error, 502, 503, 504 or 409idempotency_in_progress. InvoiceLoadworks with leases:acknowledge,releaseandintegration_resulttake theInvoiceLease; bulkacknowledge_many,release_many,integration_results;list()follows a cursor.IngestionLoadsends files over 50 MB through presigned uploads and reports duplicates;status()reads a source document.PaymentLine.invoice_tokenreplacesinvoice_token_id.PaymentLoadhas no list, search or delete.- New fields: supplier location
is_default; supplier addressremit_to_addressandstatus; bank accountstatus; organizationtimezone; source keys on addresses, bank accounts and organization identifiers. Child ids are strings (UUID). - Job scopes as in production:
push_invoicemay acknowledge and release and post lifecycle messages,get_invoicemay upload source documents,get_invoice_statusandget_paymentmay post lifecycle messages,get_purchaseorderandget_receiptsmay read the master data they map to,get_invoicemay read receiving inboxes. - Examples renamed after their entrypoint keys:
get_supplier.py(was02_pull_suppliers.py),push_invoice.py(was05_push_invoices.py), and a newget_custom_1.py.
0.4.8 (2026-10-01)
- New entrypoint key
get_fx_rates: load the customer's own exchange rates from an ERP. Its job token may read and write exchange rates only. FxRateLoad(lib/objects/fx_rate.py): rates against the base currency or pairs with the base on one side,delete_range()to restate a period,effective()for the rate the reports use and its source. Reference:docs/fx_rate.md.- Example entrypoint
connectors/get_fx_rates.py(delta cursor, restate option) and fixturesfx_rate.json,fx_settings.json,platform_fx_rate.json; the sandbox simulates the exchange-rate endpoints.
0.4.7 (2026-09-22)
- The sandbox enforces token operation scoping like production: a run may only call the operations its entrypoint key allows, anything else gets a 403 from the fake (the same mapping the integration worker and the internal API use).
load.find_by_external_id(external_id_1=..., ...): invoices by the external integration ids an integration result stored, in any state.- Organization units round-trip:
GET /v1/organizationreturnsparent_codenext toparent_id, and a save accepts the parent's code or an existing unit's id inparent_id. temp_work/run folders older than 24 hours are purged at the next run.- A handler that returns
{'success': False}is reported as failed in production (main.pyno longer overwrites it).
0.4.6 (2026-09-22)
load.list()/load.list_all()acceptPendingConnectorIntegration: invoices the process engine handed to a connector'spush_invoicejob and that still wait for its result (status 94). For housekeeping and reconciliation; apush_invoicejob gets its own invoice injob_paramsand never claims from this list.api.get_pending_connector_invoices()is the one-call form.- The state name is sent to the API the way the API wants it (lowerCamel), so
state='PendingIntegration'works against the real API, not only the fake. - The fake API answers the new state (status 94 in the invoice fixture) and accepts both spellings.
0.4.5 (2026-09-13)
- Global connectors are templates. Creating a customer connector copies
the template's default config, entrypoints, shared modules and code as
customer version 1; the copy is edited on the customer from then on.
AGENTS.mdand README section 7 describe the new setup flow (template, then customer connector from it, or a blank connector).
0.4.4 (2026-09-13)
- Shared code modules: a
connectors/shared_<name>.pyfile withoutrun()is a module every entrypoint of the connector can import by file name (from shared_zoho import ZohoBooks). In the admin portal it is pasted on the connector's new "Shared code" tab; the platform writes it next to the entrypoint at run time. The menu lists only files that definerun()and names the shared modules. AGENTS.mdand README: the one-file rule replaced by the shared module rule; admin portal setup includes the Shared code tab.
0.4.3 (2026-09-12)
- SDK and docs: organization units use UUID ids and
codeis optional when a unit carries a source identity; organization types documented (1 Group, 2 Company, 3 Department);save()sends the whole object. - Fake API: organizations and suppliers match soft-deleted rows by source identity and revive them on save, as production does.
0.4.2 (2026-09-12)
AGENTS.mdadded: one document that gives an AI coding tool or a new developer the full picture (runtime contract, context sources, entrypoint keys, admin portal setup, implementation checklist).
0.4.1 (2026-09-12)
- External identity documented:
source_system,source_record_id,source_updated_aton suppliers, supplier locations and organization units;02_pull_suppliers.py(nowget_supplier.py) stamps them. docs/organization.md: the read, merge, save pattern for records that people also edit by hand;codeoptional for units with a source identity.
0.4.0 (2026-09-12)
- SDK: external identity fields on
Supplier,SupplierLocation,Organization;BaseLoad.index_by_source(). - Fake API matches on
(source_system, source_record_id)before the natural key, as production does.
0.3.0 (2026-09-12)
- SDK resynced with the production framework: purchase order, PO line and
receipt field names (
net_amount,tax_amount,gross_amount,description), discount basis fields on invoices, and three objects that were missing (IngestionLoad,LifecycleMessageLoad,UserLoad). lib/api_client.pyshipsApiError, and the sandbox raises it, soexcept ApiErrorbehaves the same locally and in production.- Fixtures use the production invoice field keys (
invoiceNumber,grossAmount,matched_supplier_number, ...); auser.jsonfixture. - Fake API:
/v1/users,/v1/invoice-import, real source-document result shape. - README:
settings/layout and the rules that only bite in production. - New docs:
ingestion.md,lifecycle_message.md,user.md.
0.2.0 (2026-05-16)
- Renamed from
ap-receiving-connector-sandbox→nuntiq-connector-toolkitto match the platform rebrand. - Bundled with the Nuntiq developer portal.
0.1.0 (2026-04-25)
- Initial release. Six example connectors covering the four most common shapes (pull with delta, push with claim/ack/result, lifecycle poll, read-only export).
Stay current
Toolkit releases are versioned alongside the production framework. Major behavioural changes get a major-version bump; SDK additions are minor. Anything you've written against an older toolkit should run unchanged on the next minor version. Connectors written against 0.2.0 need one check before running on 0.3.0 or later: invoice field keys are the capture template's camelCase names, not snake_case.
The framework itself ships on a separate cadence maintained by Nuntiq.
Both share the SDK in lib/ byte-for-byte, so anything that imports
cleanly in your toolkit will import cleanly in production at the version
you tested against.