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://test-quick-1-api.test.env.quick.riport.co.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://test-quick-1-api.test.env.quick.riport.co.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://test-quick-1-api.test.env.quick.riport.co.hu/1/partners/?page_size=2" \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "count": 128,
  "next": "https://test-quick-1-api.test.env.quick.riport.co.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://test-quick-1-api.test.env.quick.riport.co.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}/.

5. Download the invoice images of expenses and incomes

Every expense or income can have an invoice image (called artifact in the API): the scanned or uploaded invoice file, a PDF or an image. The expense list and detail endpoints only tell you whether one exists — has_artifact — and describe it with artifact_filename, artifact_extension and artifact_created_at. The file itself comes from a separate lookup, which takes a list of ids and returns a temporary download link for each.

First list the expenses and pick the ids with has_artifact: true:

curl https://test-quick-1-api.test.env.quick.riport.co.hu/2/expenses/ \
  -H "Authorization: Token $QUICK_API_TOKEN"

Then ask for the invoice images of those ids:

curl -X POST https://test-quick-1-api.test.env.quick.riport.co.hu/1/artifacts/expense/ \
  -H "Authorization: Token $QUICK_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "ids": [90231, 90232, 90233] }'

The response is a plain list, without pagination:

[
  {
    "url": "https://example-bucket.s3.amazonaws.com/artifacts/invoice-2026-001.pdf?X-Amz-Expires=3600&X-Amz-Signature=abc123",
    "expense_id": 90231,
    "filename": "invoice-2026-001.pdf",
    "extension": "pdf"
  },
  {
    "url": "https://example-bucket.s3.amazonaws.com/artifacts/scan-0042.jpg?X-Amz-Expires=3600&X-Amz-Signature=def456",
    "expense_id": 90232,
    "filename": "scan-0042.jpg",
    "extension": "jpg"
  }
]

Here 90233 has no invoice image, so it is simply missing from the list — expenses without an image, and ids that belong to another company, are omitted rather than reported as errors. Match the items to your request by expense_id, not by position.

The url is a temporary, pre-signed storage link. Download it with a plain GET and without the Authorization header — the signature is part of the link, and the link stops working after it expires (one hour by default). Fetch the file right away and ask again for a fresh link whenever you need it later; do not store the url itself.

curl -o invoice-2026-001.pdf "https://example-bucket.s3.amazonaws.com/artifacts/invoice-2026-001.pdf?X-Amz-Expires=3600&X-Amz-Signature=abc123"

Incomes work the same way through POST /1/artifacts/income/, with the income ids in ids and income_id in the response items. Take the ids from GET /1/incomes/; incomes have no has_artifact flag, so send the ids you need and the ones without a invoice image are left out of the response.

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 GET /1/documents/files/ — — —
Expense / income invoice images (artifacts) — POST /1/artifacts/expense/, POST /1/artifacts/income/ (lookup by ids, read-only) — —
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.

6. Pagination

A list endpoint uses one of three styles, and which one does not follow from the /1/ or /2/ prefix — five /1/ lists page with a cursor. The table below is the full picture; the API Reference shows it per endpoint too.

Style Endpoints
Page-numbered — page, page_size, a total count GET /1/expenses/, GET /1/incomes/, GET /1/partners/, GET /1/accounts/
Cursor — page_size and an opaque cursor, no count GET /1/documents/, GET /1/documents/files/, GET /1/tax-codes/, GET /1/monthly-salaries/, GET /1/monthly-taxes/, GET /2/expenses/, GET /2/document-types/, GET /2/accounting/ledger-numbers/, GET /2/accounting/vat-categories/, GET /2/monthly-salaries/, GET /2/monthly-taxes/
Not paginated — one response, everything in it everything else, including GET /1/payments/, GET /2/tags/ and GET /2/companies/ (a bare array), and GET /2/expense-types/ and GET /2/revenue-types/ (one object with the rows under expense_types / revenue_types)

Page-numbered lists

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://test-quick-1-api.test.env.quick.riport.co.hu/1/partners/?page_size=2" \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "count": 128,
  "next": "https://test-quick-1-api.test.env.quick.riport.co.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
    }
  ]
}

Cursor-paginated lists

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://test-quick-1-api.test.env.quick.riport.co.hu/2/accounting/ledger-numbers/?page_size=2" \
  -H "Authorization: Token $QUICK_API_TOKEN"
{
  "next": "https://test-quick-1-api.test.env.quick.riport.co.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" }
  ]
}

No more pages

Both paginated 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://test-quick-1-api.test.env.quick.riport.co.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