Use devop-dk --help to discover top-level groups and append --help at any level, for example devop-dk sales order --help.
| Group | Operations |
|---|---|
| root | login, logout, update |
config |
show, set, language, path; nested columns list/show/select/set/reset |
connection |
bounded read-only connectivity test |
company |
show, status, licence, connection, sync, action |
customer |
list, get, search, create, update, delete; nested transaction, group, contact, and receiver commands |
product |
list, get, price, create, update, delete; nested group, category, and unit lists |
currency |
list |
payment |
nested mode, term, and type list/get commands |
employee |
list, get; payroll fields are never output, including with --json |
project |
list, all, modified, get, create, update, delete, invoices, journals; nested phase, task, work-request, transaction, memo, and work-entry commands |
dimension |
list, optionally restricted to a positional level |
sales |
nested invoice, order, quote, recurring, and person commands |
nation |
get |
table |
records, record, fields, changes, deleted, create, update, delete |
webhook |
list, get, template, create, update, delete, test |
permission |
show |
inventory |
journal, transfer, count |
purchase |
nested vendor and invoice reads, writes, transactions, attachments, and approvals |
warehouse |
list, update-product |
general-ledger |
nested account, transaction, journal, and tax-group reads and writes |
token |
list, get, create, update, delete, companies, employee; nested report logs/pdf/email |
The fluent list commands read 100 records unless --take or --all says otherwise; see Reading more than 100 records. The other list commands (table records, table changes, table deleted, and the direct page commands) are bounded by --take or --count.
devop-dk connection test
devop-dk company show
devop-dk customer list --modified-since 2026-10-01T00:00:00Z --take 100
devop-dk customer get C-100
devop-dk product get ITEM-1
devop-dk product price ITEM-1 --quantity 2 --customer C-100
devop-dk sales order list --take 25 --json
devop-dk warehouse list
--modified-since, --modified-after, and --modified-before send a window that DK Plus applies to the entity's stored time (ObjectDate, shown as "Stored Date") when it has one and to Modified (the dkERP modification time) otherwise. The CLI does not filter client-side, so a record whose Modified is older than the window can still be listed.
Every fluent list command (customer, customer transaction, customer group, product, product group, product category, product unit, employee, sales invoice, sales order, sales quote, sales recurring, sales person, payment mode, payment term, payment type, currency, and project) reads 100 records by default.
--take <n> (-n) sets the maximum, 1 to 1000 (default 100); a larger value exits with code 2 before any request. Use --all to read more: it asks first, so a huge --take cannot bypass the question. Past 50,000 records an --all query continues in a new modified-after window, as the library does (see Paging limit). Breaking in 0.5.0: 0.4.2 accepted up to 10,000, so a script with --take 5000 must now use --all.--all reads every record, page by page, and cannot be combined with --take (exit code 2 before any request). Because it can move a lot of data it asks first: Fetch every customer? This reads every page from DK Plus and may use a lot of data. [y/N] is written to stderr and one line is read from stdin. --yes (-y) skips the question. When stdin, stdout, or stderr is redirected and --yes is missing, the command stops with exit code 2 naming --yes, before any request. This is the one read that takes --yes: it confirms the data volume, not a change.customer transaction list-for, project all, and project work-request list-for have no --all because they always read every record; they ask the same question and take --yes.--json, --all streams one JSON array record by record, so a very large result never sits in memory. A failure or Ctrl+C after the first page leaves a truncated array on stdout together with a non-zero exit code. A table is buffered because its layout needs every row, so prefer --json for a very large list.--include and --exclude (repeat the option or separate names with commas) narrow the properties DK Plus returns. They exist on the 13 lists whose resource supports projection: customer, product, customer transaction, customer group, product group, employee, sales invoice, sales order, sales quote, sales recurring, payment mode, payment term, and payment type. Names are the public property names (Number, Name, VatNumber); an unknown name, a sensitive employee field in --include, or an identity property in --exclude stops with exit code 2 before any request. The request always carries the identity. A table under --include shows only the columns of the named properties, in the order named.devop-dk customer list --all --yes --json > customers.json
devop-dk customer list --include Number,Name --take 500 --json
devop-dk customer list --exclude Contacts --all --yes --json
devop-dk project list --all --yes
devop-dk project all --customer C-100 --yes
devop-dk project work-request list-for P-100 --yes
customer list and product listThe filters are sent to DK Plus and applied by the server together with the modified window; the CLI applies none of them itself. DK Plus matches the customer group, name, and email only in lowercase, so the client sends the text filters lowercase (--group, --name, --sales-person, --postal-code, --payment-term, --payment-mode, --email, and the product --group) and --country uppercase. --name without * is a contains search (--name Jón sends name=*jón*); with * the value is a wildcard pattern (quote it in the shell: --name "jón*"), and a value with several words matches nothing. --alias-item-code is sent as written and * is a wildcard. Live (2026-10-13), letter country codes such as IS match no customers while numeric codes do.
| Command | Options |
|---|---|
customer list |
--group (-g), --name, --sales-person, --country, --postal-code, --payment-term, --payment-mode, --email, --blocked, --vat-exempt, --deleted |
product list |
--item-code (-i), --group (-g), --alias-item-code, --inactive, --on-web, --deleted |
The boolean filters (--blocked, --vat-exempt, --deleted, --inactive, --on-web) need a value: --blocked true or --blocked false. Omitting the option sends no filter. What --blocked false, --inactive false, and --deleted true mean to DK Plus is not verified.
devop-dk customer list --sales-person S-7 --name Jón --blocked false --email [email protected]
devop-dk product list --on-web true --alias-item-code "BOLT*"
devop-dk currency list
devop-dk payment mode get BANK
devop-dk payment term list
devop-dk customer group list
devop-dk product group list
devop-dk product category list
devop-dk product unit list --page-size 100
devop-dk customer transaction list --customer C-100 --take 100
devop-dk project phase link list P-100
devop-dk project transaction list --project P-100
devop-dk general-ledger account list
devop-dk general-ledger account transaction list 1000 --count 100
DK Plus serves at most 50,000 records per query (page x count). Commands that take a page number and --count (for example customer transaction page, purchase vendor page, project work-request list, project transaction list, general-ledger transaction list, and general-ledger account transaction list) reject a page and count whose product is above 50,000 before any request is sent. The error is written to stderr with exit code 2 and suggests narrowing the query with a modified-after/modified-before window or another filter and starting again from page 1. Commands that read every page (dimension list, project phase list, project task list, and project journals), and the fluent list commands with --all, continue past the limit automatically. --take is at most 1000, so it always fits in one window. The CLI does not expose continuation tokens.
devop-dk customer create --number C-100 --name "Example customer" --group ALM
devop-dk purchase vendor create --number V-100 --name "Example supplier" --group LOCAL
devop-dk product create --item-code ITEM-1 --description "Example item" --price 100
devop-dk product update ITEM-1 --description "Renamed item" --price 120
devop-dk customer update C-100 --file customer-update.json
devop-dk warehouse update-product MAIN ITEM-1 --file ./warehouse-product.json
Explicit create and update commands execute directly because the command itself expresses intent. Destructive and safety-sensitive actions use --yes; see Automation and safety.
The entity to update is a positional argument (product update <itemCode>, customer update <number>). Update commands send only the values you supply: with direct options, an option you do not pass is not sent, and a --file update document is a sparse {Entity}UpdateRequest that contains no identity. For example, customer-update.json can contain only the intended change:
{"address":{"lines":["Example street 1"]}}
Customer deletion supports the optional --purge switch.
Contacts, item receivers, and memos are not part of the customer document; DK Plus ignores them there. customer create therefore has no --contact-* or --receiver-* options, a customer --file containing Contacts or Receivers is rejected with a pointer to the commands below, and one containing Memos is rejected as read-only (customer memos have no write endpoint). All three still appear in customer get output.
Contacts and receivers have their own commands. The customer number (and, for get, update, and delete, the contact or receiver id) are positional arguments:
devop-dk customer contact list C-100
devop-dk customer contact get C-100 1 --json
devop-dk customer contact create C-100 --number 1 --name "Accounts payable" --email [email protected] --mobile 5551234
devop-dk customer contact update C-100 1 --title CFO
devop-dk customer contact update C-100 1 --file contact-update.json
devop-dk customer contact delete C-100 1 --yes
devop-dk customer receiver list C-100
devop-dk customer receiver create C-100 --number MAIN --name "Main delivery" --email [email protected]
devop-dk customer receiver update C-100 MAIN --phone 5551234
devop-dk customer receiver delete C-100 MAIN --yes
create --file reads a CustomerContactCreateRequest or CustomerReceiverCreateRequest; update --file reads the sparse ...UpdateRequest. When --file is set, the direct field options are ignored.Number (the key is rejected), Mobile is rejected in favour of MobilePhone, and the read-only Modified key of a saved get --json row is rejected. A receiver document also rejects Customer, because the customer number is the argument.create prints a success line, or the record returned by DK Plus with --json. Contact update does the same; receiver update prints only a success line, because DK Plus returns no document for it.delete changes remote state and requires --yes; create and update do not.contact list and receiver list use the customer.contact and customer.receiver column profiles.table create, table update, and table delete write records of any dkERP table through the generic table endpoints. Values are strings (dkERP text), given as repeatable --value Field=Value options (split on the first =) or as a flat JSON object with --file; the two are mutually exclusive, and invalid or missing input stops with exit code 2 before any request. create and update run directly like other ordinary writes; only delete requires --yes. The <key> argument of update is the record key as DK Plus interprets it (for example the customer number for ARCUST); this has not been verified live, so try it on a test company first.
devop-dk table create ARCUST --value Number=C-100 --value Name="Acme = Ltd"
devop-dk table update ARCUST C-100 --value Name="Acme Ltd" --json
devop-dk table delete ARCUST --key Number --value C-100 --yes
See Generic tables for reads. The table list commands (table records, table changes, table deleted) are bounded by --take (default 100) and have no --all.
DK Plus has no project DELETE endpoint. project delete removes a project by its number (positional) or by --record-id (the RecordId of project get --json) through the generic table endpoint on the dkERP project table pjjob:
devop-dk project delete P-100 --yes
devop-dk project delete --record-id 1111 --yes
Give exactly one of the number and --record-id, otherwise the command stops with exit code 2 before any request; without --yes it also stops with exit code 2 and sends nothing. The command skips DK Plus project validation, and what dkERP does with a project that has transactions, phases, or work requests is not verified live, so try it on a test company first.
Every command that takes --file reads one of the request types, and documents use the public member names (case-insensitive camel case, enum member names):
| Command | Document type |
|---|---|
customer create, customer update <number> |
CustomerCreateRequest, CustomerUpdateRequest |
customer contact create <customer>, ... update <customer> <id> |
CustomerContactCreateRequest, CustomerContactUpdateRequest |
customer receiver create <customer>, ... update <customer> <id> |
CustomerReceiverCreateRequest, CustomerReceiverUpdateRequest |
product create, product update <itemCode> |
ProductCreateRequest, ProductUpdateRequest |
warehouse update-product <warehouse> <product> |
ProductWarehouseSettingsRequest |
project create, project update <number> |
ProjectCreateRequest, ProjectUpdateRequest |
project phase|task create, ... update <number> |
ProjectPhaseCreateRequest/ProjectPhaseUpdateRequest, ProjectTaskCreateRequest/ProjectTaskUpdateRequest |
project work-request create, ... update <id> |
WorkRequestCreateRequest, WorkRequestUpdateRequest |
purchase vendor create, purchase vendor update <number> |
VendorCreateRequest, VendorUpdateRequest |
purchase invoice create <vendor> |
VendorInvoiceCreateRequest |
sales person create, sales person update <number> |
SalesPersonCreateRequest, SalesPersonUpdateRequest |
sales invoice create |
SalesInvoiceCreateRequest (at least one line) |
sales order\|quote\|recurring create |
SalesOrderCreateRequest, SalesQuoteCreateRequest, RecurringSaleCreateRequest (without Lines) |
sales order\|quote\|recurring lines create <uuid> |
SalesDocumentLineRequest (without Sequence) |
general-ledger account create, general-ledger tax-group create |
GeneralLedgerAccountCreateRequest, GeneralLedgerTaxGroupCreateRequest |
Update documents do not contain the identity; the argument supplies it. Null and omitted members are not sent, so they leave existing values unchanged. --file input is strict for every command that reads a file, including inventory, general-ledger, warehouse, and work-entry documents. A document type without an AdditionalData escape hatch accepts only its own properties: any other key is rejected with a message naming the key, its JSON path, and the type. A type with AdditionalData still forwards unknown keys to DK Plus, except keys an earlier release used and the current type renamed, moved, or made read-only, and the identity in an update document (Number, ItemCode, UId, ...); those are rejected with the replacement named. The error is written to stderr as Error: ... with exit code 2 before any token lookup or network request, and stdout stays empty. Read the message, fix the key, and retry.
Since 0.4.0 the same rule covers nested rows, which are read into dedicated request types:
Barcodes, Warehouses, Variations, Units, and Vendors rows use their own request types (ProductBarcodeRequest, ProductWarehouseSettingsRequest, ProductVariationRequest, ProductUnitConversionRequest, ProductVendorRequest). A nested stock quantity (QuantityInStock, QuantityOnBackOrders, QuantityOnPurchaseOrders, a variation Quantity, a unit QuantityOnHand), a vendor LastPurchased* value, or a barcode, warehouse, or memo Modified is rejected with its JSON path, for example $.Warehouses[0].QuantityInStock. Change stock with devop-dk inventory journal or inventory count, never through a product document.MemoPageRequest; a memo Modified is rejected, while a memo RecordId is still accepted and sent as the row match key.warehouse update-product reads one ProductWarehouseSettingsRequest (before 0.4.0 it read the ProductWarehouse read model and sent stock quantities). The earlier wire spellings LocationInWarehouse, DefaultPOQuantity, SkipPOSuggestion, and variation Code2/Description2 are rejected, naming Location, DefaultPurchaseQuantity, SkipPurchaseOrderSuggestions, SecondaryCode, and SecondaryDescription. The command needs no --yes.general-ledger account create and tax-group create read the dedicated create requests (before 0.4.0 they read the read models and sent Modified). A Modified key, for example in a row saved from general-ledger account get --json, is rejected. In a tax-group document Dim1, Dim2, Dim3, Reconciliate, and InTaxType are rejected, naming Dimension1, Dimension2, Dimension3, Reconcile, and InputTaxType.get --json read, which includes read-only members such as ObjectDate, RecordId, Attachments, or Changes, is rejected rather than silently trimmed. ObjectDate is read-only in customer, product, vendor, and work-request documents.Approvers, Dimensions, Expenses, Delivery, BankAccount, and PaymentBankAccount objects are forwarded to DK Plus, because those types carry AdditionalData.Read-model JSON output changed shape in 0.3.0: output is the flat DkPlus* subtype with enum member names, and vendor-invoice and vendor-transaction keys follow the public names (id, dimension1, the approval status Unapproved) instead of the DK Plus spelling. Since 0.4.0 the output also carries objectDate for entities that have one.
sales invoice has list, get, create, pdf, html, and email. create builds an invoice from direct options, repeatable --line values, or a SalesInvoiceCreateRequest file:
devop-dk sales invoice create -c C-100 -i ABCD -q 4 -p 1000
devop-dk sales invoice create -c C-100 --line "item-code=ABCD;quantity=4;unit-price=1000" --line "item-code=EFGH;description=\"Fitting; 2=1 adapter\""
devop-dk sales invoice create --file ./invoice.json
devop-dk sales invoice create -c C-100 -i ABCD --post --yes
--item-code (-i) with --quantity (-q), --unit-price (-p), --description, --description2, --memo, --warehouse, --reference-line, --discount-percent, --discount-amount, --total, --dimension1, and --prices-include-tax makes a one-line invoice. Each --line adds one line instead. The single-line options and --line cannot be combined.--line grammar. A value is key=value pairs separated by ;. The keys are item-code (required), quantity (default 1), unit-price, description, description2, memo, warehouse, reference, discount-percent, discount-amount, total, dimension1, and prices-include-tax (true or false). Keys are case-insensitive and hyphens are ignored, so item-code, itemcode, and ItemCode are the same key, and the request member names are accepted too. A value may be double-quoted; inside the quotes ; and = are literal and "" is one quote character. A value that starts with { is one JSON SalesInvoiceLineRequest. Numbers use . as the decimal point whatever the UI language (4,5 is rejected). A missing =, an unknown or repeated key, and a line without item-code stop with exit code 2 before any request, and the message names the line (--line 2: item-code is required.). The DK Plus v1 wire spellings Price, Text, Text2, IncludingVAT, Discount, and Dim1 are rejected naming the key to use.--customer (-c) is required unless --file is used. --reference, --date (default: now), --due-date, --currency, --exchange-rate, --payment-term, --payment-mode, --header-text1, --header-text2, --project, --customer-order-number, --customer-order-date, --sale-type (default Debit), and --original-prices set the head. When --due-date or --currency is omitted it is not sent, and DK Plus derives it. An invoice file without lines is refused before any request.--file replaces the direct options. Combining them stops with exit code 2 before any request.--sales-person wins, then the salesPerson setting (devop-dk config set salesPerson S-7; see Setup and authentication); with --file, the document's own SalesPerson wins and the setting only fills a document that has none. Without any of them the member is not sent and DK Plus uses the sales person of the API user's employee. A blank value counts as not given.--post the request carries post=false and DK Plus creates the invoice unposted. --post posts it, which is irreversible, and requires --yes: --post without --yes stops with exit code 2 before any request, and --yes without --post has no effect.Invoice 'INV-123' was created (record 4711, posted: no)., or with --json the returned invoice.sales order, sales quote, and sales recurring have list and get; orders and quotes also have pdf, html, and email. Since 0.4.1 they have update and delete and lines update and lines delete (by document UUID and line sequence), and since 0.4.2 create and lines create:
devop-dk sales order create -c C-100 --reference PO-77 --status OnHold
devop-dk sales order create -c C-100 --no-auto-create-customer
devop-dk sales quote create --file ./quote.json
devop-dk sales order lines create 38e492ef-6169-43e1-8798-4d06ab60ee87 --item-code ABCD --quantity 2.5
devop-dk sales recurring lines create 0b7e1a50-2f7a-4f1e-8f55-1c3f9d6b4a22 --file ./line.json
create makes the head only. One command creates one thing, so the head comes first and each line is a lines create. A --file (SalesOrderCreateRequest, SalesQuoteCreateRequest, or RecurringSaleCreateRequest) must not contain Lines.--customer (-c, required unless --file), --number, --reference, --date, --sales-person, --warehouse, --header-text1, --header-text2, --project, --payment-term, --payment-mode, --currency, --exchange-rate, --discount-amount, --dimension1 to --dimension3, and --reason-code. Orders add --status, --delivery-status, --customer-order-number, --customer-order-date, and --group; quotes add --status, --valid-from, and --valid-until; recurring sales add --group. --file replaces the direct options: combining them stops with exit code 2 before any request.--no-auto-create-customer sends AutoCreateCustomer=false; what DK Plus does with false and a missing customer is not verified live.--number can address an existing document. A --number that already exists may make DK Plus update that document instead of creating a new one (an upsert, not verified live), so give --number only when you mean to address a specific document.--sales-person, then the salesPerson setting, otherwise omitted. A blank value counts as not given (on update, "" still clears).lines create <uuid> takes the same line fields as lines update or a SalesDocumentLineRequest --file without Sequence (DK Plus assigns it), and needs at least one non-blank member. Order lines create prints Line 20000 was added to sales order '<uuid>'. and supports --json; DK Plus returns no line for quotes and recurring sales, so their lines create prints Line was added to ... and has no --json.--yes. delete and lines delete require it.Sales order 12345 was created (<uuid>)., or with --json the returned document.For the library behavior behind these commands, including how the client writes a head and its lines, see Sales documents.
project work-request replaces the former project request group (the DK Plus entity is a work or service request):
devop-dk project work-request list --project P-100
--type and --status take the enum member names (WorkRequestType: None, Job, Other, Booking; WorkRequestStatus: NotStarted, InProgress, OnHold, Finished, PreWork, OrderingParts, Cancelled, ReadyToBill, Billed, Arrived), not the DK Plus wire spellings. Creating and updating a work request needs no --yes; deleting one does.
--take limit, --all/--yes, typed filters, and the Windows login locationDkPlus* subtypes, and the create and sparse update requests