noxctl 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +279 -29
- package/dist/auth.d.ts +15 -7
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +128 -22
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts +2 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1640 -70
- package/dist/cli.js.map +1 -1
- package/dist/completions.d.ts +13 -0
- package/dist/completions.d.ts.map +1 -0
- package/dist/completions.js +150 -0
- package/dist/completions.js.map +1 -0
- package/dist/config-paths.d.ts +2 -0
- package/dist/config-paths.d.ts.map +1 -0
- package/dist/config-paths.js +7 -0
- package/dist/config-paths.js.map +1 -0
- package/dist/credentials-store.d.ts +13 -3
- package/dist/credentials-store.d.ts.map +1 -1
- package/dist/credentials-store.js +197 -62
- package/dist/credentials-store.js.map +1 -1
- package/dist/date-periods.d.ts +14 -0
- package/dist/date-periods.d.ts.map +1 -0
- package/dist/date-periods.js +103 -0
- package/dist/date-periods.js.map +1 -0
- package/dist/formatter.d.ts +11 -2
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +27 -4
- package/dist/formatter.js.map +1 -1
- package/dist/fortnox-client.d.ts +1 -0
- package/dist/fortnox-client.d.ts.map +1 -1
- package/dist/fortnox-client.js +28 -5
- package/dist/fortnox-client.js.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +92 -2
- package/dist/index.js.map +1 -1
- package/dist/keychain-target.d.ts +32 -0
- package/dist/keychain-target.d.ts.map +1 -0
- package/dist/keychain-target.js +349 -0
- package/dist/keychain-target.js.map +1 -0
- package/dist/operations/analytics.d.ts +84 -0
- package/dist/operations/analytics.d.ts.map +1 -0
- package/dist/operations/analytics.js +154 -0
- package/dist/operations/analytics.js.map +1 -0
- package/dist/operations/contracts.d.ts +23 -0
- package/dist/operations/contracts.d.ts.map +1 -0
- package/dist/operations/contracts.js +52 -0
- package/dist/operations/contracts.js.map +1 -0
- package/dist/operations/costcenters.d.ts +20 -0
- package/dist/operations/costcenters.d.ts.map +1 -0
- package/dist/operations/costcenters.js +38 -0
- package/dist/operations/costcenters.js.map +1 -0
- package/dist/operations/customers.d.ts.map +1 -1
- package/dist/operations/customers.js +12 -2
- package/dist/operations/customers.js.map +1 -1
- package/dist/operations/financial-years.d.ts +16 -0
- package/dist/operations/financial-years.d.ts.map +1 -0
- package/dist/operations/financial-years.js +32 -0
- package/dist/operations/financial-years.js.map +1 -0
- package/dist/operations/invoice-payments.d.ts +21 -0
- package/dist/operations/invoice-payments.d.ts.map +1 -0
- package/dist/operations/invoice-payments.js +40 -0
- package/dist/operations/invoice-payments.js.map +1 -0
- package/dist/operations/offers.d.ts +25 -0
- package/dist/operations/offers.d.ts.map +1 -0
- package/dist/operations/offers.js +48 -0
- package/dist/operations/offers.js.map +1 -0
- package/dist/operations/orders.d.ts +24 -0
- package/dist/operations/orders.d.ts.map +1 -0
- package/dist/operations/orders.js +44 -0
- package/dist/operations/orders.js.map +1 -0
- package/dist/operations/pricelists.d.ts +36 -0
- package/dist/operations/pricelists.d.ts.map +1 -0
- package/dist/operations/pricelists.js +52 -0
- package/dist/operations/pricelists.js.map +1 -0
- package/dist/operations/projects.d.ts +19 -0
- package/dist/operations/projects.d.ts.map +1 -0
- package/dist/operations/projects.js +33 -0
- package/dist/operations/projects.js.map +1 -0
- package/dist/operations/supplier-invoice-payments.d.ts +20 -0
- package/dist/operations/supplier-invoice-payments.d.ts.map +1 -0
- package/dist/operations/supplier-invoice-payments.js +34 -0
- package/dist/operations/supplier-invoice-payments.js.map +1 -0
- package/dist/operations/taxreductions.d.ts +19 -0
- package/dist/operations/taxreductions.d.ts.map +1 -0
- package/dist/operations/taxreductions.js +28 -0
- package/dist/operations/taxreductions.js.map +1 -0
- package/dist/operations/vouchers.d.ts +15 -0
- package/dist/operations/vouchers.d.ts.map +1 -1
- package/dist/operations/vouchers.js +97 -0
- package/dist/operations/vouchers.js.map +1 -1
- package/dist/profile-name.d.ts +9 -0
- package/dist/profile-name.d.ts.map +1 -0
- package/dist/profile-name.js +58 -0
- package/dist/profile-name.js.map +1 -0
- package/dist/profiles.d.ts +55 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +210 -0
- package/dist/profiles.js.map +1 -0
- package/dist/tools/analytics.d.ts +3 -0
- package/dist/tools/analytics.d.ts.map +1 -0
- package/dist/tools/analytics.js +52 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/bookkeeping.d.ts.map +1 -1
- package/dist/tools/bookkeeping.js +38 -2
- package/dist/tools/bookkeeping.js.map +1 -1
- package/dist/tools/contracts.d.ts +3 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +136 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/costcenters.d.ts +3 -0
- package/dist/tools/costcenters.d.ts.map +1 -0
- package/dist/tools/costcenters.js +79 -0
- package/dist/tools/costcenters.js.map +1 -0
- package/dist/tools/financial-years.d.ts +3 -0
- package/dist/tools/financial-years.d.ts.map +1 -0
- package/dist/tools/financial-years.js +33 -0
- package/dist/tools/financial-years.js.map +1 -0
- package/dist/tools/invoice-payments.d.ts +3 -0
- package/dist/tools/invoice-payments.d.ts.map +1 -0
- package/dist/tools/invoice-payments.js +60 -0
- package/dist/tools/invoice-payments.js.map +1 -0
- package/dist/tools/offers.d.ts +3 -0
- package/dist/tools/offers.d.ts.map +1 -0
- package/dist/tools/offers.js +130 -0
- package/dist/tools/offers.js.map +1 -0
- package/dist/tools/orders.d.ts +3 -0
- package/dist/tools/orders.d.ts.map +1 -0
- package/dist/tools/orders.js +114 -0
- package/dist/tools/orders.js.map +1 -0
- package/dist/tools/pricelists.d.ts +3 -0
- package/dist/tools/pricelists.d.ts.map +1 -0
- package/dist/tools/pricelists.js +104 -0
- package/dist/tools/pricelists.js.map +1 -0
- package/dist/tools/projects.d.ts +3 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +80 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/status.d.ts.map +1 -1
- package/dist/tools/status.js +6 -1
- package/dist/tools/status.js.map +1 -1
- package/dist/tools/supplier-invoice-payments.d.ts +3 -0
- package/dist/tools/supplier-invoice-payments.d.ts.map +1 -0
- package/dist/tools/supplier-invoice-payments.js +60 -0
- package/dist/tools/supplier-invoice-payments.js.map +1 -0
- package/dist/tools/taxreductions.d.ts +3 -0
- package/dist/tools/taxreductions.d.ts.map +1 -0
- package/dist/tools/taxreductions.js +51 -0
- package/dist/tools/taxreductions.js.map +1 -0
- package/dist/views.d.ts +28 -0
- package/dist/views.d.ts.map +1 -1
- package/dist/views.js +234 -0
- package/dist/views.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,14 +39,21 @@ Fortnox product plans, API activation requirements, and integration licensing ca
|
|
|
39
39
|
- Copy your **Client ID** and **Client Secret**
|
|
40
40
|
4. On the **Integration** tab, enable these scopes under **Behörigheter** / **Permissions**:
|
|
41
41
|
|
|
42
|
-
| Swedish (SV) | English (EN) |
|
|
43
|
-
|
|
44
|
-
| Artikel | Article |
|
|
45
|
-
| Bokföring | Bookkeeping |
|
|
46
|
-
| Faktura | Invoice |
|
|
47
|
-
| Företagsinformation | Company Information |
|
|
48
|
-
| Inställningar | Settings |
|
|
49
|
-
| Kund | Customer |
|
|
42
|
+
| Swedish (SV) | English (EN) | Needed for |
|
|
43
|
+
|----------------------|---------------------|------------------------------------------------|
|
|
44
|
+
| Artikel | Article | Articles, prices, price lists |
|
|
45
|
+
| Bokföring | Bookkeeping | Vouchers, accounts, financial reports |
|
|
46
|
+
| Faktura | Invoice | Invoices, invoice payments, offers, orders, tax reductions |
|
|
47
|
+
| Företagsinformation | Company Information | Company info |
|
|
48
|
+
| Inställningar | Settings | Financial year, locked period |
|
|
49
|
+
| Kund | Customer | Customers |
|
|
50
|
+
| Leverantör | Supplier | Suppliers |
|
|
51
|
+
| Leverantörsfaktura | Supplier Invoice | Supplier invoices, supplier invoice payments |
|
|
52
|
+
| Projekt | Project | Projects |
|
|
53
|
+
| Kostnadsställe | Cost Center | Cost centers |
|
|
54
|
+
| Priser | Price | Price lists, prices |
|
|
55
|
+
|
|
56
|
+
Enable every scope for the resources you intend to use. Missing scopes surface as `403 Forbidden` with a hint pointing at the right one.
|
|
50
57
|
|
|
51
58
|
5. Save the integration
|
|
52
59
|
|
|
@@ -108,45 +115,283 @@ node dist/cli.js company info
|
|
|
108
115
|
|
|
109
116
|
You should see your company name, organisation number, and address. If this works, you're all set.
|
|
110
117
|
|
|
118
|
+
## Profiles (multi-tenant)
|
|
119
|
+
|
|
120
|
+
noxctl supports multiple Fortnox tenants from a single installation — useful if you bookkeep for several companies, or want to keep a sandbox tenant separate from production. Each profile has its own OAuth credentials in the OS secure store (macOS Keychain / Linux Secret Service / Windows DPAPI), keyed by profile name.
|
|
121
|
+
|
|
122
|
+
### Running against a specific profile
|
|
123
|
+
|
|
124
|
+
Three ways to pick the profile, in precedence order (highest wins):
|
|
125
|
+
|
|
126
|
+
1. **`--profile <name>` flag** — explicit, per-command
|
|
127
|
+
2. **`NOXCTL_PROFILE` environment variable** — scoped to a shell session
|
|
128
|
+
3. **Active pointer** — `~/.fortnox-mcp/active-profile`, set by `noxctl profile use`
|
|
129
|
+
|
|
130
|
+
If none of these is set, noxctl uses the `default` profile (what existing users have been using all along — no opt-in required).
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
noxctl init --profile staging # authorize a second tenant
|
|
134
|
+
noxctl --profile staging invoices list # one-off against staging
|
|
135
|
+
NOXCTL_PROFILE=staging noxctl company info # shell-scoped
|
|
136
|
+
noxctl profile use staging # sticky — persists to the pointer
|
|
137
|
+
noxctl profile current # show resolved profile + source
|
|
138
|
+
noxctl profile list # list known profiles
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### MCP server
|
|
142
|
+
|
|
143
|
+
When launched by Claude Desktop / claude.ai, the MCP server resolves the profile from `NOXCTL_PROFILE` and the active pointer at startup, and binds for the session. When launched via `noxctl --profile <name> serve`, the CLI forwards the flag.
|
|
144
|
+
|
|
145
|
+
To run multiple MCP servers (one per tenant) in parallel, register them with distinct names and scoped environments:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
claude mcp add fortnox-prod -- npx noxctl serve
|
|
149
|
+
claude mcp add fortnox-staging -e NOXCTL_PROFILE=staging -- npx noxctl serve
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Non-default sessions print a `[profile: <name>]` stderr banner on startup and prefix every Fortnox API error and token-refresh failure with the same tag so mis-bound sessions are diagnosable from a single error line.
|
|
153
|
+
|
|
154
|
+
### Fail-closed pointer semantics
|
|
155
|
+
|
|
156
|
+
If the active pointer becomes unreadable or corrupt and no explicit `--profile` flag or `NOXCTL_PROFILE` is set, `noxctl serve` **refuses to start** rather than silently falling back to `default`. This prevents a corrupted pointer from routing production MCP sessions to the wrong tenant. The CLI's `doctor` and `profile use` commands are exempt — they can still run against a broken pointer so you can repair it.
|
|
157
|
+
|
|
158
|
+
## YubiKey-locked keychain (macOS)
|
|
159
|
+
|
|
160
|
+
By default, credentials live in your macOS **login keychain**, which unlocks automatically when you log in. That's convenient, but it means an AI agent (or anything running as you) can read the tokens without a per-session gesture from you.
|
|
161
|
+
|
|
162
|
+
The optional **dedicated keychain** moves credentials into a separate, lock-on-sleep keychain whose password is derived from your YubiKey via HMAC-SHA1 challenge-response. You unlock it once per session with a single tap; it re-locks when your Mac sleeps. The keychain password is never typed or stored — it only exists on the YubiKey.
|
|
163
|
+
|
|
164
|
+
This is opt-in and macOS-only. It does not change how credentials are stored on Linux or Windows.
|
|
165
|
+
|
|
166
|
+
### One-time YubiKey setup
|
|
167
|
+
|
|
168
|
+
Program OTP slot 2 for challenge-response (requires touch). **This writes only the empty OTP slot 2** — FIDO2, PIV, OATH, OpenPGP, and slot 1 are untouched, and it is reversible with `ykman otp delete 2`.
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
brew install ykman # if not already installed
|
|
172
|
+
ykman otp chalresp --generate --touch 2 # program slot 2
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Enable it
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
noxctl keychain init # generate challenge, tap to derive the password,
|
|
179
|
+
# create the locked keychain, copy existing creds in
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
`init` uses **copy-and-keep**: your existing login-keychain credentials are copied into the new keychain but left in place as a rollback. Once you've confirmed the new flow works, remove the originals:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
noxctl keychain seal # delete the login-keychain copies (irreversible)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Until you `seal`, the login copies remain readable without a tap — so the per-session protection isn't fully in effect.
|
|
189
|
+
|
|
190
|
+
### Daily use
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
noxctl keychain unlock # tap your YubiKey — open until the Mac next sleeps
|
|
194
|
+
noxctl keychain status # mode, lock state, ykman/YubiKey presence
|
|
195
|
+
noxctl keychain lock # lock immediately
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
When the keychain is locked, any command that needs credentials fails fast with a message telling you to run `noxctl keychain unlock` — it never pops a macOS password dialog (the challenge-response password can't be typed into one).
|
|
199
|
+
|
|
200
|
+
### Recovery if you lose the YubiKey
|
|
201
|
+
|
|
202
|
+
The keychain password lives only on the key, so a lost or re-programmed key means the dedicated keychain can't be unlocked. As long as you have **not** run `seal`, your credentials are still in the login keychain — delete the dedicated keychain and challenge file to fall back:
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
security delete-keychain ~/Library/Keychains/fortnox-mcp.keychain-db
|
|
206
|
+
rm ~/.fortnox-mcp/keychain-challenge
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
If you *have* sealed, re-run `noxctl init` to re-authenticate from scratch.
|
|
210
|
+
|
|
111
211
|
## Tools
|
|
112
212
|
|
|
113
|
-
Every operation is available both as a CLI command and as an MCP tool. The CLI is the primary interface; the MCP server exposes the same operations to AI agents.
|
|
213
|
+
Every operation is available both as a CLI command and as an MCP tool. The CLI is the primary interface; the MCP server exposes the same operations to AI agents. All mutations — every row labeled `(mutation)` — prompt for confirmation on a TTY and require `--yes` (CLI) or `confirm: true` (MCP) when piped. See [Mutation safety](#mutation-safety).
|
|
114
214
|
|
|
115
215
|
### Customers
|
|
116
216
|
|
|
117
217
|
| CLI | MCP tool | Description |
|
|
118
218
|
|-----|----------|-------------|
|
|
119
219
|
| `noxctl customers list [--search <term>]` | `fortnox_list_customers` | List/search customers |
|
|
120
|
-
| `noxctl customers get <number>` | `fortnox_get_customer` | Get a single customer
|
|
121
|
-
| `noxctl customers create --name <name>` | `fortnox_create_customer` | Create a
|
|
122
|
-
| `noxctl customers update <number> --input <file>` | `fortnox_update_customer` | Update
|
|
220
|
+
| `noxctl customers get <number>` | `fortnox_get_customer` | Get a single customer |
|
|
221
|
+
| `noxctl customers create --name <name>` | `fortnox_create_customer` | Create a customer (mutation) |
|
|
222
|
+
| `noxctl customers update <number> --input <file>` | `fortnox_update_customer` | Update a customer (mutation) |
|
|
223
|
+
|
|
224
|
+
### Suppliers
|
|
225
|
+
|
|
226
|
+
| CLI | MCP tool | Description |
|
|
227
|
+
|-----|----------|-------------|
|
|
228
|
+
| `noxctl suppliers list [--search <term>]` | `fortnox_list_suppliers` | List/search suppliers |
|
|
229
|
+
| `noxctl suppliers get <number>` | `fortnox_get_supplier` | Get a single supplier |
|
|
230
|
+
| `noxctl suppliers create --name <name>` | `fortnox_create_supplier` | Create a supplier (mutation) |
|
|
231
|
+
| `noxctl suppliers update <number> --input <file>` | `fortnox_update_supplier` | Update a supplier (mutation) |
|
|
232
|
+
|
|
233
|
+
### Articles
|
|
234
|
+
|
|
235
|
+
| CLI | MCP tool | Description |
|
|
236
|
+
|-----|----------|-------------|
|
|
237
|
+
| `noxctl articles list [--search <term>]` | `fortnox_list_articles` | List/search articles |
|
|
238
|
+
| `noxctl articles get <number>` | `fortnox_get_article` | Get a single article |
|
|
239
|
+
| `noxctl articles create --description <text>` | `fortnox_create_article` | Create an article (mutation) |
|
|
240
|
+
| `noxctl articles update <number> --input <file>` | `fortnox_update_article` | Update an article (mutation) |
|
|
123
241
|
|
|
124
242
|
### Invoices
|
|
125
243
|
|
|
126
244
|
| CLI | MCP tool | Description |
|
|
127
245
|
|-----|----------|-------------|
|
|
128
246
|
| `noxctl invoices list [--filter <status>] [--customer <number>]` | `fortnox_list_invoices` | List/filter invoices. Filters: `cancelled`, `fullypaid`, `unpaid`, `unpaidoverdue`, `unbooked` |
|
|
129
|
-
| `noxctl invoices get <docNumber>` | `fortnox_get_invoice` | Get a single invoice
|
|
130
|
-
| `noxctl invoices create --customer <number> --input <file>` | `fortnox_create_invoice` | Create an invoice
|
|
247
|
+
| `noxctl invoices get <docNumber>` | `fortnox_get_invoice` | Get a single invoice |
|
|
248
|
+
| `noxctl invoices create --customer <number> --input <file>` | `fortnox_create_invoice` | Create an invoice (mutation) |
|
|
131
249
|
| `noxctl invoices update <docNumber> --input <file>` | `fortnox_update_invoice` | Update an invoice that has not been bookkeept (mutation) |
|
|
132
|
-
| `noxctl invoices send <docNumber> [--method email\|print\|einvoice] [--subject <s>] [--body <s>] [--bcc <email>]` | `fortnox_send_invoice` | Send
|
|
133
|
-
| `noxctl invoices bookkeep <docNumber>` | `fortnox_bookkeep_invoice` |
|
|
250
|
+
| `noxctl invoices send <docNumber> [--method email\|print\|einvoice] [--subject <s>] [--body <s>] [--bcc <email>]` | `fortnox_send_invoice` | Send via email (default), print, or e-invoice (mutation) |
|
|
251
|
+
| `noxctl invoices bookkeep <docNumber>` | `fortnox_bookkeep_invoice` | Bookkeep an invoice (mutation) |
|
|
134
252
|
| `noxctl invoices credit <docNumber>` | `fortnox_credit_invoice` | Credit an invoice (mutation) |
|
|
135
253
|
|
|
254
|
+
### Invoice payments (inbetalningar)
|
|
255
|
+
|
|
256
|
+
| CLI | MCP tool | Description |
|
|
257
|
+
|-----|----------|-------------|
|
|
258
|
+
| `noxctl invoice-payments list [--invoice <number>]` / alias `noxctl ip list` | `fortnox_list_invoice_payments` | List invoice payments |
|
|
259
|
+
| `noxctl invoice-payments get <number>` | `fortnox_get_invoice_payment` | Get a single invoice payment |
|
|
260
|
+
| `noxctl invoice-payments create --invoice <n> --amount <a> --date <date>` | `fortnox_create_invoice_payment` | Register a payment against an invoice (mutation) |
|
|
261
|
+
| `noxctl invoice-payments bookkeep <number>` | — | Bookkeep an invoice payment (mutation) |
|
|
262
|
+
| `noxctl invoice-payments delete <number>` | `fortnox_delete_invoice_payment` | Delete an invoice payment (mutation) |
|
|
263
|
+
|
|
264
|
+
### Supplier invoices (leverantörsfakturor)
|
|
265
|
+
|
|
266
|
+
| CLI | MCP tool | Description |
|
|
267
|
+
|-----|----------|-------------|
|
|
268
|
+
| `noxctl supplier-invoices list [--filter <status>] [--supplier <number>]` / alias `si list` | `fortnox_list_supplier_invoices` | List/filter supplier invoices |
|
|
269
|
+
| `noxctl supplier-invoices get <givenNumber>` | `fortnox_get_supplier_invoice` | Get a single supplier invoice |
|
|
270
|
+
| `noxctl supplier-invoices create --supplier <n> --input <file>` | `fortnox_create_supplier_invoice` | Create a supplier invoice (mutation) |
|
|
271
|
+
| `noxctl supplier-invoices bookkeep <givenNumber>` | `fortnox_bookkeep_supplier_invoice` | Bookkeep a supplier invoice (mutation) |
|
|
272
|
+
|
|
273
|
+
### Supplier invoice payments (utbetalningar)
|
|
274
|
+
|
|
275
|
+
| CLI | MCP tool | Description |
|
|
276
|
+
|-----|----------|-------------|
|
|
277
|
+
| `noxctl supplier-invoice-payments list [--invoice <number>]` / alias `sip list` | `fortnox_list_supplier_invoice_payments` | List supplier invoice payments |
|
|
278
|
+
| `noxctl supplier-invoice-payments get <number>` | `fortnox_get_supplier_invoice_payment` | Get a single supplier invoice payment |
|
|
279
|
+
| `noxctl supplier-invoice-payments create --invoice <n> --amount <a> --date <date>` | `fortnox_create_supplier_invoice_payment` | Register a payment against a supplier invoice (mutation) |
|
|
280
|
+
| `noxctl supplier-invoice-payments delete <number>` | `fortnox_delete_supplier_invoice_payment` | Delete a supplier invoice payment (mutation) |
|
|
281
|
+
|
|
282
|
+
### Offers (offerter)
|
|
283
|
+
|
|
284
|
+
| CLI | MCP tool | Description |
|
|
285
|
+
|-----|----------|-------------|
|
|
286
|
+
| `noxctl offers list [--filter <status>] [--customer <number>]` | `fortnox_list_offers` | List/filter offers. Filters: `cancelled`, `expired`, `ordercreated`, `invoicecreated` |
|
|
287
|
+
| `noxctl offers get <docNumber>` | `fortnox_get_offer` | Get a single offer |
|
|
288
|
+
| `noxctl offers create --customer <number> --input <file>` | `fortnox_create_offer` | Create an offer (mutation) |
|
|
289
|
+
| `noxctl offers update <docNumber> --input <file>` | `fortnox_update_offer` | Update an offer (mutation) |
|
|
290
|
+
| `noxctl offers create-invoice <docNumber>` | `fortnox_create_invoice_from_offer` | Convert offer → invoice (mutation) |
|
|
291
|
+
| `noxctl offers create-order <docNumber>` | `fortnox_create_order_from_offer` | Convert offer → order (mutation) |
|
|
292
|
+
|
|
293
|
+
### Orders (ordrar)
|
|
294
|
+
|
|
295
|
+
| CLI | MCP tool | Description |
|
|
296
|
+
|-----|----------|-------------|
|
|
297
|
+
| `noxctl orders list [--filter <status>] [--customer <number>]` | `fortnox_list_orders` | List/filter orders. Filters: `cancelled`, `invoicecreated`, `invoicenotcreated` |
|
|
298
|
+
| `noxctl orders get <docNumber>` | `fortnox_get_order` | Get a single order |
|
|
299
|
+
| `noxctl orders create --customer <number> --input <file>` | `fortnox_create_order` | Create an order (mutation) |
|
|
300
|
+
| `noxctl orders update <docNumber> --input <file>` | `fortnox_update_order` | Update an order (mutation) |
|
|
301
|
+
| `noxctl orders create-invoice <docNumber>` | `fortnox_create_invoice_from_order` | Convert order → invoice (mutation) |
|
|
302
|
+
|
|
136
303
|
### Bookkeeping
|
|
137
304
|
|
|
138
305
|
| CLI | MCP tool | Description |
|
|
139
306
|
|-----|----------|-------------|
|
|
140
307
|
| `noxctl vouchers list [--series <s>] [--from <date>] [--to <date>]` | `fortnox_list_vouchers` | List vouchers, optionally filtered by series and date range |
|
|
308
|
+
| `noxctl vouchers get <series> <number>` | `fortnox_get_voucher` | Get a single voucher with rows |
|
|
141
309
|
| `noxctl vouchers create --input <file>` | `fortnox_create_voucher` | Create a voucher with debit/credit rows (mutation) |
|
|
310
|
+
| `noxctl vouchers attach <series> <number> <file...> [--year]` | `fortnox_attach_voucher_files` | Upload receipt/underlag files and link them to a voucher (mutation; needs the Fortnox archive scope) |
|
|
142
311
|
| `noxctl accounts list [--search <term>]` | `fortnox_list_accounts` | View chart of accounts, search by name or number |
|
|
143
312
|
|
|
313
|
+
### Financial reports
|
|
314
|
+
|
|
315
|
+
| CLI | MCP tool | Description |
|
|
316
|
+
|-----|----------|-------------|
|
|
317
|
+
| `noxctl reports income [--year <n>] [--from <date>] [--to <date>]` / alias `reports resultat` | `fortnox_income_statement` | Income statement (resultaträkning) |
|
|
318
|
+
| `noxctl reports balance [--year <n>] [--to <date>]` / alias `reports balans` | `fortnox_balance_sheet` | Balance sheet (balansräkning) |
|
|
319
|
+
|
|
144
320
|
### Tax
|
|
145
321
|
|
|
146
322
|
| CLI | MCP tool | Description |
|
|
147
323
|
|-----|----------|-------------|
|
|
148
324
|
| `noxctl tax report --from <date> --to <date>` | `fortnox_tax_report` | Informational VAT summary for a period. Reconcile against Fortnox before filing. Dates in `YYYY-MM-DD` format |
|
|
149
325
|
|
|
326
|
+
### Tax reductions (ROT/RUT)
|
|
327
|
+
|
|
328
|
+
| CLI | MCP tool | Description |
|
|
329
|
+
|-----|----------|-------------|
|
|
330
|
+
| `noxctl tax-reductions list [--filter <type>]` | `fortnox_list_taxreductions` | List tax reductions (ROT/RUT) |
|
|
331
|
+
| `noxctl tax-reductions get <id>` | `fortnox_get_taxreduction` | Get a single tax reduction |
|
|
332
|
+
| `noxctl tax-reductions create --reference <n> --type <rot\|rut> --document-type <type> --customer-name <name> --amount <öre>` | `fortnox_create_taxreduction` | Create a ROT/RUT tax reduction (mutation) |
|
|
333
|
+
|
|
334
|
+
### Projects
|
|
335
|
+
|
|
336
|
+
| CLI | MCP tool | Description |
|
|
337
|
+
|-----|----------|-------------|
|
|
338
|
+
| `noxctl projects list` | `fortnox_list_projects` | List projects |
|
|
339
|
+
| `noxctl projects get <number>` | `fortnox_get_project` | Get a single project |
|
|
340
|
+
| `noxctl projects create --description <text>` | `fortnox_create_project` | Create a project (mutation) |
|
|
341
|
+
| `noxctl projects update <number> --input <file>` | `fortnox_update_project` | Update a project (mutation) |
|
|
342
|
+
|
|
343
|
+
### Cost centers (kostnadsställen)
|
|
344
|
+
|
|
345
|
+
| CLI | MCP tool | Description |
|
|
346
|
+
|-----|----------|-------------|
|
|
347
|
+
| `noxctl costcenters list` | `fortnox_list_costcenters` | List cost centers |
|
|
348
|
+
| `noxctl costcenters get <code>` | `fortnox_get_costcenter` | Get a single cost center |
|
|
349
|
+
| `noxctl costcenters create --code <code> --description <text>` | `fortnox_create_costcenter` | Create a cost center (mutation) |
|
|
350
|
+
| `noxctl costcenters update <code> --input <file>` | `fortnox_update_costcenter` | Update a cost center (mutation) |
|
|
351
|
+
| `noxctl costcenters delete <code>` | `fortnox_delete_costcenter` | Delete a cost center (mutation) |
|
|
352
|
+
|
|
353
|
+
### Price lists and prices
|
|
354
|
+
|
|
355
|
+
| CLI | MCP tool | Description |
|
|
356
|
+
|-----|----------|-------------|
|
|
357
|
+
| `noxctl pricelists list` | `fortnox_list_pricelists` | List price lists |
|
|
358
|
+
| `noxctl pricelists get <code>` | `fortnox_get_pricelist` | Get a single price list |
|
|
359
|
+
| `noxctl pricelists create --code <code> --description <text>` | `fortnox_create_pricelist` | Create a price list (mutation) |
|
|
360
|
+
| `noxctl pricelists update <code> --input <file>` | `fortnox_update_pricelist` | Update a price list (mutation) |
|
|
361
|
+
| `noxctl prices list --pricelist <code> [--article <number>]` | `fortnox_list_prices` | List prices within a price list |
|
|
362
|
+
| `noxctl prices get --pricelist <code> --article <number>` | `fortnox_get_price` | Get a specific price |
|
|
363
|
+
| `noxctl prices update --pricelist <code> --article <number> --input <file>` | `fortnox_update_price` | Update a price (mutation) |
|
|
364
|
+
|
|
365
|
+
### Contracts (avtal — recurring invoicing)
|
|
366
|
+
|
|
367
|
+
| CLI | MCP tool | Description |
|
|
368
|
+
|-----|----------|-------------|
|
|
369
|
+
| `noxctl contracts list [--filter active\|inactive\|finished]` | `fortnox_list_contracts` | List/filter contracts |
|
|
370
|
+
| `noxctl contracts get <number>` | `fortnox_get_contract` | Get a single contract |
|
|
371
|
+
| `noxctl contracts create --customer <number> --input <file>` | `fortnox_create_contract` | Create a contract (mutation) |
|
|
372
|
+
| `noxctl contracts update <number> --input <file>` | `fortnox_update_contract` | Update a contract (mutation) |
|
|
373
|
+
| `noxctl contracts finish <number>` | `fortnox_finish_contract` | Finish a contract (mutation) |
|
|
374
|
+
| `noxctl contracts create-invoice <number>` | `fortnox_create_invoice_from_contract` | Create the next invoice now (mutation) |
|
|
375
|
+
| `noxctl contracts increase-invoice-count <number>` | `fortnox_increase_contract_invoice_count` | Extend by one invoice (mutation) |
|
|
376
|
+
|
|
377
|
+
### Financial years and locked period
|
|
378
|
+
|
|
379
|
+
| CLI | MCP tool | Description |
|
|
380
|
+
|-----|----------|-------------|
|
|
381
|
+
| `noxctl financial-years list [--date <date>]` | `fortnox_list_financialyears` | List financial years (räkenskapsår) |
|
|
382
|
+
| `noxctl financial-years get <id>` | `fortnox_get_financialyear` | Get a single financial year |
|
|
383
|
+
| `noxctl financial-years locked-period` | `fortnox_get_lockedperiod` | Show through which date bookkeeping is locked |
|
|
384
|
+
|
|
385
|
+
### Analytics and dashboard
|
|
386
|
+
|
|
387
|
+
| CLI | MCP tool | Description |
|
|
388
|
+
|-----|----------|-------------|
|
|
389
|
+
| `noxctl analytics overdue` | `fortnox_overdue_invoices` | Overdue invoices summary |
|
|
390
|
+
| `noxctl analytics unpaid` | `fortnox_unpaid_totals` | Outstanding receivables, with overdue split |
|
|
391
|
+
| `noxctl analytics top-customers [--period <period>]` | `fortnox_top_customers` | Top customers by invoiced amount |
|
|
392
|
+
| `noxctl analytics vat --period <period>` | `fortnox_vat_summary` | VAT summary with net VAT position |
|
|
393
|
+
| `noxctl dashboard` | — | At-a-glance: outstanding, overdue, recent invoices, monthly revenue |
|
|
394
|
+
|
|
150
395
|
### Company
|
|
151
396
|
|
|
152
397
|
| CLI | MCP tool | Description |
|
|
@@ -160,6 +405,10 @@ Every operation is available both as a CLI command and as an MCP tool. The CLI i
|
|
|
160
405
|
| `noxctl init` | — | Interactive setup wizard — connects to Fortnox, stores credentials, optionally registers MCP server |
|
|
161
406
|
| `noxctl doctor` | `fortnox_status` | Validate setup: Node version, credentials, token status, API connectivity, and scopes |
|
|
162
407
|
| `noxctl logout` | — | Remove stored credentials from the OS keychain |
|
|
408
|
+
| `noxctl profile use <name>` | — | Set the active profile (writes `~/.fortnox-mcp/active-profile`) |
|
|
409
|
+
| `noxctl profile current` | — | Show the currently resolved profile and where it came from |
|
|
410
|
+
| `noxctl profile list` | — | List known profiles from the index |
|
|
411
|
+
| `noxctl completion <bash\|zsh\|fish>` | — | Generate a shell completion script |
|
|
163
412
|
|
|
164
413
|
## CLI output
|
|
165
414
|
|
|
@@ -172,6 +421,14 @@ noxctl -o table invoices list # force table
|
|
|
172
421
|
noxctl invoices list | jq . # auto-JSON (piped)
|
|
173
422
|
```
|
|
174
423
|
|
|
424
|
+
JSON output has a stable envelope: list commands wrap under the plural resource key (`{"Invoices": [...], "MetaInformation": {...}}`) and single-resource commands under the singular key (`{"Invoice": {...}}`), so scripted callers can rely on a fixed accessor. Failures in JSON mode are emitted to stderr as a structured envelope:
|
|
425
|
+
|
|
426
|
+
```json
|
|
427
|
+
{ "error": { "status": 400, "message": "...", "source": "fortnox-api" } }
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Natural date periods are accepted wherever `--from`/`--to` work, via `--period`: `Q1`, `2025-Q3`, `march`/`mars`, `this-quarter`, `last-quarter`, `this-month`, `last-month`, `ytd`, `this-year`, `last-year`, or a bare year. Periods are **calendar-year** based (broken fiscal years are not yet considered).
|
|
431
|
+
|
|
175
432
|
When running from a local clone instead of an installed binary, replace `noxctl` with `node dist/cli.js`.
|
|
176
433
|
|
|
177
434
|
## Mutation safety
|
|
@@ -228,16 +485,18 @@ Ask Claude naturally — works in both Swedish and English:
|
|
|
228
485
|
|
|
229
486
|
## Troubleshooting
|
|
230
487
|
|
|
231
|
-
**"FORTNOX_CLIENT_ID and FORTNOX_CLIENT_SECRET
|
|
488
|
+
**"stdin is not a TTY. Set FORTNOX_CLIENT_ID and FORTNOX_CLIENT_SECRET env vars to run non-interactively"**
|
|
232
489
|
|
|
233
|
-
|
|
490
|
+
`noxctl init` is normally interactive — it prompts for the Client ID and Secret. In CI or other non-TTY contexts it falls back to reading them from environment variables:
|
|
234
491
|
|
|
235
492
|
```bash
|
|
236
493
|
export FORTNOX_CLIENT_ID=<your-id>
|
|
237
494
|
export FORTNOX_CLIENT_SECRET=<your-secret>
|
|
495
|
+
export FORTNOX_SERVICE_ACCOUNT=1 # optional, enables service account mode
|
|
496
|
+
noxctl init
|
|
238
497
|
```
|
|
239
498
|
|
|
240
|
-
|
|
499
|
+
Once authorized, the tokens are stored in the OS keychain. No env vars are needed afterwards — only for re-running `init` non-interactively.
|
|
241
500
|
|
|
242
501
|
**"Not authenticated. Run `noxctl init`"**
|
|
243
502
|
|
|
@@ -245,16 +504,7 @@ Credentials are missing or were not saved. Re-run the setup step. On macOS, chec
|
|
|
245
504
|
|
|
246
505
|
**403 Forbidden from Fortnox API**
|
|
247
506
|
|
|
248
|
-
Your app is missing
|
|
249
|
-
|
|
250
|
-
| Swedish (SV) | English (EN) |
|
|
251
|
-
|----------------------|---------------------|
|
|
252
|
-
| Artikel | Article |
|
|
253
|
-
| Bokföring | Bookkeeping |
|
|
254
|
-
| Faktura | Invoice |
|
|
255
|
-
| Företagsinformation | Company Information |
|
|
256
|
-
| Inställningar | Settings |
|
|
257
|
-
| Kund | Customer |
|
|
507
|
+
Your app is missing one or more scopes. The error message names the specific scope needed (e.g. `Missing "supplier" scope`). Go to [developer.fortnox.se](https://developer.fortnox.se/), open your app, and enable the matching permission under **Behörigheter** / **Permissions** — see the full table in [Setup → Create a Fortnox app](#1-create-a-fortnox-app). Then re-run `noxctl init`.
|
|
258
508
|
|
|
259
509
|
**"Token refresh failed"**
|
|
260
510
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export declare const SCOPES = "article customer invoice supplier supplierinvoice bookkeeping companyinformation settings";
|
|
1
|
+
export declare const SCOPES = "article customer invoice payment supplier supplierinvoice bookkeeping companyinformation settings inbox connectfile";
|
|
2
|
+
export declare const CREDENTIAL_SCHEMA_VERSION = 2;
|
|
2
3
|
export interface FortnoxCredentials {
|
|
3
4
|
client_id: string;
|
|
4
5
|
client_secret: string;
|
|
@@ -6,24 +7,31 @@ export interface FortnoxCredentials {
|
|
|
6
7
|
refresh_token: string;
|
|
7
8
|
expires_at: number;
|
|
8
9
|
tenant_id?: string;
|
|
10
|
+
company_name?: string;
|
|
11
|
+
schema_version?: number;
|
|
12
|
+
last_write_epoch?: number;
|
|
9
13
|
}
|
|
10
14
|
export interface FortnoxAppConfig {
|
|
11
15
|
clientId: string;
|
|
12
16
|
clientSecret: string;
|
|
13
17
|
serviceAccount?: boolean;
|
|
14
18
|
}
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function
|
|
19
|
+
export declare function setResolvedProfile(name: string): void;
|
|
20
|
+
export declare function getResolvedProfile(): string;
|
|
21
|
+
export declare function __resetLegacyObservedForDefault(): void;
|
|
22
|
+
export declare function loadCredentials(profile?: string): Promise<FortnoxCredentials | null>;
|
|
23
|
+
export declare function saveCredentials(creds: FortnoxCredentials, profile?: string): Promise<void>;
|
|
17
24
|
export declare function exchangeCodeForTokens(code: string, redirectUri: string, config: FortnoxAppConfig): Promise<{
|
|
18
25
|
access_token: string;
|
|
19
26
|
refresh_token: string;
|
|
20
27
|
expires_in: number;
|
|
21
28
|
}>;
|
|
22
|
-
export declare function getTokenViaClientCredentials(creds: FortnoxCredentials): Promise<FortnoxCredentials>;
|
|
23
|
-
export declare function refreshAccessToken(creds: FortnoxCredentials): Promise<FortnoxCredentials>;
|
|
24
|
-
export declare function getValidToken(): Promise<string>;
|
|
29
|
+
export declare function getTokenViaClientCredentials(creds: FortnoxCredentials, profile?: string): Promise<FortnoxCredentials>;
|
|
30
|
+
export declare function refreshAccessToken(creds: FortnoxCredentials, profile?: string): Promise<FortnoxCredentials>;
|
|
31
|
+
export declare function getValidToken(profile?: string): Promise<string>;
|
|
25
32
|
export declare function fetchTenantId(accessToken: string): Promise<string | undefined>;
|
|
33
|
+
export declare function fetchCompanyNameSafe(accessToken: string): Promise<string | undefined>;
|
|
26
34
|
export declare function escapeHtml(value: string): string;
|
|
27
35
|
export declare function buildAuthorizationUrl(config: FortnoxAppConfig, redirectUri: string, state: string): string;
|
|
28
|
-
export declare function runOAuthSetup(config: FortnoxAppConfig): Promise<void>;
|
|
36
|
+
export declare function runOAuthSetup(config: FortnoxAppConfig, profile?: string): Promise<void>;
|
|
29
37
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,MAAM,wHACoG,CAAC;AAExH,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAcD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAErD;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAGD,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD;AAuBD,wBAAsB,eAAe,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CA2B1F;AAED,wBAAsB,eAAe,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUhG;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CA2B9E;AAED,wBAAsB,4BAA4B,CAChD,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAwC7B;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,kBAAkB,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,CAqC7B;AAED,wBAAsB,aAAa,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA8BrE;AAED,wBAAsB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAepF;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgB3F;AAgBD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOhD;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GACZ,MAAM,CAeR;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,gBAAgB,EACxB,OAAO,GAAE,MAAwB,GAChC,OAAO,CAAC,IAAI,CAAC,CAgIf"}
|