This package provides a CLI and an API Library to integrate with NHP New Zealand. You can search products, check pricing, check stock availability, manage your cart, and pull order/invoice history.
Note
This tool is currently configured for NHP New Zealand (nhpnz.co.nz).
However, it should also work on the Australian version of the site
(nhp.com.au) with some minor domain and configuration tweaks.
-
Deno 2.x. Install it with the official one-liner, then check
deno --version:irm https://deno.land/install.ps1 | iex # Windows (PowerShell)
curl -fsSL https://deno.land/install.sh | sh # macOS / Linux
-
Git, to clone this repository.
-
A login for the NHP New Zealand web portal (
nhpnz.co.nz).
Clone the repository and run the CLI from inside it:
git clone https://github.com/Bedz01/nhp-cli.git
cd nhp-cli
deno run -A nhp_cli.js help-A grants the network and file access the tool needs: the portal, and its
state directory (see Setup). deno task cli <args> is a shorthand for the
same command. Deno fetches the dependencies on the first run, so that run
takes a moment longer.
To run it as nhp from any directory, install it globally:
deno install -g -A -f -n nhp nhp_cli.jsDeno puts the nhp shim in its bin directory (~/.deno/bin, or
%USERPROFILE%\.deno\bin on Windows) and tells you if that directory is not
on your PATH yet. All dependencies are referenced with explicit versioned
specifiers, so the installed command works from any directory without extra
flags. Re-run the install command after pulling changes.
Log in once:
nhp login
It asks for your portal username and password (the password is not echoed),
logs in, and remembers both so later commands - and the frequent re-logins
the portal's short sessions force - happen silently. Any command run before
that will ask the same questions itself if it is running in a terminal.
nhp login --reset asks again (to change accounts); nhp logout forgets
the session and the saved credentials.
Where things live. All state goes in a per-user directory, never in the
checkout, so it survives re-clones and git never sees it:
| Platform | Directory |
|---|---|
| Windows | %APPDATA%\nhp |
| Linux / macOS | $XDG_CONFIG_HOME/nhp, else ~/.config/nhp |
| Override | NHP_CONFIG_DIR |
config.json { "sellMarginMultiplier": 1.25 } optional; enables the Sell: line
credentials.json saved by `nhp login`
cookies.json the portal session
How the password is stored. On Windows it is encrypted with DPAPI in
the current-user scope, so the file can only be decrypted by your Windows
account on that machine; copying it elsewhere yields nothing. The CLI does
this through a short PowerShell script (ProtectedData, no cmdlets or
modules involved), tried in Windows PowerShell 5.1 and then pwsh, and only
when it actually needs to log in. If neither PowerShell works, the password
is stored as plain text and nhp login says so. On Linux and macOS the file
is plain text with mode 600.
Scripts and CI. Environment variables override the stored file and never prompt:
NHP_USERNAME,NHP_PASSWORDNHP_SELL_MARGIN(optional)NHP_CONFIG_DIR(optional)
With --json or --tsv, or with no terminal, a missing login is an error
(Run 'nhp login') rather than a prompt, so stdout stays clean.
Upgrading from 1.3 or earlier. The old credentials.json and
cookies.json next to nhp_cli.js are still read as a fallback. Run
nhp login once: it moves the credentials (and the margin) into the state
directory, after which the old files are no longer read and can be deleted.
Run nhp help (or nhp --help) for the full built-in reference.
Products & Pricing
nhp search <query>- Search for products matching a query.nhp price <part>[:qty]... [--full|--tsv]- Price and stock for one or more part numbers as a ledger, one line per part:PART DESCRIPTION QTY BUY [SELL] STOCK(description truncated to fit,SELLonly when a margin is configured, stock as glyph + NZ on-hand quantity + state; an unknown part keeps its line with the portal's reason and✗ NOT FOUND). Quantities go on the part asK144:2 06850863:10;--qty <n>is the default for parts without one. A bare number is always a part number. Add--fullfor the record view with discount, AU stock and the rest.nhp csv <csvFile> [--full|--tsv]- The same for part numbers listed in a CSV file. Columns:partNumber[,qty]- a single-column file of part numbers also works (qty defaults to 1), and a header row is skipped automatically.
Orders & Invoices
nhp orders [offset] [--full|--tsv]- Get order history as a compact ledger (ORDER PO STATUS DATE, one line per order). Add--fullfor the record view with totals. Optional search flags:--dateFrom,--dateTo,--purchaseNumber,--documentNumber,--orderNumber,--customerReference.nhp invoices [offset] [--brief|--tsv]- Get invoice history. Accepts the same optional search flags as orders. Add--brieffor the ledger view.nhp order <orderId...> [--full|--tsv]- Line items for one or more orders as a ledger, one line per item:PART DESCRIPTION DLV/ORD STATUS(description truncated to fit, delivered/ordered quantity coloured green/yellow/red). Several order IDs are fetched one at a time (each is a full page scrape) and print one ledger each, separated by a blank line and led byOrder | PO | Ref | Date; an order that can't be fetched is reported on stderr without stopping the others, and the command still exits non-zero. With several IDs,--jsonemits an array with one entry per argument (a failure becoming{ orderId, error }); a single ID keeps the bare scrape result. Add--fullfor the record view with header, addresses, prices and totals.nhp invoice <id> [--brief|--tsv]- Get detailed line items for a specific invoice.--briefprintsPART DESCRIPTION QTY.nhp po <query>- Search order history by PO Number. A single match auto-expands likenhp order(ledger, or record view with--full).
Cart Management
nhp cart add <partNumber> [qty]- Add an item to the cart. The two-argument form treats the second argument as a quantity only when it is 1-9999 with no leading zero; anything else (e.g. the numeric part number06850863) is treated as a second part number. For explicit quantities - including large ones - use thepart:qtyform:nhp cart add K144:2 06850863:10.nhp cart list- View current items in the cart.nhp cart remove <partNumber|line#>- Remove an item, by part number or by the line number shown incart list. Part numbers take priority when both interpretations are possible.nhp cart update <partNumber|line#> <qty>- Update the quantity of a cart item.nhp cart clear- Empty the entire cart.nhp cart upload <csvFilePath>- Upload a CSV of part numbers to the cart (same format asnhp csv). The tool verifies every part actually landed in the cart and reports any that were rejected.
Authentication
nhp login [--reset]- Log in and save the session. Asks for the username and password the first time (or with--reset) and remembers them; otherwise re-logs in with the saved ones.--jsonreports the username, where the credentials came from, and the file paths.nhp logout- Forget the saved session and credentials.
price, csv, orders, order, invoices, invoice and po take
--tsv: one header row and one tab-separated row per record (per line item
for order/invoice, across every order given), with the order- or
invoice-level fields repeated on each row so the result is a flat table.
Plain text only - no colour, glyphs, truncation, or progress line; money and
quantities are bare numbers parsed out of the portal's $1,234.56 strings -
so it pastes straight into columns:
nhp order SOR1000001 SOR1000002 --tsv | Set-Clipboard # then paste into Excel/Sheets
nhp invoices --dateFrom 2026-07-01 --tsv | Set-Clipboard
nhp price K144 06850863 --tsv | ConvertFrom-Csv -Delimiter "`t" # PowerShell objectsColumns (blank where NHP has no such value):
price/csv PART DESCRIPTION QTY BUY SELL LIST CURRENCY STOCK STOCK STATUS ERROR
orders ORDER PO STATUS DATE TOTAL
order ORDER PO ORDER STATUS DATE LINE PART DESCRIPTION DELIVERED ORDERED LINE STATUS UNIT PRICE TOTAL
invoices INVOICE PO REF STATUS DATE TOTAL OUTSTANDING
invoice INVOICE PO REF DATE LINE PART DESCRIPTION QTY UNIT PRICE TOTAL
STOCK is the NZ on-hand quantity and STOCK STATUS the badge text; LIST
is always blank (the portal has no list price) and CURRENCY always NZD;
SELL is filled only when sellMarginMultiplier is configured; LINE is the
1-based position (the portal has no line numbers). Dates are dd/mm/yyyy as
in every other view. Failures behave as in the human view (stderr, non-zero
exit) and contribute no rows. --tsv wins over --full/--brief, --json
wins over both.
You can append --json to any command to receive the raw JSON response on
stdout instead of the formatted terminal output. Progress and error messages go
to stderr, so stdout stays valid JSON for piping:
nhp orders 0 --purchaseNumber PO-12345 --json > orders.jsonFailed operations (unknown part numbers on cart add, items not found, API
errors) exit with a non-zero status code, so the CLI is safe to script against.
- Header labels differ between the order and invoice pages, including
capitalisation. The order page says
Order Created onandCustomer Reference no; the invoice page saysInvoice date(lower-case d) and the sameCustomer Reference no. The ledger summary line and the--tsvexports look the labels up through one shared fallback list informatters.js; add new spellings there, not at the call sites. - Portal dates are unpadded
d/M/yyyy(3/09/2026, day first). Every printed date goes throughformatDate, which zero-pads todd/mm/yyyy.
You can also integrate this package directly into your own JavaScript applications.
In Deno (or standard ES module environments), simply import the entry point
mod.js.
import { NHPClient } from "./mod.js";Create a new instance of the client. By default it resolves credentials the
way the CLI does (environment variables, then the file nhp login saved)
and keeps the session in the same state directory (see Setup).
const client = new NHPClient({
// Optional configuration overrides:
cookiePath: "./data/custom_cookies.json",
credentials: {
username: "your_email@example.com",
password: "your_password",
},
timeoutMs: 120000, // Per-request timeout (default 120s - the NHP portal is slow)
silent: true, // Set to true to suppress internal console log messages
});
// Always call ensureLogin() before making API calls
await client.ensureLogin();credentials may also be an async function; it is called only when a login
is actually needed, so an expensive lookup (a keychain, a prompt) does not
run on every command. onLogin(creds) is called after each successful
login. The store export has the pieces the CLI uses: resolveCredentials,
saveCredentials, dpapiProtect/dpapiUnprotect, configDir.
All requests carry a timeout and automatically re-authenticate once if the saved session has expired.
Searches for products matching a keyword.
const results = await client.searchProducts("battery");
console.log(results.widgets[0].content); // Array of productsFetches the current pricing and stock availability (including local NZ and AU
stock). Expects an array of objects containing itemId (the part number) and
qty.
const items = [
{ itemId: "TPHS25R5GM", qty: 1 },
{ itemId: "1756BA1", qty: 5 },
];
const pricingData = await client.getPriceAndStock(items);
// Example response mapping:
for (const prod of pricingData.ChildProducts) {
if (prod.HasError || prod.ProductExist === false) {
// Unknown part - the reason is in prod.ErrorMessages
continue;
}
console.log(`Buy Price: ${prod.AdjustedPriceWithCurrency}`);
console.log(`NZ Stock: ${prod.OnHandQty}`);
}Fetches the user's order history. The options object can include any
combination of the following search filters: documentNumber, orderNumber,
purchaseNumber, customerReference, dateFrom, dateTo.
// Get first 20 orders
const orders = await client.getOrders(20, 0);
// Search for a specific Purchase Order number and Date Range
const poOrders = await client.getOrders(20, 0, {
purchaseNumber: "PO-12345",
dateFrom: "01/12/2025",
dateTo: "06/12/2025",
});Fetches the user's invoice history. The options object accepts the same search
filters as getOrders.
const invoices = await client.getInvoices(20, 0, { dateFrom: "01/01/2026" });Fetches specific line items and shipping statuses for a given order ID.
const details = await client.getOrderDetails("ORDER_ID_HERE");
console.log(details);Fetches specific line items and pricing for a given invoice ID (Document Number).
const items = await client.getInvoiceDetails("SIN987654321");
console.log(items);The library provides complete functionality to manage the user's shopping cart:
await client.addToCart("115797", 2);
const cart = await client.getCart();
await client.updateCartLineQuantity(cart.Lines[0].ExternalCartLineId, 5);
await client.removeCartLine(cart.Lines[0].ExternalCartLineId);
await client.clearCart();
// CSV uploads verify the result against the cart, since the NHP upload
// endpoint does not report failures in its response:
const result = await client.uploadCartCsv("./bulk_order.csv");
// -> { success: boolean, requested: string[], missing: string[] }deno task check # lint + type-check + tests
deno task test # offline unit tests onlyOffline unit tests live in tests/ and run in CI on every push.