_Version: 2.0_

> This document is the LLM-friendly export of the API. It is regenerated on every request from the live OpenAPI spec. Use it as context when asking an AI assistant for help.

# fature.al API v2

Version 2 covers **invoices**, **clients**, **warehouse transfer notes** and the **cash register**.
Only these change shape from v1, so only these have a second address. Everything else (purchase
invoices, products, reports and registration) stays on `v1` and is called from there even by an
integration that sends its invoices with `v2`.

Both versions share the token, the application credentials, the response format and the rate
limits. Concepts, identification and the shared rules are in the v1 documentation, at
[`/docs/api`](/docs/api). If you have not read it, start there.

## Servers

| Environment | URL |
| --- | --- |
| **Live** | `https://fature.al/api/v2` |
| **Sandbox** | `https://demo.fature.al/api/v2` |

## What changes from v1

### Invoice payment is always a list

The invoice sends its payment as `payment_methods`, with one element per method. An invoice paid with
a single method sends a one-element list, so there is only one shape to learn.

```json
"payment_methods": [
  {"type": "BANKNOTE", "amount": 1200},
  {"type": "CARD", "amount": 800}
]
```

| Rule | Limit |
| --- | --- |
| Methods per invoice | 10 |
| Value of an `amount` | Any number the CIS accepts, including 0 and negative |
| Same method twice | Not allowed |
| Sum of `amount` | Equal to the invoice total, with the discount already applied |

The `payment_method`, `company_card` and `vouchers` fields on the invoice are not accepted: the company card
goes in `payment_methods[].company_card` and vouchers in `payment_methods[].vouchers`, inside the
element that requires them. Invoices returned by lists and details carry the same list, so
payment reconciliation always reads a single shape.

An order invoice carries no payment at all: it opens the table, and the summary invoice pays for it.

### The client is split into objects

- **Objects instead of flat fields.** The data comes in `company`, `person`, `address` and
  `contact`, and the `type` field (`company` or `person`) says which of the first two is filled in.
- **The identifier is an object.** `id` with `type` (`nuis`, `vat`, `tax` for companies; `id`,
  `passport`, `social` for persons) and `value`, instead of `nipt` or `document_number`.
- **Values are lowercase.** `company` instead of `COMPANY`, `nuis` instead of `NUIS`.
- **Updates use `PATCH`**, not `PUT`, and `type` does not change: a `type` different from
  the existing one is rejected with `422`.

`GET /clients` in v2 still returns the flat v1 format, because the mobile app was built on it.
Details, creation and updates use the new format, and a client created with `v1` can be read with
`v2` with no extra step.

### The warehouse transfer note requires internalId

`POST /invoice/wtn` requires `internalId`, the note's identifier in your system, whereas in v1 it
is optional. The rest of the note is exactly the same as in v1.

Keep it unique within the company for the year. A second request with the same `internalId` does not issue
a second note; it returns the first note with `200`, so retrying after a timeout is safe.
Read the note later with `GET /invoice/wtn/details/{internalId}`, without storing our `id`.

The note carries no fiscal device: the CIS registers it with the business unit, the operator code and
the software code, so it can be issued even by a user without a TCR.

### Cash in and cash out require internalId

`POST /cash-register/deposit` and `POST /cash-register/withdraw` require `internalId`, the action's identifier in your system. v1 does not know this field. The rest of the request is word for word the v1 one, as are opening, closing and the balance. `GET /cash-register/actions` returns `internalId` for every action, so the list can be matched to the actions in your system.

Keep it unique within the company for the year. A second request with the same `internalId` does not put money in or take it out a second time, it returns the first action with `200`. A request that arrives while the first is still being processed, or an `internalId` used for the other direction, is refused with `409`.

## Servers

- `https://fature.al/api/v2` - Live
- `https://demo.fature.al/api/v2` - Sandbox

## Endpoints

### GET /cash-register/actions

**Summary:** List cash register actions

Returns the cash register history, newest first, filtered by type and date:
cash ins, cash outs, and day openings and closings.

Use it to reconcile the shift and to read the fiscal state of an action that was
saved while the CIS could not be reached.

| `type` | Action |
| --- | --- |
| `MONEY_IN` | Cash in |
| `MONEY_OUT` | Cash out |
| `OPEN_DAY` | Opening the day |
| `CLOSE_DAY` | Closing the day |

- Several types are separated by commas: `type=MONEY_IN,MONEY_OUT` returns only money movements.
- The closing from `POST /cash-register/close-balance` appears as `MONEY_OUT` with the description
  "Mbyllje turni/xhiro ditore" (shift/daily close), not as `CLOSE_DAY`.
- `fromDate` and `toDate` limit by action date, both inclusive.
- Pagination with `limit` and `offset`. `pagination` does not return `total`, so stop when a page
  comes back shorter than `limit`.

`amount` is always positive, in ALL; `action_type` gives the direction. `fiscal_status` and
`fiscal_fic` say whether the action has been fiscalized.

The only change from the v1 list is `internalId`: every action returns it as it was sent with the cash in or cash out, so you can match it to the action in your system. It is `null` for day openings and closings and for actions recorded without one.

**Tags:** Cash register

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `limit` | query | no | How many actions to return per page. |
| `offset` | query | no | Which action to start from, counting from 0. |
| `type` | query | no | Filter by type: `MONEY_IN`, `MONEY_OUT`, `OPEN_DAY` or `CLOSE_DAY`, several types separated by commas. |
| `fromDate` | query | no | Start date, `YYYY-MM-DD`. |
| `toDate` | query | no | End date, `YYYY-MM-DD`. |

**Responses:**

- `200` - The period's actions, newest first, with `pagination`; `items` is empty when there are no actions.
- `401`
- `500` - Unexpected server error.

---

### GET /cash-register/balance

**Summary:** Get the cash register balance

Returns the balance of the fiscal device (TCR) of the token's user, in ALL, together with its
`fiscal_tcr_code`.

Without `toDate`, the current balance is returned. With `toDate`, the balance at the end of that day is returned, for
reconciliation with a past day. The balance is cached for 5 seconds, so a just-recorded
action may show up a moment later.

- The user must have a TCR.

**Tags:** Cash register

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `toDate` | query | no | The date, `YYYY-MM-DD`, up to which the cash register balance is calculated. Without it, the latest balance is returned. |

**Responses:**

- `200` - When the user has no fiscal device, the response is returned with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field.
- `500` - Unexpected server error.

---

### POST /cash-register/close-balance

**Summary:** Close the day on the cash register

Closes the working day on the fiscal device (TCR) of the token's user: takes the whole
balance out of the cash register and returns the amount that went out.

Call it at the end of the shift, without first checking whether the day is open: when it is not,
it is opened automatically with `0` and closed immediately. The request has no body.

- The user must have a TCR.

`balance` is the amount in ALL that went out of the cash register, with two decimals; `0` when the cash register was empty. The cash out appears in
`GET /cash-register/actions` as a `MONEY_OUT` action with the description "Mbyllje turni/xhiro ditore" (shift/daily close); when the cash register was empty, no cash out is recorded. When the CIS cannot be reached, the closing
is saved and is fiscalized automatically later; the response is the same.

**Tags:** Cash register

**Responses:**

- `200` - When the user has no fiscal device or when closing fails, the response is returned with HTTP 200 and `status: false`; the reason is in `message`.
- `401`

---

### POST /cash-register/deposit

**Summary:** Record a cash in

Records money that enters the cash register without being a sale, such as a collection from a client or an opening
float, as a `MONEY_IN` action on the fiscal device (TCR) of the token's user.

For a sale, issue a cash invoice, not a cash in. If the day has not been opened yet, it is opened automatically with
`0`.

- `internalId` is required.
- `amount` is in ALL and must be greater than `0`.
- `id_client`, when sent, must be the `id` of a client of your company.
- The user must have a TCR.

## internalId

This is the only change from [`POST /api/v1/cash-register/deposit`](/docs/api). Keep it unique within the company for the year: a second request with the same `internalId` does not record a second cash in, it returns the first action with `200`. That makes a retry after a timeout safe. An `internalId` already used for a cash out is refused with `409`.

The response carries the action's `id`, `internalId`, its numbers (`transaction_no`, `cash_action_no`) and its fiscal state (`fiscal_status`, `fiscal_uuid`, `fiscal_fic`). When the CIS cannot be reached, the action is saved and fiscalised automatically later: the response comes back complete, with `fiscal_status: UNFISCALIZED`.

**Tags:** Cash register

**Request body content types:** application/json

**Responses:**

- `200` - When the user has no fiscal device, when `id_client` does not belong to your company, or when saving fails, the response is returned with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation, including a missing `internalId`. This response uses the `success` field, not `status`, and `errors` is an object keyed by field.
- `409` - A request with this `internalId` is still being processed, or the `internalId` was used for a cash out. The reason is in `message`.

---

### POST /cash-register/open-balance

**Summary:** Open the day on the cash register

Opens the working day on the fiscal device (TCR) of the token's user with the opening
`balance`, in ALL, and records it as an `OPEN_DAY` action.

Call it at the start of the shift, before the first cash invoice. It is not required: the first
cash invoice, a cash in, a cash out or closing the day open the day automatically with `0`. Use it when the day
starts with money in the cash register and you want the balance to be accurate.

- `balance` is optional; without it, `0` is used. A negative value is rejected.
- The user must have a TCR.

The response returns `balance` rounded to two decimals, as it was saved. When the CIS cannot be reached, the opening is saved and
is fiscalized automatically later; the response is the same.

**Tags:** Cash register

**Request body content types:** application/json

**Responses:**

- `200` - When the user has no fiscal device, when `balance` is negative, or when saving fails, the response is returned with HTTP 200 and `status: false`; the reason is in `message`.
- `401`

---

### POST /cash-register/withdraw

**Summary:** Record a cash out

Records money that leaves the cash register, such as a supplier payment or a withdrawal during the day, as a
`MONEY_OUT` action on the fiscal device (TCR) of the token's user.

To withdraw the whole balance at the end of the shift, use `POST /cash-register/close-balance`.
If the day has not been opened yet, it is opened automatically with `0`.

- `internalId` is required.
- `amount` is in ALL and must be greater than `0`; the sign comes from the endpoint, not from the number.
- The register must hold at least `amount`; when it does not, the cash out is refused.
- `id_client`, when sent, must be the `id` of a client of your company.
- The user must have a TCR.

## internalId

This is the only change from [`POST /api/v1/cash-register/withdraw`](/docs/api). Keep it unique within the company for the year: a second request with the same `internalId` does not take money out a second time, it returns the first action with `200`. An `internalId` already used for a cash in is refused with `409`.

The response has the same format as the cash in. When the CIS cannot be reached, the action is saved and fiscalised automatically later: the response comes back complete, with `fiscal_status: UNFISCALIZED`.

**Tags:** Cash register

**Request body content types:** application/json

**Responses:**

- `200` - When the user has no fiscal device, when the cash register does not hold as much money as `amount`, when `id_client` does not belong to your company, or when saving fails, the response is returned with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation, including a missing `internalId`. This response uses the `success` field, not `status`, and `errors` is an object keyed by field.
- `409` - A request with this `internalId` is still being processed, or the `internalId` was used for a cash in. The reason is in `message`.

---

### GET /clients

**Summary:** List clients

Returns the company's clients, newest first, with text search. Each client comes
with the same fields as in `GET /clients/{id}/details`.

Use it to find the `id` of a saved client, or to sync your
records with those in fature.al.

- Pagination me `limit` dhe `offset`. `limit` kufizohet në `100`; një vlerë më e madhe ulet pa
  gabim. `pagination` kthen `total`, ndaj vazhdoni me `offset += limit` derisa `offset` të
  arrijë `total`.
- `query` kërkon me përputhje të pjesshme në emrin e kompanisë, emrin, mbiemrin, atësinë,
  NIPT-in, numrin e dokumentit, adresën, email-in, telefonin dhe numrin e klientit, njësoj si
  kërkimi i klientëve në fature.al.
- Një kërkesë e dytë nga i njëjti token, ndërsa e para nuk ka mbaruar, refuzohet menjëherë me
  `429`.

`company_type` is returned `null` for a person, and `first_name` and `surname` for a company;
`name` always holds the full name.

**Tags:** Clients

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `limit` | query | no | How many clients to return per page, up to 100. |
| `offset` | query | no | Which client to start from, counting from 0. |
| `query` | query | no | Kërkim në emrin e kompanisë, emrin, mbiemrin, atësinë, NIPT-in, numrin e dokumentit, adresën, email-in, telefonin ose numrin e klientit. |

**Responses:**

- `200` - The page's clients in `items`, with `pagination`; `items` is empty when no client matches.
- `401`
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.

---

### POST /clients

**Summary:** Create a client

Creates a client in the v2 format. The body is split by the `type` field, which accepts `company` or
`person`, and only the object matching the type is filled in.

| `type` | Object | Required fields |
| --- | --- | --- |
| `company` | `company` | `name`, `id` |
| `person` | `person` | `first_name`, `last_name`, `id` |

## Identifier

Always sent as an `id` object with `type` and `value`, and the allowed values depend on the type:

- company: `nuis`, `vat`, `tax`;
- person: `id`, `passport`, `social`.

## Shared objects

`address` (`line`, `city`, three-letter `country`) and `contact` (`phone`, `email`) are
optional and apply to both types. `company.category` accepts `business`, `bank` or
`exchange`.

Store the returned `id`: with it the client is found and updated again. A second request
from the same token while the first has not finished is rejected immediately with `429`.

**Tags:** Clients

**Request body content types:** application/json

**Responses:**

- `200` - The created client in `data.client`, with its `id` in fature.al; store it for invoices and updates.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `409` - A client with the same data already exists.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.

---

### GET /clients/{id}

**Summary:** Get a client by id

Returns the client in the v2 format: `company` or `person` depending on `type`, plus `address`,
`contact`, `customer_number` and `verified`.

It is the same client that `v1` returns, only structured differently. A client created with
`v1` can be read here with no extra step.

**Tags:** Clients

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The client id. |

**Responses:**

- `200` - The client in `data.client`, with `company` or `person` filled in according to its type.
- `401`
- `404` - The client was not found, or does not belong to your company.
- `500` - Unexpected server error.

---

### PATCH /clients/{id}

**Summary:** Update a client

Updates a client with `PATCH` and the same body as creation, with one restriction: `type`
does not change. A company does not become a person, nor the other way around; a `type` different from the
existing one is rejected with `422`.

A second request from the same token while the first has not finished is rejected immediately with
`429`.

**Tags:** Clients

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The client id. |

**Request body content types:** application/json

**Responses:**

- `200` - The updated client in `data.client`, in full, not just the fields sent.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field. Only a `type` different from the existing one is returned in the standard error format.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `404` - The client was not found, or does not belong to your company.
- `409` - A client with the same data already exists.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.

---

### GET /invoice

**Summary:** List invoices

The same list as in `v1`, where each invoice carries `payment_methods` instead of `payment_method`.
Filters and pagination are those of [`GET /api/v1/invoice`](/docs/api).

**Tags:** Invoices

**Responses:**

- `200` - The period's invoices in `items`, newest first, with `pagination`; `items` is empty when there are no invoices.
- `401`

---

### POST /invoice/bulk-noncash

**Summary:** Issue bulk non-cash invoices

The same bulk as in `v1`, where each invoice in `invoices` carries its own payment as a
`payment_methods` list. The response is an object with one entry per `internalId`, and the HTTP
status stays `200` even when some invoice fails: check `status` inside each entry and
retry only the ones that failed.

**Tags:** Invoices

**Responses:**

- `200` - An object with one entry per `internalId`. An invoice that fails carries the error format inside its own entry, and the response status stays 200.
- `401`
- `403` - The subscription has expired, or the action is not allowed for this account.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.

---

### POST /invoice/cancel-by-internal-id/{internalId}

**Summary:** Cancel an invoice by internalId

The same cancellation as in `v1`, finding the invoice by the `internalId` you sent when you issued it:
without a body the whole invoice is canceled; with `lines`, only part of it, which is possible **only for
cash invoices**. The full rules are in
[`POST /api/v1/invoice/cancel-by-internal-id/{internalId}`](/docs/api).

The response is the corrective document, with its own `iic` and `fic` and with `payment_methods`
alongside. When the CIS cannot be reached, the cancellation is saved with `fic: null` and is fiscalized automatically
later.

**Tags:** Invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `internalId` | path | yes | Your identifier for the invoice, sent when it was issued. |

**Request body content types:** application/json

**Responses:**

- `200` - The corrective document, with its own `iic` and `fic`; `fic` is `null` when the CIS could not be reached and fiscalization will complete automatically.
- `401`
- `422` - The invoice cannot be canceled in its current state, or the lines sent do not match the original. `errors` says why.
- `404` - The invoice was not found, or does not belong to your company.
- `500` - Unexpected server error, including when the cancellation could not be registered with the CIS.
- `429` - The cancellation limit was reached, or another cancellation of the same type is still in progress. When the cancellation limit is hit, the response carries only `message` and the `Retry-After` header.

---

### POST /invoice/cancel/{id}

**Summary:** Cancel an invoice by id

The same cancellation as in `v1`: without a body the whole invoice is canceled; with `lines`, only part of
it, which is possible **only for cash invoices**. The full rules are in
[`POST /api/v1/invoice/cancel/{id}`](/docs/api).

The response is the corrective document, with its own `iic` and `fic` and with `payment_methods`
alongside. When the CIS cannot be reached, the cancellation is saved with `fic: null` and is fiscalized automatically
later. Cancellations have their own rate limit: 30 per minute and 600 per hour per token.

**Tags:** Invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The invoice's id in fature.al. |

**Request body content types:** application/json

**Responses:**

- `200` - The corrective document, with its own `iic` and `fic`; `fic` is `null` when the CIS could not be reached and fiscalization will complete automatically.
- `401`
- `422` - The invoice cannot be canceled in its current state, or the lines sent do not match the original. `errors` says why.
- `404` - The invoice was not found, or does not belong to your company.
- `500` - Unexpected server error, including when the cancellation could not be registered with the CIS.
- `429` - The cancellation limit was reached, or another cancellation of the same type is still in progress. When the cancellation limit is hit, the response carries only `message` and the `Retry-After` header.

---

### POST /invoice/cash

**Summary:** Issue a cash invoice

The same invoice as in `v1`, with the payment as a list. For the buyer, lines, discounts and
self-invoicing, the rules of [`POST /api/v1/invoice/cash`](/docs/api) apply.

## Payment

`payment_methods` carries one element per method and is required even when the invoice
is paid entirely with one method.

```json
{
  "internalId": "CASH-2026-0101",
  "lines": [
    {
      "product_name": "Coffee",
      "product_code": "KAF-001",
      "unit": "piece",
      "quantity": 10,
      "price": 200,
      "total": 2000,
      "vat": 20
    }
  ],
  "payment_methods": [
    {"type": "BANKNOTE", "amount": 1200},
    {"type": "CARD", "amount": 800}
  ]
}
```

| Rule | Limit |
| --- | --- |
| Methods per invoice | 10 |
| Same method twice | Not allowed |
| Sum of `amount` | Equal to the invoice total, with the discount already applied |

Only the `COMPANY` element carries `company_card`, and only the `SVOUCHER` element carries `vouchers`.
A list that does not add up to the total is rejected with `400`, because the sum is checked only after
the discount on the invoice has been applied.

Self-invoicing is paid from the cash register, as in `v1`: when the cash register does not cover the total, the invoice is not saved and
the response comes with HTTP 200 and `status: false`.

## Selling vouchers

When the goods sold are single-purpose vouchers themselves, the line carries `vouchers_sold`:
which goods or services the voucher is for (`purpose`), the expiry date, the nominal value of one
voucher and the serial numbers, from 1 to 1000. They go to CIS as the line's `VS` element.

The name sent to CIS stays `product_name`, up to 50 characters. fature.al builds the full
description required by Instruction 16/2020 (article 8, point 4) from `purpose` and the numbers, and
writes it under the item name on the printed invoice and in the e-invoice. The description is legal text and stays in Albanian; for the example below it reads: Tollon me një qëllim për veshje dhe këpucë me numrat serialë 12-2026-K51501011M, 13-2026-K51501011M.

```json
{
  "internalId": "CASH-2026-0102",
  "lines": [
    {
      "product_name": "Gift voucher 1000 L",
      "product_code": "TOL-1000",
      "unit": "piece",
      "quantity": 2,
      "price": 1000,
      "total": 2000,
      "vat": 20,
      "vouchers_sold": {
        "purpose": "clothing and shoes",
        "expires_at": "2027-12-31",
        "nominal_value": 1000,
        "numbers": ["12-2026-K51501011M", "13-2026-K51501011M"]
      }
    }
  ],
  "payment_methods": [
    {"type": "BANKNOTE", "amount": 2000}
  ]
}
```

| Rule | Limit |
| --- | --- |
| Serial numbers on one line | 1 to 1000 |
| The same serial number | Once on the whole invoice |
| `purpose` | Required, up to 255 characters |
| Partial cancellation | A voucher line is returned only whole |

Cancellation returns the line with the same vouchers. The invoice details return this same object
in `lines[].vouchers_sold`; the response example is on `GET /invoice/{id}/details`.

**Tags:** Invoices

**Request body content types:** application/json

**Responses:**

- `200` - When the user has no fiscal device, when the cash register does not cover a self-invoice, or when the invoice is rejected after validation by the CIS or while saving, the response comes back with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field. Only a missing `internalId` comes back in the standard error format.
- `400` - Incomplete or invalid data for fiscalization. `errors` says exactly what needs to be filled in. The invoice was not saved; retry with the same `internalId`.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `409` - Një kërkesë me këtë `internalId` është ende në proces. Prisni pak dhe riprovoni, ose lexoni gjendjen me `GET /invoice/details/{internalId}`.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.
- `503` - The fiscalization service could not be reached and the invoice was not saved. Retry with the same `internalId`.

---

### GET /invoice/details/{internalId}

**Summary:** Get an invoice by internalId

The same issuing result as in `v1`, with `payment_methods` alongside. Use it to read
the state after a timeout or after a `409`.

Pranohet edhe `POST` në të njëjtën adresë, për integrimet që e thërrasin ashtu që nga fillimi.

**Tags:** Invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `internalId` | path | yes | Your identifier for the invoice, sent when it was issued. |

**Responses:**

- `200` - The issuing result of the invoice with this `internalId`, the same one the issue call returned, with `payment_methods`.
- `401`

---

### POST /invoice/e-invoice

**Summary:** Issue an e-invoice

The same e-invoice as in `v1`, with the payment as a `payment_methods` list. For the buyer, the document
type (`doc_type` and `process`) and correction notes, the rules of
[`POST /api/v1/invoice/e-invoice`](/docs/api) apply.

**Tags:** Invoices

**Request body content types:** application/json

**Responses:**

- `200` - When the invoice is rejected after validation, by the CIS or while saving, the response comes back with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field. Only a missing `internalId` comes back in the standard error format.
- `400` - Incomplete or invalid data for fiscalization. `errors` says exactly what needs to be filled in. The invoice was not saved; retry with the same `internalId`.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `409` - Një kërkesë me këtë `internalId` është ende në proces. Prisni pak dhe riprovoni, ose lexoni gjendjen me `GET /invoice/details/{internalId}`.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.
- `503` - The fiscalization service could not be reached and the invoice was not saved. Retry with the same `internalId`.

---

### POST /invoice/noncash

**Summary:** Issue a non-cash invoice

The same invoice as in `v1`, with the payment as a `payment_methods` list. For the buyer, the bank
account, correction notes and discounts, the rules of
[`POST /api/v1/invoice/noncash`](/docs/api) apply.

The allowed methods are `ACCOUNT`, `COMPENSATION`, `FACTORING`, `KIND`, `OTHER`, `TRANSFER`
and `WAIVER`; none of them carries a company card or vouchers. When the method paying the largest
share is `ACCOUNT`, the invoice requires the bank account, as in `v1`.

**Tags:** Invoices

**Request body content types:** application/json

**Responses:**

- `200` - When the invoice is rejected after validation, by the CIS or while saving, the response comes back with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field. Only a missing `internalId` comes back in the standard error format.
- `400` - Incomplete or invalid data for fiscalization. `errors` says exactly what needs to be filled in. The invoice was not saved; retry with the same `internalId`.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `409` - Një kërkesë me këtë `internalId` është ende në proces. Prisni pak dhe riprovoni, ose lexoni gjendjen me `GET /invoice/details/{internalId}`.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.
- `503` - The fiscalization service could not be reached and the invoice was not saved. Retry with the same `internalId`.

---

### POST /invoice/order

**Summary:** Issue an order invoice

The same order as in `v1`. The order opens the table and pays nothing, so it carries neither
`payment_method` nor `payment_methods`: payment comes later, with the summary invoice that
closes the table. The rules are those of [`POST /api/v1/invoice/order`](/docs/api).

**Tags:** Invoices

**Request body content types:** application/json

**Responses:**

- `200` - When the user has no fiscal device, or when the invoice is rejected after validation by the CIS or while saving, the response comes back with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field. Only a missing `internalId` comes back in the standard error format.
- `400` - Incomplete or invalid data for fiscalization. `errors` says exactly what needs to be filled in. The invoice was not saved; retry with the same `internalId`.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `409` - Një kërkesë me këtë `internalId` është ende në proces. Prisni pak dhe riprovoni, ose lexoni gjendjen me `GET /invoice/details/{internalId}`.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.
- `503` - The fiscalization service could not be reached and the invoice was not saved. Retry with the same `internalId`.

---

### GET /invoice/print-eic/{eic}

**Summary:** Download the e-invoice PDF by EIC

Returns the official e-invoice PDF from the e-invoicing platform, by the EIC code that issuing
returned to you.

Every failure comes back with its own status and a JSON `message`, never as a PDF:

| Status | When |
| --- | --- |
| `404` | The e-invoicing platform answered, but holds no document with this EIC. |
| `422` | The e-invoicing platform refused the request; `message` says why. |
| `503` | The e-invoicing platform is not responding. Try again after `Retry-After` seconds. |

When the platform is not responding and the EIC belongs to one of your e-invoices in fature.al,
you get the fallback PDF instead of `503`, the same as when downloading by id. The
`X-Document-Source` header says which one you got: `official` from the platform, `stored` the
copy kept from an earlier download, `local` the document generated by fature.al.

**Tags:** Invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `eic` | path | yes | The e-invoice EIC code, a UUID. |

**Responses:**

- `500`
- `200` - The e-invoice PDF. `X-Document-Source` says whether it is the official one (`official`), the stored copy (`stored`) or the fature.al document (`local`).
- `503` - The e-invoicing platform is not responding. Try again after `Retry-After` seconds.
- `401`
- `404` - The e-invoicing platform holds no document with this EIC.
- `422` - The e-invoicing platform refused the request.

---

### GET /invoice/print/{id}

**Summary:** Download the invoice document

Returns the invoice document to give to the buyer or to print. Without parameters, the default
format for the type is returned: a thermal receipt for cash invoices (58 or 80 mm, per the
user's configuration), an A4 document for non-cash invoices and estimates, and the official PDF
from the e-invoicing platform for e-invoices.

With the `format` parameter you get the same versions the fature.al dashboard offers:

| `format` | Invoice type | Result | Content-Type |
| --- | --- | --- | --- |
| `a4` | Cash | A4 invoice document | application/pdf |
| `thermal` | Non-cash | Thermal receipt | application/pdf |
| `v2` | Non-cash, estimate | A4 document, wide version | application/pdf |
| `receipt` | Estimate | Thermal receipt | application/pdf |
| `local` | E-invoice | A4 document generated by fature.al, without requesting the official PDF | application/pdf |
| `local-thermal` | E-invoice | Thermal receipt generated by fature.al | application/pdf |
| `html` | Cash | The thermal receipt as HTML, to send to the printer yourself | text/html |
| `json` | All | The document data as JSON, to format the invoice yourself | application/json |

`lang` (`en`, `it`, `de`) translates the A4 document of non-cash invoices, estimates and
e-invoices, and combines with `format`. Without it, the document is returned in Albanian. Thermal receipts and
cash invoices always stay in Albanian, because they are fiscal documents.

For cash invoices, `copy_only=1` returns the copy of the receipt instead of the original.

A `format` that does not apply to the invoice type does not return an error: the default format of
that type is returned.

## E-invoices when the platform is not responding

The official PDF comes from the e-invoicing platform. When it is not responding, no error is
returned: you get the copy kept from an earlier download or, when there is none, the document
generated by fature.al. The `X-Document-Source` header says which one you got: `official`,
`stored` or `local`.

**Tags:** Invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The invoice's id in fature.al. |
| `copy_only` | query | no | Cash invoices only: returns the copy of the receipt instead of the original. |
| `format` | query | no | The document version per the table above: `a4`, `thermal`, `v2`, `receipt`, `local`, `local-thermal`, `html`, `json`. |
| `lang` | query | no | The language of the A4 document: `en`, `it` or `de`. Without it, the document is returned in Albanian. |

**Responses:**

- `500` - The document could not be generated. The problem is reported automatically.
- `200` - The content type depends on `format`: PDF by default, `text/html` with `format=html`, and the invoice document as JSON with `format=json`, ready for you to format yourself.
- `401`
- `404` - The invoice was not found or does not belong to your company, or the e-invoicing platform holds no document for this e-invoice.
- `422` - The e-invoicing platform refused the request for the PDF; `message` says why.

---

### POST /invoice/summary

**Summary:** Issue a summary invoice

Settles the payment of one or more order invoices with a single document, as in `v1`, with the
payment as a `payment_methods` list. A table paid half in cash and half by
card sends a two-element list; one paid entirely by card, a one-element list.
Only `BANKNOTE` and `CARD` are allowed. The rules for `order_invoices` and the discount are those of
[`POST /api/v1/invoice/summary`](/docs/api).

**Tags:** Invoices

**Request body content types:** application/json

**Responses:**

- `200` - When the user has no fiscal device, or when the invoice is rejected after validation by the CIS or while saving, the response comes back with HTTP 200 and `status: false`; the reason is in `message`.
- `401`
- `422` - The data failed validation. This response uses the `success` field, not `status`, and `errors` is an object keyed by field. Only a missing `internalId` comes back in the standard error format.
- `400` - An order was not found, is not an order invoice, or is already closed; or the data is incomplete for fiscalization. `errors` says exactly which. The invoice was not saved; retry with the same `internalId`.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `409` - Një kërkesë me këtë `internalId` është ende në proces. Prisni pak dhe riprovoni, ose lexoni gjendjen me `GET /invoice/details/{internalId}`.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.
- `503` - The fiscalization service could not be reached and the invoice was not saved. Retry with the same `internalId`.

---

### POST /invoice/wtn

**Summary:** Issue a warehouse transfer note

Issues a warehouse transfer note (WTN) and fiscalizes it with the CIS. The note is not a sale: it accompanies
goods moving from one point to another, and the law requires it even when nothing is sold.

Lines are sent as `invoice_lines`, not `lines` as on invoices, and each line has
`product_name`, `product_code`, `unit` and `quantity`.

## Required fields

- `internalId`, the note's identifier in your system;
- `vehPlates`, the vehicle's license plate;
- `valueOfGoods`, the value of the goods in transit;
- `invoice_lines`, at least one line.

## internalId

This is the only change from [`POST /api/v1/invoice/wtn`](/docs/api), where the field is
optional. Keep it unique within the company for the year: a second request with the same
`internalId` does not issue a second note; it returns the first note with `200`. This makes retrying after a
timeout safe, and the note can be read later with
`GET /invoice/wtn/details/{internalId}`.

## Allowed values

| Field | Values | Default |
| --- | --- | --- |
| `type` | `WTN`, `SALE` | `WTN` |
| `transaction` | `TRANSFER`, `EXAMINATION`, `SALES`, `DOOR` | `TRANSFER` |
| `vehOwnership` | `OWNER`, `THIRDPARTY` | `OWNER` |
| `startPoint`, `destinPoint` | `ANOTHER`, `CUSTOMS`, `EXHIBITION`, `OTHER`, `SALE`, `STORE`, `WAREHOUSE` | `WAREHOUSE`, `STORE` |
| `carrier_id_type` | `NUIS`, `ID` | `NUIS` |

The departure and arrival addresses (`startCity`, `startAddr`, `destinCity`, `destinAddr`) and
the carrier (`carrier_name`, `carrier_id_num`, `carrier_town`, `carrier_address`) are taken from
your business unit and company when not sent. `startDateTime` and `destinDateTime` take the issue
time when missing.

## After issuing

The response carries `id`, `internalId`, `number`, `iic`, `fic`, `fiscalStatus`, `verifyURL` and
`print`. When the CIS cannot be reached, the note is saved unfiscalized, with `fic: null` and
`fiscalStatus: UNFISCALIZED`; fiscalization is done later from the fature.al dashboard, and you read the state
in `fiscal.status` with `GET /invoice/wtn/details/{internalId}`. When the CIS rejects the
note, it is not saved and the response is `500` with the reason in `message`.

A second request from the same token while the first is still running is rejected immediately with
`429`. The slot frees up as soon as the response is returned, or after 30 seconds if the request was interrupted.

**Tags:** Warehouse transfer notes

**Request body content types:** application/json

**Responses:**

- `200` - When the CIS cannot be reached, the note is saved unfiscalized: the response carries `invoice` with `fic: null` and `fiscalStatus: UNFISCALIZED`, and fiscalization happens later.
- `401`
- `422` - The data failed validation, including a missing `internalId`. This response uses the `success` field, not `status`, and `errors` is an object keyed by field.
- `403` - The subscription has expired, or the action is not allowed for this account.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error, or the CIS rejected the note; the reason is in `message`. The note was not saved.

---

### GET /invoice/wtn

**Summary:** List warehouse transfer notes

The same list as [`GET /api/v1/invoice/wtn`](/docs/api): the company's notes, newest
first, filtered by creation date. Each note comes in full, with `origin`,
`destination`, `carrier`, `fiscal` and `lines`, so the lines need no second
call, and with `internalId` as it was sent.

- Pagination with `limit` and `offset`. `pagination` does not return `total`, so stop when a page
  comes back shorter than `limit`.
- A second request from the same token, while the first has not finished, is rejected immediately with
  `429`. The slot is released as soon as the response returns, or after 30 seconds if the request was interrupted.

**Tags:** Warehouse transfer notes

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `limit` | query | no | How many notes to return per page. |
| `offset` | query | no | Which note to start from, counting from 0. |
| `fromDate` | query | no | The start date, `YYYY-MM-DD`, by creation date. |
| `toDate` | query | no | End date, `YYYY-MM-DD`. |

**Responses:**

- `200` - The period's notes, with `pagination`; `items` is empty when there are no notes.
- `401`
- `422` - The dates are not in `YYYY-MM-DD` format.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.

---

### GET /invoice/wtn/details/{internalId}

**Summary:** Get a warehouse transfer note by internalId

Returns the same object as `GET /invoice/wtn/{id}/details`, but by the identifier you
sent in `internalId` when the note was issued, without storing our `id`. Since in v2 `internalId`
is required, every note issued by this version can be read this way.

When the same `internalId` has been used in more than one year, the most recent note is returned.

**Tags:** Warehouse transfer notes

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `internalId` | path | yes | Your identifier for the note, sent when it was issued. |

**Responses:**

- `200` - The full note in `invoice`, the same one `GET /invoice/wtn/{id}/details` returns.
- `401`
- `404` - No note with this `internalId` in your unit.
- `422` - `internalId` is empty.
- `500` - Unexpected server error.

---

### GET /invoice/wtn/print/{id}

**Summary:** Get the warehouse transfer note for printing

Returns the note's data as document-ready JSON, not as a PDF: the company with its logo,
the carrier, departure and arrival, and in `invoice` the number, fiscal codes, lines,
`amounts` and `verifyUrl`.

Use it when you format the document yourself, in your own printing system. The address of this
endpoint comes in the `print` field of the list and of the details.

**Tags:** Warehouse transfer notes

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The id of the warehouse transfer note in fature.al. |

**Responses:**

- `200` - The note document as JSON, ready for you to format: the company with its logo, the carrier, departure, arrival and `invoice` with the lines and fiscal codes.
- `401`
- `404` - The note was not found, or it does not belong to your company.

---

### GET /invoice/wtn/{id}/details

**Summary:** Get a warehouse transfer note by id

Returns the full note by its `id` in fature.al: the vehicle, the departure and
arrival points, the carrier, the fiscal codes and the goods lines. It is the same object the
list returns.

**Tags:** Warehouse transfer notes

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The id of the warehouse transfer note in fature.al. |

**Responses:**

- `200` - The full note in `invoice`: departure and arrival points, the carrier, the fiscal codes and the goods lines.
- `401`
- `404` - The note was not found, or does not belong to your unit.
- `500` - Unexpected server error.

---

### GET /invoice/{id}/details

**Summary:** Get an invoice by id

The same details as in `v1`, where the payment comes as a `payment_methods` list. When the invoice is not found,
`data.invoice` is `null` with status `200`.

Every line also carries `vouchers_sold`: the single-purpose vouchers it sells, or `null` for an
ordinary line. The line from the voucher sale example on `POST /invoice/cash` comes back like this
in `data.invoice.lines`:

```json
{
  "name": "Gift voucher 1000 L",
  "product_code": "TOL-1000",
  "unit": "piece",
  "unit_code": null,
  "quantity": 2,
  "unitPrice": 1000,
  "vat_rate": 20,
  "vat_exempt_type": null,
  "margin_cost": null,
  "discount_rate": 0,
  "discount": 0,
  "amount_net": 1666.6666666667,
  "amount_vat": 333.3333333333,
  "total": 2000,
  "vouchers_sold": {
    "purpose": "clothing and shoes",
    "expires_at": "2027-12-31",
    "nominal_value": 1000,
    "numbers": ["12-2026-K51501011M", "13-2026-K51501011M"]
  }
}
```

**Tags:** Invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `id` | path | yes | The invoice's id in fature.al. |

**Responses:**

- `200` - The full invoice in `data.invoice`, or `null` when it is not found.
- `401`

---

### GET /purchase-invoices

**Summary:** List purchase invoices

Returns the invoices that suppliers have issued to your company, read from the CIS. The supplier
does not send you anything: the documents are fetched from the central fiscalization system, each with the
full data, the seller, the buyer, the items, VAT and payment methods.

Use it to reconcile purchases and deductible VAT without any manual step.

- Pagination uses `page`, starting at `1`, not `limit` and `offset`, because the list comes from
  the CIS and not from fature.al. Repeat with `page + 1` as long as `pagination.hasMore` is `true`.
- Do not stop at an empty `items`. A document whose details cannot be reached is dropped from
  `items` without an error, so a page can come back empty even when more pages follow it.
- The period includes both days, from 00:00:00 on `fromDate` to 23:59:59 on `toDate`,
  in Albanian time.
- A missing date defaults to today, so without either only today is read. For a past period
  send both: a past `toDate` on its own starts the period today, after it ends, and returns
  none of that period's invoices.
- `fic` searches within the period, not the whole history. Send it together with a `fromDate` and
  `toDate` that include the invoice's date, otherwise only today's invoices are searched.
- One page costs one CIS call for the list and one for each invoice on it. The details of each
  invoice are cached for one hour, and the endpoint has the fiscal rate limit.

Store `iic` and `fic`: they let you find the invoice again, and `fic` also serves as a filter here.

**Tags:** Purchase invoices

**Parameters:**

| Name | In | Required | Description |
|------|----|----------|-------------|
| `page` | query | no | The page of the list from the CIS, starting at 1. A value below 1 or not a number is read as 1. Repeat with `page + 1` as long as `pagination.hasMore` is `true`. |
| `fromDate` | query | no | Start date, `YYYY-MM-DD`, inclusive. Defaults to today. |
| `toDate` | query | no | End date, `YYYY-MM-DD`, inclusive. Defaults to today. |
| `fic` | query | no | Return only the invoice with this FIC, if it falls within the period. |

**Responses:**

- `200` - The page's purchase invoices in `items`, each one complete, with `pagination`.
- `401` - The token is missing or invalid, or the user does not have the purchase invoices right.
- `422` - The dates are not in `YYYY-MM-DD` format.
- `429` - Rate limit reached. From the global limit, the response carries only `message` and the `Retry-After` header; from the endpoint's limit, it has the standard error format.
- `500` - Unexpected server error.
- `502` - The CIS responded with an error; the reason is in `message`.
- `503` - The fiscalization service could not be reached. Try again later. `message` comes back empty; the reason is in `errors`.

---

## Full OpenAPI specification

For complete schemas, examples and request/response bodies, fetch the JSON spec:

```
https://demo.fature.al/docs/api/v2/en.json
```
