Menu
Cookbooks
API reference
- Overview
- Companies
- Capabilities
- Measures
- Packages
- Unexplained
- Liquidity forecast
- Portfolio
- Customers
- Invoices
- Collections
- Articles
- Set-offs
- Suppliers
- Supplier invoices
- Supplier payment files
- Bank payments
- Expense claims
- Transactions
- Reconciliation
- Bank accounts
- POS sales
- Webshop orders
- Journal entries
- Voucher gap explanations
- Fiscal periods
- Accounts
- Accrual schedules
- Agreements and commitments
- Tax loss carry-forwards
- Fixed assets
- Documents
- Signing requests
- Company registers
- Capital events
- Owner tax
- Inbox items
- Dimensions
- Employees
- Salary runs
- Reports
- Budgets
- Imports
- Compliance check
- Skatteverket
- Peppol
- Webhooks
- Operations
- Pending operations
- Health
- API key
- Service accounts
Pending operations
The approval door for staged writes: list and read pending operations with their status and task_state, approve one (commit it and get the outcome) or reject it. A write sent with ?stage=true waits here until it is decided.
Endpoints
GET/api/v1/companies/:companyId/pending-operations: List pending operations (staged writes waiting for approval).GET/api/v1/companies/:companyId/pending-operations/:id: Read one pending operation and its outcome.POST/api/v1/companies/:companyId/pending-operations/:id/approve: Approve a pending operation: commit it and answer the outcome.POST/api/v1/companies/:companyId/pending-operations/:id/reject: Reject a pending operation without committing it.
GET /api/v1/companies/:companyId/pending-operations
pending-operations.list · scope pending_operations:read
List pending operations (staged writes waiting for approval).
Returns the company's pending operations newest first: writes staged for approval by an agent, an integration (?stage=true on a write) or the app, with what approving each will do (preview), its risk level and, once resolved, its result. Every row carries status (pending, committing, committed, rejected, failed_partial) and task_state in the MCP Tasks / A2A vocabulary: working (pending, committing), completed (committed), failed (the approved commit did not go through), cancelled (rejected by a person, expired or superseded). Defaults to status=pending, the approval queue; status=all lists every status. Same rows as the app's review list and the MCP tool gnubok_list_pending_operations.
Use when: You run an approval or attest flow outside Accounted and need the queue of operations waiting for a decision, or the outcome of the ones you staged.
Don't use for: Supplier-invoice attest (GET /supplier-invoices and POST /supplier-invoices/{id}/approve, governed by the attest policy). Polling one operation you staged (GET /pending-operations/{id}).
Pitfalls
- Without status the list is pending only; pass status=all for history.
- The staged input (params) is never returned: preview is the approver's summary.
- A cursor that no longer decodes starts from the first page again: stop on a null next_cursor.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
status | "pending" | "committing" | "committed" | "rejected" | "failed_partial" | "all" | no | Default pending (the approval queue). all = every status. |
risk_level | "low" | "medium" | "high" | no | |
operation_type | string | no | One operation type, e.g. create_invoice. |
batch_id | string | no | Only the operations staged together under this batch. |
cursor | string | no | Opaque cursor from the previous page's meta.next_cursor. Omit for the first page. |
limit | number | no | Page size, 1-100 (default 50). Larger values are clamped to 100. |
Response fields
| Name | Type | Description |
|---|---|---|
[].pending_operation_id | string | |
[].handle | string (optional) | Typed handle of this pending operation: pop_ plus its id as 32 lowercase hex. Accepted wherever the id is. |
[].company_id | string | |
[].operation_type | string | What approving it does, e.g. create_invoice, categorize_transaction. |
[].title | string | The one line the approver sees, in Swedish. |
[].status | "pending" | "committing" | "committed" | "rejected" | "failed_partial" | pending: waiting for a decision. committing: an approval is running it. committed: done. rejected: not done (a person said no, it expired, it was handled another way, or the commit refused it). failed_partial: the commit failed after posting something; result.posted_ids names it. |
[].task_state | "working" | "completed" | "failed" | "cancelled" | The status in the MCP Tasks / A2A vocabulary: working (pending, committing), completed (committed), failed (the approved commit did not go through: failed_partial, or rejected by the commit), cancelled (rejected by a person, expired or superseded). completed, failed and cancelled never change again. |
[].risk_level | "low" | "medium" | "high" | high needs confirmed=true on approve. |
[].preview | object | What approving it will do, as the approver sees it. |
[].result | object | null | The outcome once resolved: the committed ids, or why it was not done. null while pending. |
[].agent_reason | string | null | Why the stager proposes it, as given when staging (reason, at most 500 characters): shown to the approver and kept with the decision. null when none was given. |
[].rejection_category | string | null | |
[].rejection_reason | string | null | |
[].actor_type | string | Who staged it: api_key, mcp_oauth, service_account, user, agent_chat, cron, or system (a suggestion). |
[].actor_id | string | null | |
[].actor_label | string | null | |
[].batch_id | string | null | |
[].created_at | string | |
[].resolved_at | string | null | |
[].links | object |
Example response
{
"data": [
{
"pending_operation_id": "9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"company_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"operation_type": "create_supplier",
"title": "Skapa leverantör Clas Ohlson AB",
"status": "pending",
"task_state": "working",
"risk_level": "medium",
"preview": {
"name": "Clas Ohlson AB",
"supplier_type": "swedish_business"
},
"result": null,
"agent_reason": "Clas Ohlson AB is on three supplier invoices in the inbox and is not in the supplier register yet.",
"rejection_category": null,
"rejection_reason": null,
"actor_type": "api_key",
"actor_id": "b1f04c2e-7d3a-4e5b-9c6d-0e1f2a3b4c5d",
"actor_label": "Attestflöde",
"batch_id": null,
"created_at": "2026-10-15T09:12:00.000Z",
"resolved_at": null,
"links": {
"self": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"approve": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/approve",
"reject": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/reject"
}
}
],
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12",
"next_cursor": null
}
}
GET /api/v1/companies/:companyId/pending-operations/:id
pending-operations.get · scope pending_operations:read
Read one pending operation and its outcome.
Returns one pending operation: what approving it will do (preview), its status and task_state (working, completed, failed, cancelled) and, once resolved, its result (the committed ids, or why it was not done). completed, failed and cancelled never change again.
Use when: You staged a write with ?stage=true (or an agent staged one) and wait for a person or your attest flow to decide it: poll until task_state is completed, failed or cancelled.
Don't use for: Scanning the approval queue (GET /pending-operations).
Pitfalls
- An id from another company answers 404 PENDING_OPERATION_NOT_FOUND, like an unknown one.
- status committing (task_state working) means an approval is running it right now: poll again shortly.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Response fields
| Name | Type | Description |
|---|---|---|
pending_operation_id | string | |
handle | string (optional) | Typed handle of this pending operation: pop_ plus its id as 32 lowercase hex. Accepted wherever the id is. |
company_id | string | |
operation_type | string | What approving it does, e.g. create_invoice, categorize_transaction. |
title | string | The one line the approver sees, in Swedish. |
status | "pending" | "committing" | "committed" | "rejected" | "failed_partial" | pending: waiting for a decision. committing: an approval is running it. committed: done. rejected: not done (a person said no, it expired, it was handled another way, or the commit refused it). failed_partial: the commit failed after posting something; result.posted_ids names it. |
task_state | "working" | "completed" | "failed" | "cancelled" | The status in the MCP Tasks / A2A vocabulary: working (pending, committing), completed (committed), failed (the approved commit did not go through: failed_partial, or rejected by the commit), cancelled (rejected by a person, expired or superseded). completed, failed and cancelled never change again. |
risk_level | "low" | "medium" | "high" | high needs confirmed=true on approve. |
preview | object | What approving it will do, as the approver sees it. |
result | object | null | The outcome once resolved: the committed ids, or why it was not done. null while pending. |
agent_reason | string | null | Why the stager proposes it, as given when staging (reason, at most 500 characters): shown to the approver and kept with the decision. null when none was given. |
rejection_category | string | null | |
rejection_reason | string | null | |
actor_type | string | Who staged it: api_key, mcp_oauth, service_account, user, agent_chat, cron, or system (a suggestion). |
actor_id | string | null | |
actor_label | string | null | |
batch_id | string | null | |
created_at | string | |
resolved_at | string | null | |
links | object |
Example response
{
"data": {
"pending_operation_id": "9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"company_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"operation_type": "create_supplier",
"title": "Skapa leverantör Clas Ohlson AB",
"status": "pending",
"task_state": "working",
"risk_level": "medium",
"preview": {
"name": "Clas Ohlson AB",
"supplier_type": "swedish_business"
},
"result": null,
"agent_reason": "Clas Ohlson AB is on three supplier invoices in the inbox and is not in the supplier register yet.",
"rejection_category": null,
"rejection_reason": null,
"actor_type": "api_key",
"actor_id": "b1f04c2e-7d3a-4e5b-9c6d-0e1f2a3b4c5d",
"actor_label": "Attestflöde",
"batch_id": null,
"created_at": "2026-10-15T09:12:00.000Z",
"resolved_at": null,
"links": {
"self": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"approve": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/approve",
"reject": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/reject"
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/pending-operations/:id/approve
pending-operations.approve · scope pending_operations:approve
Approve a pending operation: commit it and answer the outcome.
Commits a pending operation now, through the same executor as Godkänn in the app and the MCP approve tool, and answers the operation as committed: status committed, task_state completed and result holding what was created or posted. A high-risk operation (vouchers, corrections, storno, period lock/close, year-end, sends) needs {"confirmed": true} once a person has acknowledged it. Approving twice never commits twice: a second approval answers 409 PENDING_OPERATION_NOT_PENDING with the operation as it stands. Approvals are recorded with commit method api_key and the key as actor, and the key's unattended commit limit applies.
Use when: Your attest or approval flow (or the person it asked) has decided yes on an operation that was staged with ?stage=true or by an agent.
Don't use for: Attesting a supplier invoice under the attest policy (POST /supplier-invoices/{id}/approve). Operations only a person may approve in the app (collections): those answer 403 COLLECTION_ATTESTATION_WEB_ONLY; a rule for automatic booking (create_grant) answers 403 GRANT_NEEDS_PERSON.
Pitfalls
- Idempotency-Key is mandatory; a retry with the same key and body replays the first answer.
- risk_level high without confirmed=true answers 400 PENDING_OPERATION_CONFIRMATION_REQUIRED and commits nothing.
- A refused commit names where the operation landed in error.details.pending_operation: status pending means nothing was consumed and it can be approved again once the cause is fixed (e.g. ACCOUNTS_NOT_IN_CHART, UNATTENDED_COMMIT_LIMIT_EXCEEDED); rejected or failed_partial means it is resolved.
- Needs the pending_operations:approve scope; a key that also stages writes both proposes and decides, so give it only to flows that hold their own approval step.
Risk: high · Idempotent: yes · Reversible: no · Dry-run supported: no
Request body
| Name | Type | Required | Description |
|---|---|---|---|
confirmed | boolean | no | Required true for risk_level high: the approver acknowledged that the posting cannot be undone (BFL 5 kap. 5 §). |
Response fields
| Name | Type | Description |
|---|---|---|
pending_operation_id | string | |
handle | string (optional) | Typed handle of this pending operation: pop_ plus its id as 32 lowercase hex. Accepted wherever the id is. |
company_id | string | |
operation_type | string | What approving it does, e.g. create_invoice, categorize_transaction. |
title | string | The one line the approver sees, in Swedish. |
status | "pending" | "committing" | "committed" | "rejected" | "failed_partial" | pending: waiting for a decision. committing: an approval is running it. committed: done. rejected: not done (a person said no, it expired, it was handled another way, or the commit refused it). failed_partial: the commit failed after posting something; result.posted_ids names it. |
task_state | "working" | "completed" | "failed" | "cancelled" | The status in the MCP Tasks / A2A vocabulary: working (pending, committing), completed (committed), failed (the approved commit did not go through: failed_partial, or rejected by the commit), cancelled (rejected by a person, expired or superseded). completed, failed and cancelled never change again. |
risk_level | "low" | "medium" | "high" | high needs confirmed=true on approve. |
preview | object | What approving it will do, as the approver sees it. |
result | object | null | The outcome once resolved: the committed ids, or why it was not done. null while pending. |
agent_reason | string | null | Why the stager proposes it, as given when staging (reason, at most 500 characters): shown to the approver and kept with the decision. null when none was given. |
rejection_category | string | null | |
rejection_reason | string | null | |
actor_type | string | Who staged it: api_key, mcp_oauth, service_account, user, agent_chat, cron, or system (a suggestion). |
actor_id | string | null | |
actor_label | string | null | |
batch_id | string | null | |
created_at | string | |
resolved_at | string | null | |
links | object |
Example request
{
"confirmed": true
}
Example response
{
"data": {
"pending_operation_id": "9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"company_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"operation_type": "create_supplier",
"title": "Skapa leverantör Clas Ohlson AB",
"status": "committed",
"task_state": "completed",
"risk_level": "medium",
"preview": {
"name": "Clas Ohlson AB",
"supplier_type": "swedish_business"
},
"result": {
"supplier_id": "2c4e…"
},
"agent_reason": "Clas Ohlson AB is on three supplier invoices in the inbox and is not in the supplier register yet.",
"rejection_category": null,
"rejection_reason": null,
"actor_type": "api_key",
"actor_id": "b1f04c2e-7d3a-4e5b-9c6d-0e1f2a3b4c5d",
"actor_label": "Attestflöde",
"batch_id": null,
"created_at": "2026-10-15T09:12:00.000Z",
"resolved_at": "2026-10-15T09:14:03.000Z",
"links": {
"self": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"approve": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/approve",
"reject": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/reject"
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}
POST /api/v1/companies/:companyId/pending-operations/:id/reject
pending-operations.reject · scope pending_operations:approve
Reject a pending operation without committing it.
Resolves a pending operation as rejected: nothing is booked, sent or created. Answers the operation with status rejected and task_state cancelled. category and reason are optional and kept with it, so the agent or flow that staged it can learn why. An operation that is no longer pending (already approved, rejected or expired, or claimed by an approval running right now) answers 409 PENDING_OPERATION_NOT_PENDING and is left as it is. Same write as Avvisa in the app and the MCP reject tool.
Use when: Your attest or approval flow (or the person it asked) has decided no on a pending operation.
Don't use for: Undoing a committed operation: reverse or correct what it posted instead (POST /journal-entries/{id}/reverse or /correct, or credit the invoice).
Pitfalls
- Idempotency-Key is mandatory.
- Needs the pending_operations:approve scope, as approving does.
- reason is 1-500 characters.
Risk: low · Idempotent: yes · Reversible: no · Dry-run supported: no
Request body
| Name | Type | Required | Description |
|---|---|---|---|
category | "wrong_category" | "wrong_amount" | "duplicate" | "wrong_period" | "other" | no | What was wrong with the proposal: wrong_category, wrong_amount, duplicate, wrong_period or other. |
reason | string | no | Free text, kept with the operation. |
Response fields
| Name | Type | Description |
|---|---|---|
pending_operation_id | string | |
handle | string (optional) | Typed handle of this pending operation: pop_ plus its id as 32 lowercase hex. Accepted wherever the id is. |
company_id | string | |
operation_type | string | What approving it does, e.g. create_invoice, categorize_transaction. |
title | string | The one line the approver sees, in Swedish. |
status | "pending" | "committing" | "committed" | "rejected" | "failed_partial" | pending: waiting for a decision. committing: an approval is running it. committed: done. rejected: not done (a person said no, it expired, it was handled another way, or the commit refused it). failed_partial: the commit failed after posting something; result.posted_ids names it. |
task_state | "working" | "completed" | "failed" | "cancelled" | The status in the MCP Tasks / A2A vocabulary: working (pending, committing), completed (committed), failed (the approved commit did not go through: failed_partial, or rejected by the commit), cancelled (rejected by a person, expired or superseded). completed, failed and cancelled never change again. |
risk_level | "low" | "medium" | "high" | high needs confirmed=true on approve. |
preview | object | What approving it will do, as the approver sees it. |
result | object | null | The outcome once resolved: the committed ids, or why it was not done. null while pending. |
agent_reason | string | null | Why the stager proposes it, as given when staging (reason, at most 500 characters): shown to the approver and kept with the decision. null when none was given. |
rejection_category | string | null | |
rejection_reason | string | null | |
actor_type | string | Who staged it: api_key, mcp_oauth, service_account, user, agent_chat, cron, or system (a suggestion). |
actor_id | string | null | |
actor_label | string | null | |
batch_id | string | null | |
created_at | string | |
resolved_at | string | null | |
links | object |
Example request
{
"category": "wrong_amount",
"reason": "Beloppet stämmer inte med fakturan."
}
Example response
{
"data": {
"pending_operation_id": "9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"company_id": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
"operation_type": "create_supplier",
"title": "Skapa leverantör Clas Ohlson AB",
"status": "rejected",
"task_state": "cancelled",
"risk_level": "medium",
"preview": {
"name": "Clas Ohlson AB",
"supplier_type": "swedish_business"
},
"result": null,
"agent_reason": "Clas Ohlson AB is on three supplier invoices in the inbox and is not in the supplier register yet.",
"rejection_category": "wrong_amount",
"rejection_reason": "Beloppet stämmer inte med fakturan.",
"actor_type": "api_key",
"actor_id": "b1f04c2e-7d3a-4e5b-9c6d-0e1f2a3b4c5d",
"actor_label": "Attestflöde",
"batch_id": null,
"created_at": "2026-10-15T09:12:00.000Z",
"resolved_at": "2026-10-15T09:14:03.000Z",
"links": {
"self": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f",
"approve": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/approve",
"reject": "/api/v1/companies/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pending-operations/9a44c0de-1f2b-4c3d-8e4f-5a6b7c8d9e0f/reject"
}
},
"meta": {
"request_id": "req_…",
"api_version": "2026-05-12"
}
}