DevOp.DK.Plus.Cli 0.5.0

dotnet tool install --global DevOp.DK.Plus.Cli --version 0.5.0
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local DevOp.DK.Plus.Cli --version 0.5.0
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=DevOp.DK.Plus.Cli&version=0.5.0
                    
nuke :add-package DevOp.DK.Plus.Cli --version 0.5.0
                    

DevOp.DK.Plus.Cli

A .NET 10 global tool for querying DK Plus through the typed DevOp.DK.Plus client.

Run devop-dk or append --help at any command level for branded, contextual help. The banner is colored only in an interactive terminal and remains plain when redirected or when NO_COLOR is set.

dotnet tool install --global DevOp.DK.Plus.Cli
devop-dk update
export DKPLUS_API_TOKEN="..."
devop-dk connection test
devop-dk company status
devop-dk customer list --take 25
devop-dk customer list --sales-person S-7 --name Jón --blocked false --include Number,Name --json
devop-dk customer list --all --yes --json > customers.json
devop-dk customer get C-100 --expand Memos
devop-dk customer transaction list --customer C-100 --take 25
devop-dk customer create --number C-100 --name "Example customer" --group ALM
devop-dk customer update C-100 --file customer-update.json
devop-dk customer contact create C-100 --number 1 --name "Accounts" --email accounts@example.com
devop-dk customer contact list C-100
devop-dk customer receiver create C-100 --number MAIN --name "Main delivery"
devop-dk customer receiver list C-100
devop-dk product price ITEM-1 --quantity 2 --json
devop-dk product create --item-code ITEM-1 --description "Example item"
devop-dk currency list
devop-dk payment term list
devop-dk customer group list
devop-dk product unit list
devop-dk sales invoice create -c C-100 --line "item-code=ITEM-1;quantity=2" --line "item-code=ITEM-2;unit-price=1500"
devop-dk sales order create -c C-100 --reference PO-77
devop-dk sales order lines create 38e492ef-6169-43e1-8798-4d06ab60ee87 --item-code ABCD --quantity 2
devop-dk sales invoice pdf INV-100 --output INV-100.pdf
devop-dk sales order pdf 123 --output order-123.pdf
devop-dk sales quote html 456
devop-dk sales order lines update 38e492ef-6169-43e1-8798-4d06ab60ee87 2 --item-code ABCD
devop-dk sales order delete 38e492ef-6169-43e1-8798-4d06ab60ee87 --yes
devop-dk webhook list
devop-dk permission show
devop-dk purchase vendor search "Example supplier" --maximum 25
devop-dk purchase invoice my-unapproved
devop-dk warehouse list
devop-dk general-ledger account list
devop-dk project transaction list --project P-100
devop-dk token report logs
devop-dk table records ARCUST --fields Number,Name --take 25

devop-dk update updates the normal user-wide installation to the latest stable release from NuGet.org. Use --version, --prerelease, or --tool-path for a controlled version or custom installation; the command preserves the underlying dotnet tool update exit code.

The tool supports company information and connection checks, customer CRUD and transactions, product CRUD and pricing, employees, full project resources, General Ledger, token administration and reports, reference data for currencies, payments, customer groups, and product groups/categories/units, a nested sales group for invoices, orders, quotes, recurring sales, and salespeople (invoices can be created, optionally posted with --post --yes; v2 orders, quotes, and recurring sales can be created, updated, and deleted, as can their single lines; the salesPerson setting supplies a default salesperson for creates), and a nested purchase group for vendors and vendor invoices. It also covers dimensions, nation lookups, generic DK Plus table reads and writes (table create and update run directly; table delete requires --yes), and project delete (by number or --record-id; DK Plus has no project delete endpoint, so it deletes through the generic table API on pjjob, skips DK Plus project validation, and requires --yes), webhook administration, permissions, inventory, approvals, and warehouses. Human query output uses terminal tables, permission show includes each configured capability access level, and structured query commands support --json for automation. Explicit create and update commands run without redundant confirmation; destructive deletes and safety-sensitive operations retain --yes. Webhook authorization values and API tokens are redacted from normal CLI output.

Customer contacts have their own customer contact list|get|create|update|delete commands (delete requires --yes; create --file reads a CustomerContactCreateRequest and update --file a sparse CustomerContactUpdateRequest). Customer item receivers have the matching customer receiver list|get|create|update|delete commands (CustomerReceiverCreateRequest and CustomerReceiverUpdateRequest files; delete requires --yes). A contact or receiver update --file whose document sets no member stops with exit code 2 before any request. DK Plus ignores contacts, receivers, and memos in the customer document, so customer create no longer has --contact-* or --receiver-* options, a customer --file with Contacts or Receivers is rejected with a pointer to customer contact create or customer receiver create, and one with Memos is rejected as read-only (customer memos have no write endpoint).

customer get keeps its default detail table compact. Add repeatable or comma-separated --expand values for Settings (formerly Options), Contacts, Receivers, Memos, Properties, Changes, and Attachments, or use --expand All. JSON output already contains every nested section and is therefore separate from expansion-driven human output.

Customer, product, project, and vendor update commands read sparse update documents (CustomerUpdateRequest, ProductUpdateRequest, and so on); the entity is the positional argument (devop-dk product update ITEM-1 --description "...") and the document contains no identity. Null or omitted members are not sent, and an explicit empty collection remains []; this prevents a focused update from writing unrelated entity fields. --file input is strict for every command that reads a file: a document type without extension data rejects unknown keys (the message names the key path), and a type that forwards unknown keys still rejects keys from the previous release, renamed or read-only keys, and the identity in update files, naming the replacement. Read-only nested product and memo data (a warehouse QuantityInStock, unit QuantityOnHand, vendor LastPurchased*, or nested Modified) and ObjectDate are rejected the same way; change stock with inventory journal or inventory count. warehouse update-product --file reads a ProductWarehouseSettingsRequest document, so a saved warehouse-product shape with stock quantities, Modified, or the wire spellings (LocationInWarehouse, DefaultPOQuantity, SkipPOSuggestion) is rejected the same way. The error goes to stderr with exit code 2 before any network request. project request is now project work-request. JSON output is the flat DkPlus* shape with enum member names; for customers the former options object is gone and its members sit beside credit, invoiceDelivery, and electronicInvoicing.

The fluent list commands read 100 records by default. --take <n> accepts 1 to 1000 (use --all for more), and --all reads every record page by page; it cannot be combined with --take and asks first (Fetch every customer? ... [y/N] on stderr). --yes skips the question, and with redirected stdin, stdout, or stderr and no --yes the command stops with exit code 2 before any request. customer transaction list-for, project all, and project work-request list-for always read every record and ask the same way. With --json, --all streams one JSON array record by record; a failure after the first page leaves a truncated array on stdout with a non-zero exit code. --include and --exclude (repeatable or comma-separated) narrow the returned properties on customer, product, customer transaction, customer group, product group, employee, sales invoice|order|quote|recurring, and payment mode|term|type lists; a table under --include shows only the named columns. customer list also filters on the server with --name, --sales-person, --country, --postal-code, --payment-term, --payment-mode, --email, --blocked, --vat-exempt, and --deleted, and product list with --alias-item-code, --description, --description2, --extra-description1, --extra-description2, --warehouse, --inactive, --on-web, and --deleted. The boolean filters take a value (--blocked false); how DK Plus matches the values is not verified live, and * is documented as a wildcard for products.

The -m/--modified-since, --modified-after, and --modified-before options send a window that DK Plus applies to the entity's ObjectDate (the dkPlus stored time, "Stored Date") when it has one and to Modified (the dkERP modification time) otherwise; the CLI does not filter client-side. Customer, product, vendor, sales, and journal tables therefore show an ObjectDate column after Modified.

Typed collection tables keep their current default columns but can be personalized locally. Run devop-dk config columns show to list configurable profiles and devop-dk config columns show sales.person to see localized labels, stable property paths, and numeric IDs. Use config columns select for a guided prompt, config columns set sales.person 1,4,6,5,2 for scripts, and config columns reset sales.person to restore defaults. The customer profile includes the DK Plus tax and classification fields, settings values such as VatExempt and Credit.CreditLimit, and counts for contacts, receivers, memos, properties, changes, and attachments. A stored selection that names a removed column (for example Options.VatExempt) stops with a message that asks you to update or reset it. Selections control both visibility and order; --json and generic table records --fields output are unchanged.

product list skips malformed DK Plus rows with no ItemCode and writes a warning with the affected record IDs to stderr, allowing valid products—including products on later pages—to remain available. JSON output on stdout stays valid.

File-producing commands create missing parent directories for explicit output paths. --force is required only to replace an existing file.

Use devop-dk login for a hidden token prompt. devop-dk login <token> is also supported, but can expose the token in shell history and process listings. Login credentials are stored separately from ordinary configuration with user-only file permissions (an owner-only access control list on Windows); on Windows the token is kept in the non-roaming %LOCALAPPDATA%\DevOp\devop-dk folder (a token stored by an earlier version in %APPDATA% is moved on first use, so update the CLI and the PowerShell module together: a 0.4.2 CLI or module reads only the old copy). DKPLUS_API_TOKEN and DKPLUS_API_TOKEN_FILE remain supported and take precedence. Client logging is disabled by default; opt in for one invocation with the global --verbose option, select a level with DKPLUS_LOG_LEVEL, or persist logLevel through devop-dk config set. JSON is written to stdout and enabled diagnostics to stderr.

Every API request includes Accept-Language: en by default. Configure a preferred language with devop-dk config language is-IS, devop-dk config set language icelandic, or DKPLUS_ACCEPT_LANGUAGE; English is automatically retained as the lower-priority fallback. The selected language is also applied as the CLI's current and UI culture. Friendly values icelandic, íslenska, islenska, and english are normalized to standard language tags.

Expected API and configuration errors are rendered without .NET stack traces. Every DK Plus request carries an X-Correlation-Id, and rejected requests print the effective ID so it can be supplied to support even when the response omits the header. Response-contract mismatches identify the operation, JSON path, expected type, and safe endpoint without printing the response body. Recognized API details and correlation IDs are shown safely; generic-table field errors identify unknown fields and suggest devop-dk table fields <name>.

Eligible safe requests use the client's standard resilience profile: three retries with exponential jitter, bounded Retry-After, attempt and total timeouts, and circuit breaking. Mutations are never retried automatically. Destructive and safety-sensitive commands use --yes; ordinary create and update commands do not. token create collects the password directly from an attached terminal through a hidden prompt and uses request-scoped Basic authentication; passwords are never accepted as command-line arguments or redirected input.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.5.0 72 10/13/2026
0.4.2 84 10/12/2026
0.4.1 78 10/12/2026
0.4.0 85 10/11/2026
0.3.1 84 10/11/2026
0.3.0 81 10/11/2026
0.2.7 90 10/10/2026
0.2.6 91 10/9/2026
0.2.4 80 10/9/2026
0.2.3 85 10/9/2026
0.2.2 85 10/9/2026

v0.5.0: the fluent list commands (customer, customer transaction, customer group, product, product group, product category, product unit, employee, sales invoice, order, quote and recurring, sales person, payment mode, term and type, currency, project) gain --all, which reads every record page by page, and --yes (-y). --all cannot be combined with --take and exits with code 2 before any request when both are given. Breaking: --take now accepts 1-1000 on these commands (0.4.2 allowed up to 10,000; the default is still 100) and exits with code 2 before any request above that; use --all to read more. --all asks first on stderr (Fetch every customer? ... [y/N]) because it can move a lot of data; --yes skips the question, and with redirected stdin, stdout or stderr and no --yes the command stops with exit code 2 naming --yes. customer transaction list-for, project all and project work-request list-for always read every record, so they now ask the same way and take --yes. With --json the fluent lists now stream one array record by record; a failure or Ctrl+C after the first page leaves a truncated array on stdout with a non-zero exit code. --include and --exclude (repeatable or comma-separated) narrow the returned properties on the customer, product, customer transaction, customer group, product group, employee, sales invoice, order, quote and recurring, and payment mode, term and type lists; unknown names, sensitive employee fields in --include and identity properties in --exclude exit with code 2 before any request, and a table under --include shows only the named columns. customer list gains the server-side filters --name, --sales-person, --country, --postal-code, --payment-term, --payment-mode, --email, --blocked, --vat-exempt and --deleted, and product list gains --alias-item-code, --inactive, --on-web and --deleted. Text filters and --group are sent lowercase and --country uppercase, and --name without * is a contains search. An ArgumentException error no longer ends with .NET's (Parameter '...') suffix. The boolean filters take a value (--blocked false) because Cocona would read a bare bool option as a flag and ignore a following false; omitting one sends nothing. How DK Plus matches the filter values is not verified live (products document * as a wildcard). New 'table create', 'table update' and 'table delete' write records of any dkERP table through the generic table endpoints: create and update run directly, delete needs --yes; what the key of 'table update' (the record to change) means is not verified live. New 'project delete [number] [--record-id N] --yes' deletes a project by number or record id through the generic table endpoint on pjjob, because DK Plus has no project delete endpoint; the maintainer confirmed that the table name and both key names work; it bypasses DK Plus project validation and its effect on projects with transactions, phases or work requests is not verified live. Give exactly one of the number or --record-id (otherwise exit code 2 before any request); without --yes it exits with code 2 and sends nothing. On Windows, the stored login token now lives in the non-roaming %LOCALAPPDATA%\DevOp\devop-dk folder with an owner-only access control list (a token stored by an earlier version in %APPDATA% is moved on first use, and logout removes both). Update the CLI and the module together: the first read by a new version moves the token, and a 0.4.2 CLI or module reads only the roaming %APPDATA% copy, so it no longer finds the login. Writes of config.json and the token retry briefly when another process holds the file. On every OS, saved file names that are Windows device names (CON, NUL, COM1, ...) get a _ prefix (an attachment named con.pdf is saved as _con.pdf on Linux too). v0.4.2: new sales create commands. 'sales invoice create' builds an invoice from --customer and the single-line options (--item-code, --quantity, --unit-price, ...), from repeatable --line "item-code=A;quantity=4;unit-price=1000" options, or from a --file; --post posts it and requires --yes. 'sales order|quote|recurring create' create the head only (a --file with Lines is rejected) and DK Plus auto-creates a missing customer unless --no-auto-create-customer is set. 'sales order|quote|recurring lines create' add one line. On every create, --file cannot be combined with the direct options, and numbers and dates parse culture-independently. New config setting salesPerson ('devop-dk config set salesPerson S-1'): the default salesperson for sales creates after --sales-person; a --file document's own value wins; without either, DK Plus uses the API user's sales person. v0.4.2: purchase invoice approval --file now rejects a document without ApprovalAction (for example {} or one with only a Description) with exit code 2 and a validation error naming ApprovalAction, before any request is sent; before, the missing value was sent as ApprovalAction 0. v0.4.2: fixes error reporting for failures the client raises before or without contacting DK Plus: a request-model validation failure now exits with code 2 and lists the rejected members instead of reporting that the DK Plus API could not be reached, authentication and authorization failures exit with code 1 with the login hint, and a missing connection, timeout, open circuit, or local concurrency limit has its own message; only real transport failures print the network message. v0.4.1: sales order, sales quote and sales recurring gain update and delete, and a lines group with lines update and lines delete, for v2 documents by UUID and line sequence. update takes direct fields or a sparse SalesOrderUpdateRequest, SalesQuoteUpdateRequest or RecurringSaleUpdateRequest --file; lines update takes direct line fields (--item-code, --quantity, --unit-price and others) or a sparse SalesDocumentLineRequest --file, whose top-level Sequence is rejected because the sequence is the argument. --date, --valid-from and --valid-until take an ISO 8601 date (yyyy-MM-dd) or a date-time with an offset, and the line number options take '.' as the decimal point (2.5), independent of the UI language; other text stops with exit code 2 before any request. Update and line files reject the DK Plus wire spellings (for example Text1, Project, Payment, Dimensions, and on lines Description2, LedgerCode, Discount, Dimentions) naming the request member, and the read-only members of a saved document or line (Amount, TaxAmount, Created, Modified, ...), instead of silently dropping them. An update with neither --file nor a field, or with a file that sets no member, stops with exit code 2 before any request; a Lines list counts only when a line sets a member other than Sequence. delete and lines delete require --yes; order delete and order lines delete take --purge; update and lines update need no --yes. Both update (the head PUT) and lines update (the line PUT) send only the supplied fields; whether DK Plus keeps or resets the unsent fields is unverified live, so try an update on a test company first. The doubled Swagger route that sales quote delete uses is also unverified live. v0.4.0: Breaking: customer create no longer has --contact-number, --contact-name, --contact-email or --contact-mobile, and a customer create or update --file with Contacts is rejected with exit code 2 naming customer contact create, because DK Plus ignores contacts in the customer document; the new customer contact list, get, create, update and delete commands manage contacts (create and update take direct options or a CustomerContactCreateRequest or sparse CustomerContactUpdateRequest --file; delete requires --yes; list uses the new customer.contact column profile). Breaking: customer create no longer has --receiver-number, --receiver-name, --receiver-email or --receiver-phone, a customer create or update --file with Receivers is rejected with exit code 2 naming customer receiver create, and one with Memos is rejected as read-only, because DK Plus ignores receivers and memos in the customer document (customer memos have no write endpoint); the new customer receiver list, get, create, update and delete commands manage item receivers (create and update take direct options or a CustomerReceiverCreateRequest or sparse CustomerReceiverUpdateRequest --file; delete requires --yes; list uses the new customer.receiver column profile). customer contact update and customer receiver update --file with a document that sets no member stop with exit code 2 before any request. Breaking: product and memo --file documents use the new nested request types; a nested QuantityInStock, QuantityOnBackOrders, QuantityPoOrders, QuantityOnHand, LastPurchased* or Modified key is rejected with exit code 2 naming the replacement instead of being sent, and ObjectDate is rejected as read-only in product, customer, vendor and work-request files. warehouse update-product --file now reads a ProductWarehouseSettingsRequest instead of the ProductWarehouse read model: stock quantities (including variation Quantity and QuantityOnBackOrders) are rejected naming inventory journal or count, Modified as read-only, and the earlier wire spellings LocationInWarehouse, DefaultPOQuantity, SkipPOSuggestion, Code2 and Description2 naming their replacements, all with exit code 2 before any request; it is an ordinary update and no longer takes --yes (passing --yes is now an unknown option). General-ledger account create and tax-group create --file now read GeneralLedgerAccountCreateRequest and GeneralLedgerTaxGroupCreateRequest instead of the read models: Modified is rejected as read-only, and the tax-group wire spellings Dim1, Dim2, Dim3, Reconciliate and InTaxType are rejected naming Dimension1, Dimension2, Dimension3, Reconcile and InputTaxType, with exit code 2 before any request. Unknown keys inside vendor Approvers, Dimensions, Expenses, Delivery, BankAccount and PaymentBankAccount are now forwarded to DK Plus instead of failing with exit code 2. JSON output includes ObjectDate, and Modified is no longer filled from it. ObjectDate is the dkPlus stored time (Stored Date) and the one DK Plus pages and filters on for those entities, otherwise Modified: the -m/--modified-since, --modified-after and --modified-before help says so, the default tables of customer, product, vendor, sales invoice, order, quote, recurring sale, project journal and work request show an ObjectDate column (customer transaction JSON output now includes ObjectDate too, and customer transaction list pages on it), and the customer, product, vendor and sales-document get tables add a Stored Date row. Numeric vendor transaction enum fields (all but JournalType) show their Swagger names instead of digits. Project work-entry updates no longer send an empty Properties object. Commands that take a page number and -n/--count now fail locally with exit code 2, before any request is sent, when page × count passes the DK Plus limit of 50,000 records per query, with a hint to narrow the query with a modified-after/modified-before window or another filter and start again from page 1. dimension list, project phase list, project task list and project journals continue past 50,000 records by re-anchoring a modified-after window on the entity's paging field (ObjectDate where the DK Plus list response has it, otherwise Modified); project work-request list-for, which has no modification filter, stops with exit code 2 before passing 50,000 records, as does any list whose window cannot advance safely. v0.3.1: fixes reads that failed on 0.3.0 because DK Plus returns enum fields as numbers (customer, product, employee, product category, vendor transaction and v2 project journal reads); numeric enum values now map to their members, and an unrecognized value reads as null with the raw value kept in AdditionalData. Text enum fields read a number as its Swagger spelling, so portable Project.Status and v2 order and quote Status read InProgress instead of digits (likewise employee Status, token Type, request-log ServiceType, customer transaction RecordType, product category Type, project transaction fields, v2 Origin and project journal Status); a numeric vendor-invoice ApprovedStatus now maps to its member, and an unknown company or permission enum value reads as null instead of failing the read. v0.3.0 (breaking): customer get --expand Options is now --expand Settings; customer JSON output is the flat DkPlusCustomer shape with enum member names and no options object; product update takes the item code as a positional argument; project request is now project work-request, whose list --type and --status take enum names; --file documents are the new CreateRequest and UpdateRequest types; every --file reader now fails on stderr with exit code 2 for an unknown key on a type without extension data, and for a key from the previous release, a renamed or read-only key, or an identity key in an update file, naming the replacement; vendor invoice and vendor transaction JSON keys follow the public names (id, dimension1, Unapproved); config columns selections that name a removed column (for example Options.VatExempt) stop with the existing update-or-reset message and the project.request profile is now project.work-request; a vendor invoice attachment without content reports a contract error; ordinary create and update commands still need no --yes. purchase invoice attachment without --output saves under the sanitized last segment of the server-supplied name in the current directory and never writes elsewhere; product --file documents with read-only keys such as Attachments, Changes or RecordId fail with exit code 2 instead of being silently dropped; employee list and get output (including --json) never contain payroll fields; sales order and quote Status, order DeliveryStatus and project Status in --file documents take enum member names. Earlier features remain: self-update, column selection, safe contract-mismatch errors, correlation IDs and the branded help. v0.4.2: on creates a blank --sales-person (or a blank SalesPerson in the file) counts as not given instead of being sent as an empty string; "sales order|quote|recurring lines create" refuse a line with only blank values; unquoted --line values are trimmed and "reference-line" is accepted as a key; a --number that already exists may update that document (unverified).