Konto Agent API (1.0.0)

Download OpenAPI specification:

OAuth2 agent surface for Konto — customers, items, invoices, costs, projects, tasks and product sales.

The Agent API lets a third-party application act on behalf of a Konto user, after that user signs in and consents to a set of scopes. It is the OAuth2 surface behind the Konto MCP connector and is the recommended path for user-delegated and AI integrations.

For headless server-to-server integrations (no user to log in) — e.g. a billing cron or an ERP sync running as a service — use the legacy username + api_key REST API instead (see the in-app reference).

Environments

Host Purpose
Production https://konto.is Real accounts, real invoices & email.
Development https://dev.konto.is Safe sandbox. Sign in with a dev/test account.

OAuth clients are per-environment: a client_id registered on dev does not exist on prod, and vice versa. The OAuth endpoints live under /strax/agent/*; the resource endpoints under /api/v1/agent/*.

dev.konto.is is always in maintenance mode. Server-side calls to dev must append ?en4_maint_code=<code> or receive HTTP 503. Ask Konto for the current dev bypass code. Never send it to production.

Response envelope

Every resource endpoint returns a JSON envelope:

{ "status": true, "message": "", "result": { ... } }

status is a boolean. On a handled error the HTTP status may still be 200 with status: false and a message — always check status, not just the HTTP code. (Auth, scope and method failures do use real 4xx codes, with the Error body { "error": true, "message": "…", "code": 403 }.) A plan that lacks the feature behind an endpoint is also a handled error: HTTP 200, status: false, "Subscription does not include the required feature: …".

Most creates and updates answer with result = the record's guid, not the record. GET it afterwards if you need the stored values.

Sending write payloads

Create/update endpoints read the resource object from a data field. Send it as a JSON-encoded string — this is the universally-supported form and the one the official MCP/SDK uses:

{ "data": "{\"name\":\"Acme ehf\",\"kennitala\":\"1234567890\",\"email\":\"acme@example.is\",\"lang\":\"is\",\"currency\":\"ISK\"}" }

With an application/json body, data may also be the object itself ({"data":{...}}). Customer endpoints additionally accept the fields flat at the top level. The schemas below document the decoded object shape in each data field. Endpoints marked otherwise (costs, task time entries) take top-level fields.

Lists and paging

Unless an endpoint says otherwise, a list without limit returns every match in one response. The exceptions: GET /costs returns 10 per page by default, and the product-sale lists always page (10 per page by default). Projects, time entries and the sync mode (updated_since / cursor) have their own paging rules, described on those endpoints.

Retrying writes

Every write (anything that is not a GET) accepts an optional Idempotency-Key header. A retry with the same key and the same request replays the first response instead of writing again — use it on invoice creation so a network retry cannot issue the invoice twice.

The write contract (read before creating anything)

  • VAT code (tax): S = 24 %, AA = 11 %, Z = 0 %.
  • Unit of measure (uom): C62 pieces · HUR hours · DAY days · KGS kg · MTR m · LTR l · MTK m² · MTQ m³ · TNE t · KWH kWh · MIN minutes · KTM km.
  • Invoice amount must equal Σ(line items) + VAT, rounded to 2 dp — the server rejects a mismatch.
  • Invoice dates: issue_date is today or earlier, and due_date ≤ settlement_date. All three are required; settlement_date is the eindagi.
  • Invoice lines: a line that does not reference a catalogue item by guid needs item_number, description, qty, uom and tax.
  • kennitala: 10 digits, no dash; validated (mod-11 checksum).
  • trading_partner_id (e-invoice): the recipient's bare kennitala — do not prefix the Peppol scheme 0196:.
  • Cost how_paid: 1 Account · 2 Debit card · 3 Credit card · 4 Owner cash.
  • Customer output_select (delivery): 2 e-invoice/XML (needs trading_partner_id) · 3 email · 4 PDF.

Authentication

OAuth2 authorization-code flow with PKCE. Endpoints live under /strax/agent.

Consent screen (start the OAuth flow)

Send the user's browser here. They sign in to Konto, review the requested scopes, and are redirected back to redirect_uri with a short-lived authorization code (10-minute TTL) and your state.

PKCE: if you send a code_challenge, only S256 is accepted. Public clients (no client_secret) must send one. redirect_uri must match the value registered for the client exactly.

query Parameters
client_id
required
string
scope
required
string

Comma-separated scopes (e.g. nav,create_invoice,cost).

redirect_uri
required
string <uri>
state
required
string
code_challenge
string
code_challenge_method
string
Value: "S256"

Responses

Exchange a code or refresh token for tokens

Exchange an authorization code (with grant_type=authorization_code) or a refresh_token (with grant_type=refresh_token) for an access token. Refresh tokens rotate — persist the new refresh_token from every response. Refresh does not require redirect_uri.

Request Body schema: application/json
required
client_id
required
string
client_secret
string

Required for confidential clients.

grant_type
required
string
Enum: "authorization_code" "refresh_token"
code
string

Required when grant_type=authorization_code.

redirect_uri
string <uri>

Required (and exact-match) when exchanging a code.

code_verifier
string

PKCE verifier, if a challenge was sent.

refresh_token
string

Required when grant_type=refresh_token.

Responses

Request samples

Content type
application/json
{
  • "client_id": "string",
  • "client_secret": "string",
  • "grant_type": "authorization_code",
  • "code": "string",
  • "redirect_uri": "http://example.com",
  • "code_verifier": "string",
  • "refresh_token": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "refresh_token": "string",
  • "expires_in": 0,
  • "scope": "string"
}

Register an agent client

Create an agent client and receive a client_id + client_secret (the secret is shown once). In practice Konto often provisions clients — especially shared app clients (one client many merchants authorize) — manually; contact Konto for onboarding.

Request Body schema: application/json
required
name
required
string
description
string
redirect_uri
required
string <uri>

Exact-match at authorize time.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "client_id": "string",
  • "client_secret": "string"
}

Account & agents

Identity, and managing which agent clients have access.

Account identity / health check

Returns the connected company name, registration number (kennitala) and username. A plain object, not the status/result envelope.

Authorizations:
kontoOAuth

Responses

Response samples

Content type
application/json
{
  • "company_name": "string",
  • "registration_no": "string",
  • "username": "string"
}

List authorized agent clients

The agent clients currently authorized on this account, with granted scopes. A plain object, not the status/result envelope.

Authorizations:
kontoOAuth

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "total": 0
}

Revoke an agent client

Immediately invalidates that client's tokens. Irreversible. Revoking the client you are currently using cuts off your own access.

Authorizations:
kontoOAuth
path Parameters
id
required
string

The agent client_id to revoke.

Responses

Response samples

Content type
application/json
{
  • "message": "Agent access revoked",
  • "agent_id": "string"
}

Agent audit log

Recent actions taken by agent clients on this account, newest first. A plain object, not the status/result envelope.

Authorizations:
kontoOAuth
query Parameters
agent_id
string

Only this client's actions. It must have an active grant on the account, or HTTP 403.

limit
integer [ 1 .. 200 ]
Default: 50

Results per page, 1–200.

page
integer
Default: 1

Page number (default 1).

Responses

Response samples

Content type
application/json
{
  • "data": [
    ],
  • "total": 0,
  • "page": 0,
  • "limit": 0
}

Customers

Scope: manage_customers.

List / search customers

Results include deactivated profiles — filter client-side if needed. Without limit, every customer is returned.

search matches the customer name only (substring). kennitala switches to a kennitala search; it is a substring match too, so check registration_no in the results when you need an exact hit.

Authorizations:
kontoOAuth
query Parameters
search
string

Substring of the customer name.

kennitala
string

Substring of the kennitala. When present, search is ignored.

limit
integer >= 1

Page size. Without it every match is returned in one response (there is no default page size). In sync mode default 100, max 500.

page
integer
Default: 1

Page number (default 1).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ]
}

Create a customer

Does not dedupe by kennitala — each call creates a new profile. To change an existing one, GET it then PUT. result is the new customer's guid.

On create, output_select is not read: the customer gets email delivery (3), or e-invoice (2) when trading_partner_id is sent. So email is always required here. For a PDF-only customer (4), create it and then PUT output_select: 4.

A refusal is HTTP 200 with status: false and one of: Please input validate kennitala, Name is required, Please input validate email, Trading partner is required, Language is required, Currency is required, or Data customer invalidate (missing or unreadable data).

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required

The customer object, as a JSON-encoded data string (also accepts a nested object or flat top-level fields).

One of
required
string or CustomerCreate (object)

Responses

Request samples

Content type
application/json
Example
{
  • "name": "Acme ehf",
  • "kennitala": "1234567890",
  • "email": "acme@example.is",
  • "lang": "is",
  • "currency": "ISK"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Count customers

Authorizations:
kontoOAuth
query Parameters
search
string

Substring of the customer name.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 42
}

Get a customer

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Update a customer

Send the fields you want to change; the others keep their values. The whole record is then validated with the create rules, and here output_select can be set (4 makes email optional). result is the customer's guid. PUT bodies must be application/json.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required

The customer object, as a JSON-encoded data string (also accepts a nested object or flat top-level fields).

One of
required
string or CustomerCreate (object)

Responses

Request samples

Content type
application/json
Example
{
  • "name": "Acme ehf",
  • "kennitala": "1234567890",
  • "email": "acme@example.is",
  • "lang": "is",
  • "currency": "ISK"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Items

Product/service catalogue. Scope: nav.

List items

Every catalogue item, inactive ones included. Without limit, all of them.

Authorizations:
kontoOAuth
query Parameters
limit
integer >= 1

Page size. Without it every match is returned in one response (there is no default page size). In sync mode default 100, max 500.

page
integer
Default: 1

Page number (default 1).

with_lots
string
Default: "0"
Enum: "0" "1"

1 to include each item's stock lots.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ]
}

Create an item

Catalogue product/service. description, uom and tax are required — there is no default VAT code (the Konto MCP server fills C62 / Z for you; the API does not). unit_price defaults to 0. The item number is assigned automatically. result is the new item's guid.

A refusal is HTTP 200 with status: false and Description is required, Unit of measure is required, Tax is required or Data item invalidate.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or ItemCreate (object)

Responses

Request samples

Content type
application/json
{
  • "data": "string"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Count items

Authorizations:
kontoOAuth

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 42
}

Get an item

Includes the stock lots.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Update an item

Send the fields to change — but always send unit_price: an update without it sets the price to 0. The other fields keep their values when omitted. result is the item's guid.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or ItemCreate (object)

Responses

Request samples

Content type
application/json
{
  • "data": "string"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Invoices

Scopes: nav (read/update), create_invoice (create), recurring.

List invoices

Recurring templates, group invoices and hidden invoices are not listed. Drafts are (status Draft). Outside sync mode, omitting limit returns every matching invoice, newest issue date first.

Send updated_since and/or cursor for sync mode: rows in a stable (updated, id) order, limit per page (default 100, max 500), has_more and next_cursor on the envelope. Pass next_cursor back as cursor until has_more is false. sort, page, date_filter, from_date and to_date are then ignored.

Authorizations:
kontoOAuth
query Parameters
limit
integer >= 1

Page size. Without it every match is returned in one response (there is no default page size). In sync mode default 100, max 500.

page
integer
Default: 1

Page number (default 1).

accounting_status
string
Example: accounting_status=Created,Error

Comma-separated list of Created, Posted, Skipped, Error. Any other value returns status: false with error_code: invalid_accounting_status (HTTP 200).

project_guid
string

Only invoices linked to this project. An unknown GUID returns status: false, No project found.

date_filter
string
Default: "issue_date"
Enum: "issue_date" "created_date"

Which date from_date / to_date apply to.

from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

sort
string
Default: "date_desc"
Enum: "date_desc" "date_asc" "created_desc" "created_asc"

date_* sorts on the issue date, created_* on the creation date. Any other value falls back to date_desc.

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Sync mode: only rows whose updated_at is at or after this point. Y-m-d, Y-m-d H:i:s or ISO 8601; zoned values are converted to UTC (the server clock). An invalid value is refused with HTTP 400 invalid_updated_since.

cursor
string

Sync mode: the next_cursor of the previous page (opaque). An invalid value is refused with HTTP 400 invalid_cursor.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "limit": 0,
  • "has_more": true,
  • "next_cursor": "string"
}

Create an invoice (or credit note)

Creates and issues an invoice — Konto delivers it to the customer per their delivery setting. amount must equal Σ(line items) + VAT. For a credit note, send type: credit + ref_invoice_guid (the original is marked credited and its customer is inherited).

result is the new invoice's guid. With save: 1 the invoice is stored as a draft instead: nothing is sent, and the answer is the same 201 with the draft's guid.

Sending a catalogue line (guid) with your own description, uom, unit_price or tax also updates that catalogue item to those values.

A refusal is HTTP 200 with status: false, the reason in message — for example Amount is required, Currency is required, Customer is required, Settlement date is required, Issue date is smaller than now (issue date in the future), Settlement date is older than the due date, Type is required (invoice or credit), Item number is required, The calculated total amount of all item lines not equal to the total amount, an empty account balance, a used-up claim quota, or error_code claim_not_configured / customer_mismatch / feature_required.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or InvoiceCreate (object)

JSON-encoded InvoiceCreate object (stringify before sending).

Responses

Request samples

Content type
application/json
{
  • "data": "{\"amount\":24800,\"currency\":\"ISK\",\"customer\":{\"guid\":\"c1a2…\"},\"issue_date\":\"2026-07-13\",\"due_date\":\"2026-07-27\",\"settlement_date\":\"2026-08-10\",\"type\":\"invoice\",\"items\":[{\"add_new\":1,\"description\":\"Consulting\",\"qty\":10,\"unit_price\":2000,\"uom\":\"HUR\",\"tax\":\"S\",\"item_number\":\"10000001\"}]}"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Get an invoice

Any of the account's invoices by guid, drafts, recurring templates and credit notes included.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Update invoice accounting status

Updates only the bookkeeping accounting status (and an optional message). It cannot edit amounts, dates, line items or the customer. Any of the four statuses can be set, in any order. result is the invoice's guid; a bad status is refused with Accounting status is required.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or InvoiceAccountingUpdate (object)

The accounting update, JSON-encoded (or as an object).

Responses

Request samples

Content type
application/json
{
  • "data": "{\"accounting_status\":\"Posted\",\"accounting_message\":\"Bókað í DK\"}"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Create a recurring invoice

A template that auto-generates invoices on a schedule. Same amount, line-item and customer rules as a normal invoice, with two differences: for ISK each line's net amount and VAT are rounded to whole krónur before they are summed, and due_date must be at least 72 hours from now. result is the recurring schedule's guid.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or RecurringInvoiceCreate (object)

JSON-encoded RecurringInvoiceCreate object.

Responses

Request samples

Content type
application/json
{
  • "data": "string"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Count invoices

The number of invoices matching the filters. Takes the filters of GET /api/v1/agent/invoices except paging and sorting.

The count covers every invoice record on the account, including recurring-invoice templates and grouped or hidden invoices that the list endpoint leaves out, so it can be higher than the number of invoices the list returns.

Known issue: with date_filter=created_date, to_date currently acts as a second lower bound rather than an upper bound. Filter on the issue date (the default) for reliable ranges.

Authorizations:
kontoOAuth
query Parameters
accounting_status
string
Example: accounting_status=Created,Error

Comma-separated list of Created, Posted, Skipped, Error. Any other value returns status: false with error_code: invalid_accounting_status.

date_filter
string
Default: "issue_date"
Enum: "issue_date" "created_date"

Which date from_date / to_date apply to.

from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

project_guid
string

Only invoices linked to this project. An unknown GUID returns status: false, No project found.

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Only invoices modified at or after this time: Y-m-d, Y-m-d H:i:s or ISO 8601 (UTC). An unparseable value returns HTTP 400 invalid_updated_since.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 42
}

Invoice summary cards for a period

Invoice counts and totals for a period, grouped by status and currency — the figures behind Konto's dashboard cards. Read from pre-computed monthly buckets, so the cost does not grow with invoice volume.

Pick one period mode (evaluated in this order):

Mode Parameters Example
Custom range from_date + to_date ?from_date=2026-01-01&to_date=2026-04-30
Single month year + month ?year=2026&month=4
Months within one year (e.g. a VAT period) year + from_month + to_month ?year=2026&from_month=1&to_month=2
Full year year ?year=2026

Buckets are monthly, so a custom range is widened to whole months: from_date=2026-01-15&to_date=2026-04-15 covers 2026-01-01 – 2026-04-30 (see period in the response). Invoices are placed by issue date.

Statuses:

  • paid — paid invoices. Credit notes are not counted here.
  • sent — issued and unpaid, not past the settlement date (eindagi), no active collection case.
  • overdue — issued and unpaid, past the settlement date, no active collection case.
  • collection — issued and unpaid, with an active collection case.
  • total — always the first entry: the sum of the four above.

Apart from total, only statuses with data are listed. Amounts include VAT, stay in the invoice currency (never converted), and are rounded to 0 decimals for ISK and 2 for other currencies. Credit notes count negative in sent, overdue and collection.

The response is not wrapped in result: period and summaries sit at the top level next to status.

Requires the dashboard_data feature on the account's plan; without it the reply is status: false with a "Subscription does not include the required feature" message.

Authorizations:
kontoOAuth
query Parameters
year
integer
Example: year=2026

Calendar year. Alone for a full year, with month for one month, or with from_month + to_month.

month
integer [ 1 .. 12 ]
Example: month=4

1–12, with year.

from_month
integer [ 1 .. 12 ]
Example: from_month=1

1–12, with year and to_month.

to_month
integer [ 1 .. 12 ]
Example: to_month=2

1–12, with year and from_month.

from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "period": {
    },
  • "summaries": [
    ]
}

Costs

Expenses / receipts. Scope: cost.

List costs

Pages by default: 10 records per page unless you send limit (limit=0 returns everything). A paged reply also carries hasMore, limit, page and next_url (a legacy-API URL; build the next request yourself with page + 1). Newest issue date first unless you pass sort. Each record includes its file and xml base64, so keep pages small. Deleted records and records Cost AI is still reading are not listed.

Send updated_since and/or cursor for sync mode: rows in a stable (updated, id) order, limit per page (default 100, max 500), has_more and next_cursor on the envelope. Pass next_cursor back as cursor until has_more is false. sort and page are then ignored; the other filters still apply.

Authorizations:
kontoOAuth
query Parameters
limit
integer >= 0
Default: 10

Page size. Default 10; 0 returns every match. In sync mode default 100, max 500.

page
integer
Default: 1

Page number (default 1).

accounting_status
string
Example: accounting_status=New,Created

Comma-separated list of New, Created, Posted, Skipped, Error. Any other value returns status: false with error_code: invalid_accounting_status (HTTP 200).

project_guid
string

Only cost records linked to this project. An unknown GUID returns status: false, No project found.

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Sync mode: only rows whose updated_at is at or after this point. Y-m-d, Y-m-d H:i:s or ISO 8601; zoned values are converted to UTC (the server clock). An invalid value is refused with HTTP 400 invalid_updated_since.

cursor
string

Sync mode: the next_cursor of the previous page (opaque). An invalid value is refused with HTTP 400 invalid_cursor.

type
string
Example: type=2353

Exact cost code (see CostAdvancedCreate.type).

supplier_id
string

Exact supplier kennitala / id.

date_filter
string
Default: "date"
Enum: "date" "created_date"

Which date from_date / to_date apply to: date (the record's issue date) or created_date.

from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

sort
string
Default: "date_desc"
Enum: "date_desc" "date_asc" "created_desc" "created_asc"

date_* sorts on the issue date, created_* on the creation date. Any other value falls back to date_desc.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "hasMore": true,
  • "page": 0,
  • "limit": 0,
  • "next_url": "string",
  • "has_more": true,
  • "next_cursor": "string"
}

Create a cost from a receipt file (OCR)

Upload a receipt/invoice image or PDF (multipart/form-data, max 5 MB; images are scaled down to 1028 px).

  • Cost AI on (plan feature): Konto's AI reads amount, supplier, VAT, date and cost code. The answer carries queue: true; the record stays out of GET /costs until the AI is done, so poll GET /costs/{guid}. date is optional and description is not stored. Each upload counts against a monthly AI allowance (2 × the account's available credits); past it the answer is status: false with code: cost_limit_reached (HTTP 200).
  • Cost AI off: date is required and the record is created at once with accounting status Created, holding just the file, date and description.

If you already have the numbers, use /costs/advanced instead. result is the new record's guid. Refusals are HTTP 200 with status: false: Field date is required and must be format Y-m-d, Field file is required, File must be jpg, jpeg, png, gif or pdf, File size must not exceed 5MB.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: multipart/form-data
required
date
string <date>

Y-m-d. Required unless the account has Cost AI.

description
string

Stored only when Cost AI is off.

project_guid
string

Put the record on this project (needs the Projects add-on).

file
required
string <binary>

jpg, jpeg, png, gif or pdf; ≤ 5 MB.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null,
  • "code": "string"
}

Create a cost from structured fields

Record a cost from numbers you already have. Fields are sent flat at the top level (no data wrapper), as JSON — or as multipart/form-data to attach the receipt. For a credit note set credit_note: 1 and keep amount positive. The record may be approved at once if the account's auto-approve rules match. result is the new record's guid.

Refusals are HTTP 200 with status: false: Field date is required and must be format Y-m-d, Field amount is required and must be a positive number, Field how_paid is required, Field supplier_name is required, Field supplier_id is required, Field type is required (an unknown cost code), or a file error.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema:
required
date
required
string <date>

Y-m-d.

amount
required
number > 0

Total including VAT. Positive, also for a credit note.

how_paid
required
integer
Enum: 1 2 3 4

1=Account, 2=Debit card, 3=Credit card, 4=Owner cash.

supplier_name
required
string
supplier_id
required
string

Supplier kennitala / ID.

type
required
string
Enum: "3028" "2173" "3033" "3085" "3090" "2093" "2243" "2193" "2423" "2443" "2353" "2383" "2403" "2263" "3163" "2333" "3140" "2283" "2533" "2633" "3653" "5028" "2153" "3080" "9535"

Cost code (numeric string). Accepted: 3028 product consumption, 2173 purchased labour/services for resale, 3033 salary, 3085 pension fund, 3090 other salary-related, 2093 operating supplies, 2243 housing operating costs, 2193 rent/leasing of facilities, 2423 small tools and equipment, 2443 maintenance and repairs, 2353 various purchased services, 2383 office expenses, 2403 leasing of movable assets, 2263 sales and advertising, 3163 hospitality and gifts, 2333 travel, 3140 vehicle, 2283 other vehicle, 2533 other expenses, 2633 interest and capital income, 3653 interest and capital expenses, 5028 capitalised operating assets, 2153 item, 3080 calculated remuneration, 9535.

description
string
vat_11
number

VAT amount in the 11% bracket — an amount, not a rate.

vat_24
number

VAT amount in the 24% bracket.

credit_note
integer
Enum: 0 1

1 to record as a credit note; keep amount positive.

project_guid
string

Put the record on this project (needs the Projects add-on).

Responses

Request samples

Content type
{
  • "date": "2019-08-24",
  • "amount": 1,
  • "how_paid": 1,
  • "supplier_name": "string",
  • "supplier_id": "string",
  • "type": "3028",
  • "description": "string",
  • "vat_11": 0,
  • "vat_24": 0,
  • "credit_note": 0,
  • "project_guid": "string"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Count costs

Takes the list's filters except paging and sorting. Unlike the list it also counts records Cost AI is still reading, so it can be higher than the number of records GET /costs returns.

Authorizations:
kontoOAuth
query Parameters
accounting_status
string

Comma-separated, as on the list.

type
string
supplier_id
string
project_guid
string
date_filter
string
Default: "date"
Enum: "date" "created_date"
from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

updated_since
string

Only records modified at or after this time (see the list).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 42
}

Get a cost (with file & xml)

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Update cost accounting status

For bookkeeping software: records what happened to an approved record. Only a record whose status is Created can be updated, and only to Posted, Skipped or Error — Created itself is set by Konto, and Posted is terminal. To edit a Created record's data, move it to New first, then re-approve. result is the record's guid.

Refusals (HTTP 200, status: false): Accounting status must be one of: Posted, Skipped, Error, Only costs with accounting_status Created can be updated via the API, No cost found, Data cost invalidate.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or object

JSON-encoded { "accounting_status": "Posted" } (or the object itself).

Responses

Request samples

Content type
application/json
{
  • "data": "{\"accounting_status\":\"Posted\"}"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Delete a cost

Moves the record to Konto's recycle bin: it disappears from the API at once, the user can restore it in Konto for 28 days, and then it is purged. Prefer status Skipped to exclude a record from bookkeeping. Refused (HTTP 200, status: false) for a Posted record (retryable: false) and while the record is being processed (retryable: true, retry_after seconds).

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null,
  • "retryable": true,
  • "retry_after": 0
}

Re-open a cost (move back to New)

Moves an approved (Created), non-Posted cost back to New so it can be edited in Konto; re-approving regenerates its bookkeeping XML. result is the record's guid. Otherwise status: false with No cost found or This cost cannot be moved to New (only approved, non-Posted costs can be re-opened for editing).

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

Projects

Projects group tasks, cost records, sales invoices and scope lines for one customer and track what has been billed and what is still outstanding. Scopes: project_read (reads) or project (reads and writes). The account needs the Projects add-on — without it the consent screen drops both scopes, and a call answers HTTP 200 status: false naming the missing feature.

  • Addressing. Projects, cost records and invoices are addressed by GUID. A project's own rows — manual cost lines and scope lines — have no GUID and use integer ids (project_cost_id, product_id) from the project's costs / products (?expand=costs,products).
  • Bodies. Create / update project read a data field: a JSON object, or the same object JSON-encoded as a string. Every other write takes top-level fields. PUT and DELETE bodies must be application/json. Do not repeat a path parameter in the body: a body key with the same name (guid, cost_guid, …) replaces the path value.
  • Status codes. Business refusals are HTTP 200 with status: false. Conflicts are 409 and malformed version / Idempotency-Key / updated_since / cursor values 400, both with an error_code. Creates answer 201. A token without the scope gets a real 403.
  • external_ref — your own id (max 128 characters), unique per account. Filter with ?external_ref=; a create with an existing one returns that project (existing: true) instead of a duplicate.
  • version — +1 on every change to the project's own fields, made in Konto or through the API. Send it with PUT / DELETE to get 409 version_conflict (with current_version) instead of overwriting a newer edit. Linking or unlinking costs, invoices, scope lines or related projects does not change the project's version or updated_at.
  • Idempotency-Key — optional header on every write (see the parameter).
  • Sync — updated_since / cursor on the list switch to keyset paging over updated_at; include_removed=1 adds removed projects.
  • Tasks, cost records and invoices are put on a project with project_guid on their own create endpoints, and filtered with ?project_guid=.
  • Calls made with an agent token also carry an action_id in the envelope.

List projects

Templates are never listed; removed projects only with include_removed=1. from_date / to_date filter on the creation date.

Paging. Without limit, every matching project is returned in one response (there is no default page size). With limit the envelope also carries total, page and limit.

Sync mode. updated_since and/or cursor switch to keyset paging: rows ordered by (updated_at, id) ascending, limit default 100 / max 500, has_more and next_cursor on the envelope — pass next_cursor back as cursor. sort, sort_type and page are then ignored; every other filter (including from_date / to_date) still applies.

totals is included only with with_totals=1 (computed per project).

Authorizations:
kontoOAuthkontoOAuth
query Parameters
status
string
Example: status=active

active | completed | on_hold | invoiced, or 1–4. Removed projects cannot be selected by status (use include_removed). An unknown value is refused with invalid_status.

search_text
string

Substring match on name, project number (digits only), owner team, category or customer name.

customer_guid
string

Only this customer's projects. An unknown or deactivated customer is refused ("No customer found").

external_ref
string <= 128 characters

Exact match on your own reference.

from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

sort
string
Default: "created_at"
Enum: "created_at" "name" "start_date" "updated_at"

Sort column (paged / unpaged mode only). Any other value falls back to created_at.

sort_type
string
Default: "down"
Enum: "down" "up"

down = descending, up = ascending.

limit
integer >= 1

Page size. Without it every match is returned in one response. In sync mode default 100, max 500.

page
integer
Default: 1

Page number (default 1).

with_totals
boolean
Default: false

1 to include totals on every project.

expand
string
Example: expand=costs,products,invoices

Comma-separated linked collections to include: tasks, costs, products, invoices, logs (newest 200), related, files, comments. Unknown names are ignored.

include_removed
string
Default: "0"
Enum: "0" "1"

1 to include removed records (for time entries: deleted ones, with deleted_at set).

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Sync mode: only rows whose updated_at is at or after this point. Y-m-d, Y-m-d H:i:s or ISO 8601; zoned values are converted to UTC (the server clock). An invalid value is refused with HTTP 400 invalid_updated_since.

cursor
string

Sync mode: the next_cursor of the previous page (opaque). An invalid value is refused with HTTP 400 invalid_cursor.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "total": 0,
  • "page": 0,
  • "limit": 0,
  • "has_more": true,
  • "next_cursor": "string"
}

Create a project

A new project is always active and gets the account's next PRJ-n number unless project_number is given. Scope lines are created with it: a product line becomes a scope line, a service line becomes a task on the project. Prefill from a saved template (template_guid) or a quote (quote_guid); fields you send win.

If external_ref matches an existing project, nothing is created: the response is HTTP 200 with existing: true, result = that project's guid and project = that project (without totals). Otherwise 201, result = the new guid and project = the new project with its products and tasks.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required

The project as data — a JSON object, or the same object JSON-encoded as a string.

required
ProjectCreate (object) or string

Responses

Request samples

Content type
application/json
Example
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "string",
  • "project": {
    },
  • "existing": true
}

Count projects

Same filters as the list (status, search_text, customer_guid, external_ref, from_date, to_date, include_removed, updated_since). result is a bare integer.

Authorizations:
kontoOAuthkontoOAuth
query Parameters
status
string

active | completed | on_hold | invoiced, or 1–4.

search_text
string
customer_guid
string
external_ref
string <= 128 characters
from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

include_removed
string
Default: "0"
Enum: "0" "1"

1 to include removed records (for time entries: deleted ones, with deleted_at set).

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Sync mode: only rows whose updated_at is at or after this point. Y-m-d, Y-m-d H:i:s or ISO 8601; zoned values are converted to UTC (the server clock). An invalid value is refused with HTTP 400 invalid_updated_since.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 14
}

List saved project templates

The account's saved "New Project" templates with their scope lines, in the shape create accepts — pass a template's guid as template_guid, or its scope as scope. Template guids are not projects; the other project endpoints answer "No project found" for them.

Authorizations:
kontoOAuthkontoOAuth

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ]
}

Get a project

Always includes totals. Add expand= for linked rows. Also returns a removed project (status 5), though every write to it is refused.

Authorizations:
kontoOAuthkontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

query Parameters
expand
string
Example: expand=costs,products,invoices

Comma-separated linked collections to include: tasks, costs, products, invoices, logs (newest 200), related, files, comments. Unknown names are ignored.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Update a project

Only the keys present in data change. status cannot be set to removed — use DELETE. A removed project cannot be updated (or restored) through the API. Send version (in data or top level) for an optimistic check. A timeline entry is written only when the status actually changes. result is the updated project (without totals).

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required

The fields to change as data (object or JSON-encoded string). version may also be sent at the top level.

required
ProjectUpdate (object) or string
version
integer >= 1

Alternative to data.version (data.version wins when both are sent).

Responses

Request samples

Content type
application/json
Example
{
  • "data": {
    }
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Remove a project (soft delete)

Sets status 5 (removed), like the Delete button in Konto. Tasks, cost records and invoices keep their link; the project leaves every list (still visible with include_removed=1 or by guid). There is no restore through the API. version may be sent as a query parameter or in a JSON body. result is the project's guid.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

query Parameters
version
integer >= 1

Optimistic check: the project's current version. A stale value is refused with HTTP 409 version_conflict; a non-positive or non-integer value with HTTP 400 invalid_version.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "string"
}

Project activity timeline

Newest first. Works for removed projects too.

Authorizations:
kontoOAuthkontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

query Parameters
limit
integer [ 1 .. 500 ]
Default: 50

Number of entries (default 50, max 500).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ]
}

Link cost record(s) to a project

Links one (cost_guid) or several (cost_guids) of the account's cost records. All-or-nothing: every record is checked before any is linked — each must exist, not be deleted, and not be on a project yet (a cost record belongs to at most one project; 409 already_linked otherwise). The billable subtotal defaults to the record's own amount. Writes the project timeline and the record's history. Returns HTTP 200 with one cost row per linked record.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
cost_guid
string

One cost record.

Array of strings or string

Several cost records — an array or a comma-separated string. Linked all-or-nothing.

subtotal
number >= 0

Billable amount, in the record's currency. Single record only (refused with cost_guids of more than one). Default: the record's own amount.

Responses

Request samples

Content type
application/json
Example
{
  • "cost_guid": "b7e0c1d2-8d11-11ef-9c3a-0242ac120002",
  • "subtotal": 21500
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ]
}

Set the billable subtotal of a linked cost

Sets subtotal — the amount billable to the customer for this row, independent of the cost record's own amount (the record is not changed). Refused once the row has been invoiced (409 already_invoiced). To set the subtotal of a manual cost line instead, send its project_cost_id in the body; it takes precedence over the path's cost_guid.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

cost_guid
required
string

GUID of a cost record linked to this project.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
subtotal
required
number >= 0

Amount billable to the customer, in the row's currency. A decimal comma is accepted in strings.

project_cost_id
integer

Address a manual cost line instead of the path's cost record (takes precedence).

Responses

Request samples

Content type
application/json
{
  • "subtotal": 18000
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Unlink a cost record from a project

Clears the record's project link and deletes the billing row (a custom subtotal is lost); the cost record itself is otherwise untouched. Refused once the row has been invoiced (409 already_invoiced). For a manual cost line use DELETE …/manual-costs/{id}. result is the cost record's guid.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

cost_guid
required
string

GUID of a cost record linked to this project.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "string"
}

Add a manual cost line

A cost that belongs only to the project (wages, a subcontractor on a contract) and never becomes a cost record. ISK only. Its subtotal starts equal to amount. Validation errors name the input in field.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
supplier_name
required
string <= 255 characters
amount
required
number > 0

ISK. Strings may use Icelandic formatting ("84.940", "1.234,50", "84 940 kr."): a comma is the decimal mark and a dot followed by exactly three digits is a thousands separator, so send plain numbers. A minus sign is dropped.

cost_date
string

Y-m-d or D.M.YYYY. Default (and on edit, when omitted): today.

description
string <= 500 characters
project_file_id
integer

One of the project's files (expand=files). Files cannot be uploaded through this API.

Responses

Request samples

Content type
application/json
{
  • "supplier_name": "Jón Jónsson — laun október",
  • "amount": 412000,
  • "cost_date": "2026-10-31",
  • "description": "Vinnulaun, 80 klst."
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Edit a manual cost line

Full replacement — send every field again. supplier_name and amount are required; an omitted cost_date becomes today, and an omitted description or project_file_id is cleared. A subtotal that was never customised follows the new amount. Refused once invoiced (409 already_invoiced). A linked cost record's row answers "No cost found".

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

id
required
integer >= 1

project_cost_id of a manual cost line (from the project's costs).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
supplier_name
required
string <= 255 characters
amount
required
number > 0

ISK. Strings may use Icelandic formatting ("84.940", "1.234,50", "84 940 kr."): a comma is the decimal mark and a dot followed by exactly three digits is a thousands separator, so send plain numbers. A minus sign is dropped.

cost_date
string

Y-m-d or D.M.YYYY. Default (and on edit, when omitted): today.

description
string <= 500 characters
project_file_id
integer

One of the project's files (expand=files). Files cannot be uploaded through this API.

Responses

Request samples

Content type
application/json
{
  • "supplier_name": "Jón Jónsson — laun október",
  • "amount": 412000,
  • "cost_date": "2026-10-31",
  • "description": "Vinnulaun, 80 klst."
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Remove a manual cost line

Permanently deletes the line (it is the cost; there is no record behind it). Refused once invoiced (409 already_invoiced). result is the deleted project_cost_id.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

id
required
integer >= 1

project_cost_id of a manual cost line (from the project's costs).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 0
}

Link an existing invoice to a project

Links one of the account's sent sales invoices. Drafts, quotes, notes, orders, voided invoices, recurring templates and group invoices are refused (status false, error_code not_linkable). An invoice can be on one project only (409 already_linked). The invoice's accounting status and XML are not touched. result is the invoice object (same shape as GET /invoices/{guid}).

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
invoice_guid
required
string

A sent sales invoice of this account.

Responses

Request samples

Content type
application/json
{
  • "invoice_guid": "e93b2a10-8d20-11ef-9c3a-0242ac120002"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Unlink an invoice from a project

Clears the link on both sides. An invoice that was created from this project (it bills the project's tasks, costs or scope lines) cannot be unlinked (409 created_from_project). result is the invoice's guid.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

invoice_guid
required
string

GUID of a sales invoice linked to this project.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "string"
}

Add a scope line

Adds a line item to the project's scope, ad hoc or from the catalogue. With item_guid only the catalogue description is copied (a sent description is ignored) — qty, unit_price and tax still come from the request and default to 1, 0 and 0. estimated defaults to 0 here (unlike scope lines sent with create). Validation errors name the input in field.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
Any of
item_guid
string

Catalogue item; only its description is copied (a sent description is ignored).

description
required
string <= 500 characters
qty
number > 0
Default: 1
unit_price
number >= 0
Default: 0

ISK, excluding VAT.

string or number

VAT: S, AA, Z or 24 / 11 / 0. Default 0.

discount_type
string
Default: "percent"
Enum: "percent" "amount"
discount_value
number >= 0
Default: 0

A percentage (max 100) or an ISK amount.

estimated
number >= 0
Default: 0

Responses

Request samples

Content type
application/json
{
  • "description": "Parket, eik 14 mm",
  • "qty": 65,
  • "unit_price": 6890,
  • "tax": "S",
  • "discount_type": "percent",
  • "discount_value": 10
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Edit a scope line

Only the fields sent change; the catalogue link (item_guid) cannot be changed. Refused once invoiced (409 already_invoiced).

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

id
required
integer >= 1

product_id of a scope line (from the project's products).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
description
string <= 500 characters
qty
number > 0
unit_price
number >= 0
string or number
discount_type
string
Enum: "percent" "amount"

Any other value is stored as percent.

discount_value
number >= 0
estimated
number >= 0

Responses

Request samples

Content type
application/json
{
  • "qty": 72,
  • "unit_price": 6490
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Remove a scope line

Permanently deletes the line. Refused once invoiced (409 already_invoiced). result is the deleted product_id.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

id
required
integer >= 1

product_id of a scope line (from the project's products).

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 0
}

Link a related project

Marks two of the account's live projects as related. Idempotent — an existing link (in either direction) is returned as is. A project cannot be linked to itself. Projects of the same customer are already related automatically (link_type customer under expand=related).

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
related_guid
required
string

GUID of another live project of this account.

Responses

Request samples

Content type
application/json
{
  • "related_guid": "f0c8d3b4-8d31-11ef-9c3a-0242ac120002"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Unlink a related project

Removes a manual link in either direction (removed projects allowed). The automatic same-customer relation cannot be removed; when there is no manual link the answer is status false, "These projects are not linked." result is the related project's guid.

Authorizations:
kontoOAuth
path Parameters
guid
required
string <uuid>

The project's GUID.

related_guid
required
string

GUID of the related project.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "string"
}

Tasks

Tasks & time tracking. Scope: task.

List tasks

Newest task first. Archived tasks are left out unless include_removed=1. Outside sync mode, omitting limit returns every matching task.

type picks a bucket: open = not invoiced yet, invoice = invoiced in the last 28 days, older = invoiced before that, all = no bucket. An unknown value falls back to the default.

Send updated_since and/or cursor for sync mode: rows in a stable (updated, id) order, limit per page (default 100, max 500), has_more and next_cursor on the envelope. Pass next_cursor back as cursor until has_more is false. In sync mode type defaults to all.

Authorizations:
kontoOAuth
query Parameters
type
string
Default: "open"
Enum: "open" "invoice" "older" "all"

Default open (all in sync mode).

project_guid
string

Only tasks in this project. An unknown GUID returns status: false, No project found.

customer_guid
string

Only tasks for this (active) customer. An unknown GUID returns status: false, No customer found.

external_ref
string <= 128 characters

Only the task with this external reference (max 128 characters).

include_removed
string
Default: "0"
Enum: "0" "1"

1 to include removed records (for time entries: deleted ones, with deleted_at set).

limit
integer >= 1

Page size. Without it every match is returned in one response (there is no default page size). In sync mode default 100, max 500.

page
integer
Default: 1

Page number (default 1).

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Sync mode: only rows whose updated_at is at or after this point. Y-m-d, Y-m-d H:i:s or ISO 8601; zoned values are converted to UTC (the server clock). An invalid value is refused with HTTP 400 invalid_updated_since.

cursor
string

Sync mode: the next_cursor of the previous page (opaque). An invalid value is refused with HTTP 400 invalid_cursor.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "limit": 0,
  • "has_more": true,
  • "next_cursor": "string"
}

Create a task

A task for a customer, billed with a catalogue item (item.guid) or a new item described inline — the inline item is added to the catalogue. item is required.

If external_ref matches an existing task, nothing is created: the answer is that task's guid with existing: true and the task in task. Otherwise result is the new task's guid, plus its version.

A refusal is HTTP 200 with status: false and e.g. Customer is required, Item is required, No project found, Customer does not match the project customer, Project name is required., an item error (Description is required, Unit of measure is required, Tax is required), estimated_amount must be a non-negative number, Data task invalidate, or error_code invalid_external_ref / feature_required.

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or TaskCreate (object)

JSON-encoded TaskCreate object (or the object itself).

Responses

Request samples

Content type
application/json
Example
{
  • "data": "{\"customer_guid\":\"5d1e2c4a-8b7f-11ef-9c3a-0242ac120002\",\"name\":\"Uppsetning\",\"item\":{\"guid\":\"43521ec8-3413-11e7-b148-408d5c6208bc\"},\"external_ref\":\"OPS-4711-T1\"}"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "string",
  • "version": 0,
  • "existing": true,
  • "task": {
    }
}

Count tasks

Takes the list's filters except paging.

Authorizations:
kontoOAuth
query Parameters
type
string
Default: "open"
Enum: "open" "invoice" "older" "all"

Default open (all when updated_since is sent).

project_guid
string
customer_guid
string
external_ref
string <= 128 characters
include_removed
string
Default: "0"
Enum: "0" "1"

1 to include removed records (for time entries: deleted ones, with deleted_at set).

updated_since
string

Only tasks modified at or after this time (see the list).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 42
}

Get a task (with time-log entries)

Archived tasks are returned too (status 2). Each log entry carries the task-LOG guid needed by the log edit/delete endpoints.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Update a task

Changes a task's name, estimate, external reference or project. Put only the fields to change in data (a JSON-encoded string, or a nested object); omitted fields are left as they are. PUT bodies must be application/json.

  • project_guid moves the task to another project of the same customer, or unlinks it with "". It needs the Projects add-on (also to unlink), and an invoiced task cannot change project (HTTP 409 task_invoiced).
  • external_ref must be unique across the account's tasks (HTTP 409 external_ref_exists); "" clears it.
  • version (inside data, or at the top level) makes the write conditional: if the task has changed since that version the update is refused with HTTP 409 version_conflict and current_version. Without it the write is unconditional.

Archived tasks cannot be updated (No task found). Returns the updated task, in the same shape as GET /api/v1/agent/tasks/{guid}.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
required
string or TaskUpdate (object)

JSON-encoded TaskUpdate object (a nested object is also accepted). Must contain at least one field.

version
integer >= 1

Alternative to data.version; used only when data has no version.

Responses

Request samples

Content type
application/json
{
  • "data": "{\"name\":\"Uppsetning – áfangi 2\",\"estimated_amount\":250000,\"external_ref\":\"OPS-4711-T2\",\"version\":3}"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Start a task timer

Starts the timer of a stopped, not yet invoiced task (status 0 → 1). Anything else — a running, archived or invoiced task, or an unknown guid — answers status: false, No task found.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Stop the timer and log the time

Stops the timer and logs the hours since it was started (2 decimals) as a timer entry dated today. result is that entry. With adv: 1 the quantity is (days from date to today, inclusive) × p × h instead.

The server does not check that the timer is running: on a stopped task it logs the time since the last start. Call it only after POST …/start. An invoiced task, or an unknown guid, answers status: false, No task found.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
optional
description
string

Note on the entry.

external_ref
string <= 128 characters

Your own reference for the entry. Must be unique within the task.

adv
integer
Enum: 0 1

1 for advanced mode (days × p × h).

date
string <date>

Advanced mode: the first day of the range (the last is today).

p
integer

Advanced mode: number of workers.

h
integer

Advanced mode: hours per day.

Responses

Request samples

Content type
application/json
{
  • "description": "string",
  • "external_ref": "string",
  • "adv": 0,
  • "date": "2019-08-24",
  • "p": 0,
  • "h": 0
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Archive a task

Sets status 2 (archived); the task leaves the lists unless include_removed=1. An invoiced or already archived task answers status: false, No task found.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": null
}

Edit a task-log entry

Takes the task-LOG entry's guid (from a task's logs or GET /tasks/logs), not the task's guid. Fields are sent flat, like POST /tasks/log, and the edit is a full replacement: qty and end_date are required, and an omitted description, adv, p or h is cleared. The entry becomes a manual entry dated date, and the task's total moves by the difference. result is the updated entry.

Send version to make the edit conditional (HTTP 409 version_conflict with current_version if the entry changed since). A refusal is status: false with No task log found, Please input quantity, Please input end date or Please input start date (advanced mode).

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema: application/json
required
guid
required
string

Task-LOG entry guid.

version
integer >= 1

The entry's version as you last read it.

qty
required
number or string

Quantity in the task item's unit — normally hours. Must be greater than 0; a decimal comma is accepted ("1,5"). In advanced mode any positive value (it is replaced).

date
required
string <date>

Entry date, Y-m-d. In advanced mode: the first day of the range.

end_date
required
string <date>

Y-m-d. In advanced mode: the last day of the range. Otherwise send the same day as date.

description
string
adv
integer
Default: 0
Enum: 0 1

1 for advanced mode (days × p × h).

p
integer >= 1

Advanced mode: number of workers. Use a whole number.

h
integer >= 1

Advanced mode: hours worked per day. Use a whole number — the entry stores h as an integer, and a decimal comma is not understood.

external_ref
string <= 128 characters

Your own reference for the entry. Must be unique within the task.

Responses

Request samples

Content type
application/json
{
  • "guid": "0f8e2c1a-6b1d-11f1-9a5e-0242ac120002",
  • "qty": 2,
  • "date": "2026-10-14",
  • "end_date": "2026-10-14",
  • "description": "Uppsetning á vef",
  • "version": 1
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Log work on a task

Adds a manual time entry to a task and returns it. Fields are sent flat at the top level (no data wrapper), as JSON — or as multipart/form-data to attach a file.

Two modes:

  • Simple — qty units (normally hours) on date. end_date is still required; send the same day as date.
  • Advanced (adv: 1) — a date range worked by a crew. The logged quantity is computed as (days from date to end_date, inclusive) × p (workers) × h (hours per day); send both p and h, or the entry is logged as 0. qty must still be a positive number — it is replaced by the computed value.

The task's total grows by the logged quantity. A task that has already been invoiced takes no new entries (the reply is No task found).

Authorizations:
kontoOAuth
header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Request Body schema:
required
guid
required
string

The task's GUID (not a time-entry GUID).

qty
required
number or string

Quantity in the task item's unit — normally hours. Must be greater than 0; a decimal comma is accepted ("1,5"). In advanced mode any positive value (it is replaced).

date
required
string <date>

Entry date, Y-m-d. In advanced mode: the first day of the range.

end_date
required
string <date>

Y-m-d. In advanced mode: the last day of the range. Otherwise send the same day as date.

description
string
adv
integer
Default: 0
Enum: 0 1

1 for advanced mode (days × p × h).

p
integer >= 1

Advanced mode: number of workers. Use a whole number.

h
integer >= 1

Advanced mode: hours worked per day. Use a whole number — the entry stores h as an integer, and a decimal comma is not understood.

external_ref
string <= 128 characters

Your own reference for the entry. Must be unique within the task.

Responses

Request samples

Content type
Example
{
  • "guid": "614669f6-3560-11e7-8d19-408d5c6208bc",
  • "qty": 1.5,
  • "date": "2026-10-14",
  • "end_date": "2026-10-14",
  • "description": "Uppsetning á vef",
  • "external_ref": "OPS-4711-L1"
}

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": {
    }
}

Delete a task-log entry

Takes the task-LOG entry's guid, not the task's guid. The entry's quantity is taken off the task's total and the entry is removed; it cannot be restored, but GET /tasks/logs?include_removed=1 still lists it with deleted_at set so a sync sees it go. result is the entry's guid; an unknown guid answers status: false, No task log found. Send version (query or JSON body) to make the delete conditional.

Authorizations:
kontoOAuth
path Parameters
guid
required
string

Task-LOG entry guid.

query Parameters
version
integer >= 1

The entry's version as you last read it. A stale value is refused with HTTP 409 version_conflict; a non-positive or non-integer value with HTTP 400 invalid_version.

header Parameters
Idempotency-Key
string [ 1 .. 128 ] characters
Example: 7c1f0d2e-ops-4711

Optional on any write. A retry with the same key and the same request (path and body) replays the stored response — same HTTP status, plus the header Idempotent-Replayed: true — and writes nothing. The same key with a different request is refused with HTTP 409 idempotency_key_reuse. Keys are per account and kept for at least 7 days. Whatever was answered is replayed, refusals included, so use a new key after changing the payload. Also accepted as an idempotency_key parameter. A key longer than 128 characters is refused with HTTP 400 invalid_idempotency_key.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": "614669f6-3560-11e7-8d19-408d5c6208bc"
}

List time entries

Time entries (task logs) across all of the account's tasks, archived tasks included. Two ways to page:

  • Page mode (default) — newest entry date first; limit (default 50, max 500) and page. The response carries total, page and limit.
  • Sync mode — send updated_since and/or cursor. Entries come in (updated_at, id) order, limit defaults to 100 (max 500), and the response carries has_more and next_cursor: pass next_cursor back as cursor until has_more is false. page is ignored; the other filters still apply. Add include_removed=1 to also receive deleted entries (deleted_at set).

from_date / to_date filter on the entry date (date) and must be Y-m-d — anything else returns status: false, Field from_date must be format Y-m-d.

Authorizations:
kontoOAuth
query Parameters
task_guid
string

Only entries of this task. An unknown GUID returns status: false, No task found.

project_guid
string

Only entries of tasks in this project. An unknown GUID returns status: false, No project found.

external_ref
string <= 128 characters

Exact match on the entry's external_ref (max 128 characters).

from_date
string <date>

Start date (YYYY-MM-DD).

to_date
string <date>

End date (YYYY-MM-DD).

include_removed
string
Default: "0"
Enum: "0" "1"

1 to include removed records (for time entries: deleted ones, with deleted_at set).

updated_since
string
Example: updated_since=2026-10-01T00:00:00Z

Sync mode: only rows whose updated_at is at or after this point. Y-m-d, Y-m-d H:i:s or ISO 8601; zoned values are converted to UTC (the server clock). An invalid value is refused with HTTP 400 invalid_updated_since.

cursor
string

Sync mode: the next_cursor of the previous page (opaque). An invalid value is refused with HTTP 400 invalid_cursor.

limit
integer [ 1 .. 500 ]

Page size — default 50 in page mode, 100 in sync mode; capped at 500.

page
integer >= 1
Default: 1

Page number (page mode only).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "total": 0,
  • "page": 0,
  • "limit": 0,
  • "has_more": true,
  • "next_cursor": "string"
}

Product sales

Read-only sell pages. Scope: sell_product.

List product sales (sell pages)

Read-only, newest first, always paged (10 per page by default). type is a number: 0 standard item, 1 subscription, 2 donation, 3 coupon, 4 event. Only event sales have attendees.

Authorizations:
kontoOAuth
query Parameters
search_text
string

Substring of the product name.

limit
integer >= 1
Default: 10

Page size (default 10).

page
integer
Default: 1

Page number (default 1).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "total": 0,
  • "page": 0
}

Count product sales

Authorizations:
kontoOAuth
query Parameters
search_text
string

Substring of the product name.

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": 42
}

List attendees of an event sale

One row per attendee. Paging (10 per page by default) runs over the purchases, not the attendees, and total counts purchases — one purchase can carry several attendees. Refused with status: false: No selling found (unknown guid) or This selling is not an event (type other than 4).

Authorizations:
kontoOAuth
path Parameters
guid
required
string

The resource's GUID.

query Parameters
limit
integer >= 1
Default: 10

Page size (default 10).

page
integer
Default: 1

Page number (default 1).

Responses

Response samples

Content type
application/json
{
  • "status": true,
  • "message": "string",
  • "result": [
    ],
  • "total": 0,
  • "page": 0
}