<nil>NILScript

Develop

Adapter Toolkit

NILScript is an open standard, not a framework. You get JSON schemas, a CLI, a typed Python SDK, and the ability to build a conformant adapter in any language — proven by an independent ERPNext adapter built from the public docs alone.

The toolkit

PackageInstallWhat’s inside
Core + CLIpip install nilscriptJSON schemas, docs, and the nilscript CLI — zero deps
SDKpip install "nilscript[sdk]"Typed Python client (httpx + pydantic)
JS / TSnpm install nilscriptThe spec + JSON Schemas
The core is just schemas
Python ≥ 3.12 for the tooling, but the core is data only. An SDK in any language can be generated from the schemas.

Explore with the CLI

Three commands explore the standard — no account, key, or waitlist.

explore
pip install nilscript

nilscript verbs                              # list every standard verb
nilscript profile commerce.process_refund    # one verb's full contract
nilscript export-openapi > nil.json          # OpenAPI 3.1 for the six endpoints

A round-trip with the SDK

The safety model is visible in one PROPOSE COMMIT round trip: propose with no side effects, read the preview and tier, commit with an idempotency key. Refusals are 200 OK outcomes, not exceptions.

round_trip.py
from nilscript.sdk import Client

client = Client(base_url="https://shop.example.com/nil", grant=GRANT_TOKEN)

# 1. PROPOSE — no side effects. Server validates, resolves, prices.
proposal = client.propose(
    verb="commerce.process_refund",
    args={
        "refund_target": {"type": "payment", "id": "pay_8812"},
        "amount": {"value": "49.00", "currency": "USD"},
        "reason": "customer_request",
    },
)

# Handle refusals as data, not exceptions.
if proposal.outcome == "AMBIGUOUS":
    pick = choose(proposal.candidates)
    proposal = client.propose(..., disambiguate=pick.id)

if proposal.outcome == "PROPOSAL":
    print(proposal.preview, proposal.tier)
    # 2. COMMIT — idempotent. Same token replays the outcome.
    result = client.commit(proposal.commit_token, idempotency_key="refund-8812")

Scaffold from OpenAPI

export-openapi emits an OpenAPI 3.1 document for the six endpoints. Feed it to any generator and you have endpoint stubs; fill in the translation layer.

scaffold
nilscript export-openapi > nil.json

# TypeScript server stubs
npx openapi-typescript-codegen --input nil.json --output ./nil-server

# Go server stubs
oapi-codegen -generate types,server nil.json > nil_server.gen.go

Build an SDK in any language

The core package is JSON schemas plus docs with zero runtime dependencies. Generate models from the schemas, wrap an HTTP client, and match the Python SDK’s surface — propose, commit, query, status, plus refusal handling as data.

The ERPNext proof

An independent developer built a conformant ERPNext adapter from the public standard and CLI alone — no privileged knowledge, no insider access. It is a stateless FastAPI shim that proves the published standard is sufficient on its own.

A reusable three-layer structure

LayerFileRoleReusable?
NIL edgeshim.pyEndpoints, envelopes, auth, idempotency, EVENT emissionGeneric across any backend
Translation coretranslate.pyMaps NIL verbs → ERPNext DocTypes, builds previewsThe one backend-specific file
System clienterpnext.pyFrappe REST client + in-memory test doubleSwappable protocol
The same recipe ports anywhere
To adapt a different system, reuse shim.py as-is, rewrite translate.py, and implement the client protocol — the same recipe builds adapters for Odoo, SAP, or NetSuite.

NILScript is an open standard, stewarded by the Wosool project. The spec is extracted from running code.

Draft standard v0.3.0 · 0.x stage · NIL wire 0.1 · DSL 0.1