QUiCK Public API documentation
HU EN

Getting started

Check the connection, identify your company and upload an expense document — in about five minutes.

In this guide you will verify the connection, read your company details, list partners and finally upload an expense document. The whole flow takes about five minutes.

Prerequisites

Export the token as an environment variable so the examples below are runnable:

export QUICK_API_TOKEN="XXXXXXXX.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

Never commit your token

The API token grants full access to your company's accounting data. It must never end up in version control, a client-side bundle or an error report.

1. Check the connection

Start with GET /1/pulse/ — it validates the token and returns the state of the company's cash and bank accounts:

curl https://api.quick.zenheads.hu/1/pulse/ \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "summary": 1284500,
  "accounts": [
    {
      "id": 12,
      "name": "Bank – HUF",
      "account_number": "12345678-00000000-00000000",
      "currency": "HUF",
      "current_balance": "1284500.00",
      "last_updated": "2026-07-14T08:12:00Z"
    }
  ]
}

If the response is 401 Unauthorized, the token is wrong or missing — double-check the Authorization header.

2. Identify your company

GET /2/company-info/ returns the base data of the token's company. The id field is your company identifier, which you can pass in the Quick-Company-Id header when needed.

curl https://api.quick.zenheads.hu/2/company-info/ \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "id": 4821,
  "name": "Riport Applications Kft.",
  "tax_account_number": "12345678-2-42",
  "default_currency_name": "HUF",
  "advanced_accounting": true
}

3. List partners

List endpoints return a paginated envelope — see the Pagination section below for how to page through the rest:

curl "https://api.quick.zenheads.hu/1/partners/?page_size=2" \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "count": 128,
  "next": "https://api.quick.zenheads.hu/1/partners/?page=2&page_size=2",
  "previous": null,
  "results": [
    {
      "id": 5501,
      "name": "Example Customer Ltd.",
      "tax_number": "12345678-2-42",
      "city": "Budapest",
      "zip_code": "1051",
      "is_customer": true,
      "is_vendor": false
    }
  ]
}

4. Upload an expense document

Expenses are recorded by uploading the document itself: POST /2/expenses/create/ expects the file contents Base64-encoded in the content field, together with the file name. A single call accepts at most 5 documents.

CONTENT=$(base64 -i invoice.pdf)

curl -X POST https://api.quick.zenheads.hu/2/expenses/create/ \
  -H "Authorization: Token $QUICK_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"expenses\": [
      { \"filename\": \"invoice.pdf\", \"content\": \"$CONTENT\" }
    ],
    \"source\": \"public_api\"
  }"

A successful call returns 201 Created with the identifiers of the expenses accepted for processing:

{
  "processed": [90231]
}

All documents in the call are validated up front — one rejected file fails the whole call with a 400 and no expenses are created, rather than leaving you with a partial batch.

Data extraction runs asynchronously after this call returns. The system extracts the expense data from the uploaded document; the extracted fields become available on the expense once processing completes, and can be read with GET /1/expenses/ or GET /2/expenses/{expenseId}/.

Versions: which prefix for which resource

There is no single rule splitting /1/ from /2/ — it reflects when each resource was added, not a read/write or basic/advanced split. Bulk expense actions (approve, check, export) are /1/ only; plain reads like company info and companies are /2/ only. Use this table to find where a resource actually lives:

Resource List Detail Create Update
Expenses GET /2/expenses/ GET /2/expenses/{expenseId}/ POST /2/expenses/create/ PATCH /2/expenses/{expenseId}/update/
Incomes GET /1/incomes/ GET /1/incomes/{incomeId}/ PATCH /2/incomes/{incomeId}/update/
Documents GET /1/documents/ GET /2/documents/{documentId}/ POST /2/documents/create/ POST /2/documents/update/{documentId}/
Document files / artifacts GET /1/documents/files/ POST /1/artifacts/expense/, POST /1/artifacts/income/
Partners GET /1/partners/
Payments / accounts GET /1/payments/, GET /1/accounts/
Monthly taxes GET /2/monthly-taxes/ POST /2/taxes/create/ POST /2/taxes/update/
Monthly salaries GET /2/monthly-salaries/ POST /2/salaries/create/ POST /2/salaries/update/
Tax codes GET /1/tax-codes/
Ledger numbers, VAT categories, tags, expense/revenue types GET /2/… POST /2/…/create/ POST/PATCH /2/…/update/
Companies, company info, user profile, audit XML GET /2/… PATCH /2/company-info/update/{companyId}/
Pulse GET /1/pulse/

Worth knowing alongside the table:

A single integration is free to mix /1/ and /2/ calls — there's no version-wide switch to flip.

5. Pagination

List endpoints are paginated, but the response shape depends on which version the endpoint belongs to.

/1/ endpoints: page-based

Control the page with page and page_size. The response includes a total count, and next/previous are page-numbered URLs you can construct yourself:

curl "https://api.quick.zenheads.hu/1/partners/?page_size=2" \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "count": 128,
  "next": "https://api.quick.zenheads.hu/1/partners/?page=2&page_size=2",
  "previous": null,
  "results": [
    {
      "id": 5501,
      "name": "Example Customer Ltd.",
      "tax_number": "12345678-2-42",
      "city": "Budapest",
      "zip_code": "1051",
      "is_customer": true,
      "is_vendor": false
    }
  ]
}

/2/ endpoints: cursor-based

Only page_size is yours to set. next/previous are opaque cursor URLs — follow them as given, do not construct or decode the cursor value yourself. There is no count: cursor pagination cannot compute a total efficiently.

curl "https://api.quick.zenheads.hu/2/accounting/ledger-numbers/?page_size=2" \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "next": "https://api.quick.zenheads.hu/2/accounting/ledger-numbers/?cursor=cD0yMDI2LTA4LTAxKzEwJTNBMTIlM0EwMA%3D%3D&page_size=2",
  "previous": null,
  "results": [
    { "id": 12, "name": "Bank", "code": "384" },
    { "id": 13, "name": "Cash", "code": "381" }
  ]
}

Not every /2/ endpoint paginates: master-data lists such as GET /2/tags/, GET /2/expense-types/ and GET /2/revenue-types/ return their rows directly, with no envelope. The API Reference shows which is which — a paginated one declares next/previous/results.

No more pages

Both styles signal the end the same way: next is null.

Five lists put an object in results, not an array

Most lists put an array of records in results. These five put a single object there instead — the paginated page under its own key, next to the reference sets needed to resolve the ids those records carry:

Endpoint The page sits under Reference sets alongside it
GET /1/expenses/ expenses partners, currencies, tags, expense_types, accounts
GET /1/incomes/ incomes partners, currencies, tags, revenue_types
GET /2/expenses/ expenses partners, currencies, tags, expense_types, accounts
GET /2/monthly-salaries/ monthly_salaries tags
GET /2/monthly-taxes/ monthly_taxes tags

The expense list is the fullest example — the page of invoices sits under expenses, and partner, currency, expense_type and account ids are numbers, not embedded objects:

{
  "next": "https://api.quick.zenheads.hu/2/expenses/?cursor=cD0yMDI2LTA4LTAxKzEwJTNBMTIlM0EwMA%3D%3D&page_size=2",
  "previous": null,
  "results": {
    "expenses": [
      {
        "id": 90231,
        "invoice_number": "INV-2026-001",
        "partner": 5501,
        "partner_name": "Example Kft.",
        "currency": 1,
        "accounting_status": "pending"
      }
    ],
    "partners": [{ "id": 5501, "name": "Example Kft." }],
    "currencies": [{ "id": 1, "name": "HUF" }],
    "tags": [],
    "expense_types": [],
    "accounts": []
  }
}

Only results.expenses is paginated — the reference sets describe the whole page, so page_size applies to the invoices alone. Requesting fields=accounting adds vats and vat_categories to the same object.

The other four follow the same rule with their own keys. GET /1/expenses/ and GET /1/incomes/ are page-based, so their envelope also carries a count; the /2/ ones are cursor-based and do not.

Next steps