# @lokavaluto/lokapi

> Lokavaluto API

Latest version **0.1.1-alpha.202607211613** (published 2026-07-21) · MIT license · 0 weekly downloads

## Install

```sh
npm install @lokavaluto/lokapi
pnpm add @lokavaluto/lokapi
yarn add @lokavaluto/lokapi
bun add @lokavaluto/lokapi
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads; no esm support; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.1.1-alpha.202607211613 |
| Published | 2026-07-21 |
| First published | 2021-11-24 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 4 |
| Unpacked size | 413.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 2 |
| Author | Valentin LAB |
| Maintainers | vaab |

## Links

- npm: https://www.npmjs.com/package/@lokavaluto/lokapi
- Repository: https://github.com/Lokavaluto/lokapi
- Homepage: https://github.com/Lokavaluto/lokapi#readme
- Issues: https://github.com/Lokavaluto/lokapi/issues
- npm.io page: https://npm.io/package/@lokavaluto/lokapi

## Dependencies (4)

- [qs](https://npm.io/package/qs.md) ^6.10.1
- [tslib](https://npm.io/package/tslib.md) ^1.14.1
- [@0k/cache](https://npm.io/package/@0k/cache.md) ^0.3.0
- [@0k/types-request](https://npm.io/package/@0k/types-request.md) ^1.0.0

## Recent versions

- 0.1.1-alpha.202607211613 (latest) — 2026-07-21
- 0.1.1-alpha.202605270647 — 2026-05-27
- 0.1.1-alpha.202604170535 — 2026-04-17
- 0.1.1-alpha.202604141748 — 2026-04-16
- 0.1.1-alpha.202604141406 — 2026-04-14
- 0.1.1-alpha.202603170757 — 2026-03-17
- 0.1.1-alpha.202603170420 — 2026-03-17
- 0.1.1-alpha.202603060850 — 2026-03-09
- 0.1.1-alpha.202602101110 — 2026-02-10
- 0.1.1-alpha.202512081648 — 2025-12-08
- 0.1.1-alpha.202511191437 — 2025-11-19
- 0.1.1-alpha.202508201502 — 2025-08-20
- 0.1.1-alpha.202505152117 — 2025-05-25
- 0.1.1-alpha.202505111810 — 2025-05-11
- 0.1.1-alpha.202505111803 — 2025-05-11
- … 44 more at https://npm.io/package/@lokavaluto/lokapi/versions

## README

LokAPI is a javascript library intended to be used in mobile
applications or web application to abstract all logics with
lokavaluto's server.


# Adding `lokapi` to your project

From the root of your project:

```sh
npm install --save @lokavaluto/lokapi
```

Or better, as `@lokavaluto/lokapi` is still in early release,

```sh
npm install --save Lokavaluto/lokapi#master
```

To be sure to get the latest version, relaunch this last command
whenever you want to update.

# Setting up `lokapi`

## Subclassing main LokAPIAbstract

Please note that an important part of the following is already
packaged in package `@lokavaluto/lokapi-browser` (for node and browser
support), and thus allow you to quick-start your projects with less
code. Here's a link to the project page of
[lokapi-browser](https://github.com/Lokavaluto/lokapi-browser) and how to use it, with an example.

Lokapi will require a way to make HTTP request, access to a permanent
store, or various other tasks. Depending on your platform or
environment, you might have different way to implement these.

To inject these exchangeable dependency, you are invited to subclass
LokAPIAbstract and define these objects:

BackendFactories :: A mapping of currency backends loaded
httpRequest :: an implementation of `HTTPRequest`
base64Encode :: a function to encode a string to base64
persistentStore :: an object implementing this interface:

```typescript
    export interface IPersistentStore {
        get(key: string, defaultValue?: string): string
        set(key: string, value: string): void
        del(key: string): void
    }
```

(optional) requestLogin :: a function to trigger a login screen when
automatic authorization fails or when no automatic autorization data
exists. This will be triggered only if a protected request is made
on the administration backend side.
(optional) requestLocalPassword :: a function for backends to
trigger a request to the user for a password that is meant
to be kept locally on the device. This is typically used before
payment with some backends (to see an example see package
`lokapi-backend-comchain`), or administrative tasks. And takes
usually the form of a popup.

## Instanciating lokapi

On instantiation time, `LokAPI` class will require you to provide a
`host` as first argument (a string, describing the URL to reach the
administrative backend), and can receive a database name as second
argument. If you don't specify the database name, it'll be the default
one selected by the server.

### Example for node


Using node's core `https` as HTTP requestion implementation:


```typescript
import http from "http"
import https from "https"

import { VueCookieNext } from 'vue-cookie-next'
import { LokAPIAbstract, e as LokAPIExc, t as LokAPIType } from "@lokavaluto/lokapi"

import cyclos from '@lokavaluto/lokapi-backend-cyclos'


class cookieStore implements LokAPIType.IPersistentStore {
  constructor() {
    VueCookieNext.config({ expire: '7d' })
  }
  get(key: string, defaultValue?: string): string {
    let value = VueCookieNext.getCookie("lokapi_" + key)
    // XXXvlab: CookieViewNext is JSON.parsing string sometimes and this
    // breaks interface !
    if (value !== null && typeof value === "object") {
        return JSON.stringify(value)
    }
    return value
  }
  set(key: string, value: string): void {
    VueCookieNext.setCookie("lokapi_" + key, value)
  }
  del(key: string): void {
    VueCookieNext.removeCookie("lokapi_" + key)
  }
}

const requesters: any = { http, https }

class NodeLokAPI extends LokApiAbstract {

    BackendFactories = {
        cyclos,
    }

    httpRequest = (opts: LokAPIType.coreHttpOpts) => {
        const httpsOpts = {
            host: opts.host,
            path: opts.path,
            method: opts.method,
            ...opts.headers && { headers: opts.headers },
            ...opts.port && { port: opts.port }
        }
        const requester = requesters[opts.protocol]
        if (!requester) {
            throw new Error(`Protocol ${opts.protocol} unsupported by this implementation`)
        }
        return new Promise((resolve, reject) => {
            let req = requester.request(httpsOpts, (res: any) => {
                const { statusCode } = res

                let rawData = ''

                res.on('data', (chunk: any) => { rawData += chunk })
                res.on('end', () => {
                    if (!statusCode || statusCode.toString().slice(0, 1) !== '2') {
                        res.resume();
                        reject(new LokAPIExc.HttpError(statusCode, res.statusMessage, rawData, res))
                        return
                    } else {
                        if (opts.responseHeaders) {
                            for (const header in res.headers) {
                                opts.responseHeaders[header] = res.headers[header]
                            }
                        }
                        resolve(rawData)
                    }
                })
            })

            if (opts.data) {
                if (typeof opts.data !== "string")
                    opts.data = JSON.stringify(opts.data)
                req.write(opts.data)
            }
            req.end()
            req.on('error', (err: any) => {
                console.error(`Encountered an error trying to make a request: ${err.message}`);
                reject(new LokAPIExc.RequestFailed(err.message))
            })
        })
    }

    base64Encode = (s: string) => Buffer.from(s).toString('base64')
    persistentStore = new cookieStore()
}


if (!process.env.VUE_APP_LOKAPI_HOST) {
    throw new Error("Please specify VUE_APP_LOKAPI_HOST in '.env'")
}


var lokAPI = new LokAPI(
    process.env.VUE_APP_LOKAPI_HOST,
    process.env.VUE_APP_LOKAPI_DB,
)
```


### Example for `nativescript`

Using `@nativescript-community/https` as HTTP request implementation:

Note that this example couldn't be thoroughly tested as much as
it should. Use with caution.

```typescript
  import * as https from '@nativescript-community/https';

  import { LokAPIAbstract, e as LokAPIExc, t as LokAPIType } from "@lokavaluto/lokapi"

  import { getString, remove as removeSetting, setString } from '@nativescript/core/application-settings';

  import cyclos from '@lokavaluto/lokapi-backend-cyclos'


  class applicationSetting implements LokAPIType.IPersistentStore {
      get(key: string, defaultValue?: string): string {
          return getString("lokapi_" + key, defaultValue)
      }
      set(key: string, value: string): void {
          setString("lokapi_" + key, value)
      }
      del(key: string): void {
          removeSetting("lokapi_" + key)
      }
  }


  class NativeLokAPI extends LokAPIAbstract {

      BackendFactories = {
          cyclos,
      }

      httpRequest = async (opts: LokAPIType.coreHttpOpts) => {
          const nativeRequestOpts = {
              url: opts.protocol + "://" + opts.host + opts.path,
              method: opts.method,
              headers: opts.headers,
              body: opts.data,
              useLegacy: true,
          }
          let response
          try {
              response = await https.request(nativeRequestOpts)
          } catch (err) {
              console.error(
                  `Encountered an error trying to make a request: ${err.message}`)
              throw new LokAPIExc.RequestFailed(err.message)
          }

          const statusCode = response.statusCode;
          let rawData = await response.content.toStringAsync();

          if (!statusCode || statusCode.toString().slice(0, 1) !== '2') {
              throw new LokAPIExc.HttpError(statusCode, response.reason, "", response)
          }

          if (opts.responseHeaders) {
              for (const header in response.headers) {
                  opts.responseHeaders[header] = response.headers[header]
              }
          }

          return rawData
      }

      base64Encode = base64Encode
      persistentStore = new applicationSetting()
  }


  var lokAPI = new NativeLokAPI(APP_HOST, APP_DB)

```


# Usage

## Basic usage

### Request to sign-up

Before being able to log in and use the local currency, you must have
a user account on the administrative backend. If you don't already
have one yet, this is how to request the creation of one from the
`lokApi` instance:

```typescript
if (await lokApi.canSignup()) {
    await lokApi.signup(
      "john.doe@company.com",  // login
      "Doe",                   // firstname
      "John",                  // lastname
      "myp4ss0rd",             // password
    )
}
```

Note that **the administrative backend might implement** sign-up
mechanism **or choose not to**. Thus, you can check if this is possible
through `lokApi.canSignup()` first before trying to effectively
use `lokApi.signup(..)`.

Under the hood, the later will trigger the administrative backend to
take actions to process your sign-up request. In `odoo` administrative
backend, traditionally, that could means sending you an email with
instructions you'll need to follow to effectively complete the
registration process.

Note that `lokApi.signup(..)` / `lokApi.canSignup()`, along with
`lokApi.resetPassword(..)` / `lokApi.canResetPassword()` do NOT require
to be logged in before.

### Requesting a password reset

If you forgot your password, you can trigger a request to reset your
password by providing your login.

```typescript
if (await lokApi.canResetPassword()) {
    await lokApi.resetPassword("myuser")
}
```

Note that **the administrative backend might implement** password reset
mechanism **or choose not to**. Thus, you can check if this is possible
through `lokApi.canResetPassword()` first before trying to effectively
use `lokApi.resetPassword(..)`.

Under the hood, the later will trigger the administrative backend to
take actions to reset your password. In `odoo` administrative backend,
traditionally, that could means sending you an email with instructions
you'll need to follow to reset your password.

Note that `lokApi.resetPassword(..)` / `lokApi.canResetPassword()` do
NOT require to be logged in before.

### Login

You must log in to the server with an existing account on the
administrative backend:

```typescript
await lokApi.login("myuser", "mypassword")
```

Note that you can check if you are logged in with `lokApi.isLogged()`

### Accessing accounts

We assume that you've instanciated `LokAPI` as stated in the previous
section, and you have logged in.

```typescript
let accounts = await lokAPI.getAccounts()

let balance = await accounts[0].getBalance()
let symbol= await accounts[0].getSymbol()

console.log(`balance in first account: ${balance} ${symbol}`)
```

 * `backend.getAccounts()` is the list of accounts in that connection
(warning, this is a promise).

 * `account.getBalance()` is the balance of the account

 * `account.getSymbol()` is the currency symbol for the account


### Crediting account

You can credit your account thanks to `account.getCreditUrl(amount)`.
This will return an url to finish the purchase of new credits.

```typescript
let accounts = await lokAPI.getAccounts()

url = await accounts[0].getCreditUrl(100)

console.log(`I need to follow instructions on $url ` +
            'to purchase credit to store in my account.')
```

Note that depending on the backend, an admin might have to
manually validate the final step of crediting the money on
your account.

### Looking for recipients

Recipients are receiving ends of a transfer of money. These are
connected to contacts in `lokapi`.

```typescript
let recipients = await lokAPI.searchRecipients("Alain")

for await (const recipient of recipients) {
    console.log(`name: ${recipient.name}`)
}
```

Note that if you look for an empty string,
`lokAPI.searchRecipients("")` will return all favorite recipients.

Recipients are always ordered with favorites first and by name.

There is also a `IBackend.searchRecipients(..)` that works similarly
and limits search to recipients able to receive money in the selected
backend. Using the general `lokAPI.searchRecipients(..)` will look for
any recipients in all the loaded backends.

You can also grab recipients by url. This url is the identity
url created by odoo. It'll return a list of recipients, one
for each backend you can use to send money.

```typescript
let url = "https://myhost.com/fr_FR/partners/foo-bar-13"
let recipients = await lokAPI.getRecipientsFromUrl(url)

recipients.forEach(recipient => {
    console.log(`name: ${recipient.name}`)
})
```


### Transfer money between an account to a recipient

Transfering money is done from an account of the logged-in user
to a recipient:

```typescript
// Fetch recipients named 'Alain'
let recipients = await lokAPI.searchRecipients("Alain")

let payments = await recipients[0].prepareTransfer("12", "Dinner Party participation")
for (const payment of payments) {
  payment.execute()
}
```

Note that `.prepareTransfer(..)` can lead to these exceptions to be thrown:
 * sub-classes of `InvalidAmount`
  * `NegativeAmount`: Upon negative amounts
  * `NullAmount`: Upon 0 amount
  * `RefusedAmount`: When the backend refuses the transaction (often
linked to insufficient funds).
 * `InactiveAccount`: Source or destination account is inactive.

If the backend supports it (test `Backend.splitMemoSupport` to check
for this support), you can then also provide a third string argument
as a recipient memo, this allows you to set different memo
(descriptions) for the transaction for the recipient and the sender.

Without support for split memo, or without specifying a third
argument, the second argument is used as the description for both
sides.

### Requesting contact info on current logged in user

The method `lokAPI.getMyContact()` allows you to get back
your own information.:

```typescript
// My own information
let me = await lokAPI.getMyContact()
console.log(`My user name: ${me.name}`)

```


### Setting/Unsetting Favorite status of a contact

You can set or unset the "favorite" state of a given contact with the
`lokAPI.setFavorite(..)`, `lokAPI.unsetFavorite(..)`, or
`lokAPI.toggleFavorite(..)` method. This can be used on a recipient
(from `.searchRecipients()`) or a contact (but for now, only
`.getMyContact()` is outputting a contact, and it doesn't make
sense to be your own favorite, does it ?).

It'll not return any value, but the contact will be updated
accordingly.

```typescript
let recipients = await lokAPI.searchRecipients("Alain")

await recipients[2].setFavorite()
await recipients[3].unsetFavorite()

console.log(recipients[3].is_favorite) // is expected to be unset

```


### List transactions

List past transactions for the current logged in user.

```typescript
let transactions = await lokAPI.getTransactions()

for await (const tr of transactions) {
    console.log(`  ${tr.date} ${tr.amount} ${tr.currency}`)
}
```

You can also retrieve transaction in a specific date span:

```typescript
  let transactions = await lokAPI.getTransactions({
    dateBegin: new Date(2020, 1, 1),
    dateEnd: new Date(2021, 1, 1)
  })

  for (const tr of transactions) {
      console.log(`  ${tr.date} ${tr.amount} ${tr.currency}`)
  }
```


## Advanced usage

### list backends instances and min/max credit amount

Lokapi provides advanced backend actions directly on it's backend's
instances. You can list them with cached, debounced, async call
`lokAPI.getBackends()`.

`Backend` objects offers `minCreditAmount` and `maxCreditAmount` that
allows you to know what would be the minimum and maximum accepted
amount for credit requests if set in the administrative backend.

```typescript
let backends = await lokAPI.getBackends()

for (const backend of backends) {
    console.log(
        `${backend.internalId}:\n` +
        `  min: ${backend.minCreditAmount}\n` +
        `  max: ${backend.maxCreditAmount}\n`)
    if (backend.safeWalletRecipient) {
      console.log(`  safe wallet: {backend.safeWalletRecipient.name}`)
    }
}
```

### Requesting the creation of a new user account

If you don't already have a money account, some backends will allow
you to submit a money account creation request. Once accepted by an
admin, you'll be able to use it.

This is done directly on the backend object thanks to
`IBackend.createUserAccount(data)`, provided that the backend
implements this.

Argument of `IBackend.createUserAccount(data)` is a simple object
whose content depends on the backend. Please refer to the target
backend documentation to provide the correct information.

You might want to have a look at the section talking about caching
and debouncing, because the backend list won't be udpated after
the user account created unless you flush the backend caches
with `lokAPI.flushBackendCaches()`.

### list user accounts

Backends holds several user accounts. These are often not so much
advertised intermediary object as they can be confusingly close from
bank accounts, as in most backend, either there are no real user
accounts but only an account, and in others, there are only one bank
account per user account. But they share one aspect : they are the
object requiring authentication of the holder.

You can list them from a `Backend` object thanks to property
`backend.userAccounts`, which output an Object associating the
`internalId` with the corresponding `userAccount` :

```typescript
let backends = await lokAPI.getBackends()

for (const backend of backends) {
    console.log(`  ${backend.internalId}`)
    for (const userAccount of Object.values(backend.userAccounts)) {
        console.log(`    - account: ${userAccount.internalId}`)
    }
}
```

You can also get the full list of all user accounts from the main
`LokAPI` object:

```typescript
const userAccounts = await lokAPI.getUserAccounts()

for (const userAccount of userAccounts) {
    console.log(`  ${userAccount.internalId}`)
}
```


### caching and debouncing

`lokAPI` instances provide partial caching and debouncing. Caching
means that most calls that generates an http request will have their
results stored, and the next call won't be actually performed but the
`lokAPI` instance will provide the caller with the stored result.

Debouncing occurs when you have several callers waiting for the same
async results, they will all be coalesced into waiting for the same
query, avoiding to make N calls for the same query.

This is especially useful if you deal with reactive GUIs with lots of
components in a typically event-driven environment.

An issue with this, is that you need to know how to forget and ask for
a new query when you know data might have changed on the server side.

We provide the `lokAPI.flushBackendCaches()` to deal with that.

This is especially important after creating a new account, as backends
creation are cached and debounced, because these are information that
typically won't change often.


## Admin

These are usage for users having special administrative rights
on the *administrative backends* or the *currency backends*.

### Validate account creation requests

Users can request the creation of a new bank account via
`IBackend.createUserAccount(..)`, provided that the backend
implements this.

#### Check user account validation rights

The call to `.hasUserAccountValidationRights()` will return a boolean
to tell you if any backends has at least one of their user accounts
with `Account Validation` rights.

Please note that this function is available on top-level `lokApi`
instance, and also on `Backend`'s instances (it'll only check for
rights in its own user accounts), and finally directly on each
`UserAccount` instances.

```typescript
let hasRights = await lokAPI.hasUserAccountValidationRights()

if (hasRights) {
  console.log("I've got the power !")
}
```


#### Get list of account waiting for validation

`.getStagingUserAccounts()` on main instance will query
administrative backend for the list of user account created that needs
validation from an administrative account.

Please note that it returns `IRecipient` objects, that allows you
to print names.

```typescript
let recipients = await lokAPI.getStagingUserAccounts()

recipients.forEach(recipient => {
    console.log(`${recipient.name} has created his account and awaits validation.`)
})
```


#### Validate account creation

`.validateCreation()` upon a recipient instance will request the
validation of that account. The current user logged in need to have
user account validation rights.

Depending on the backend, it could have to check your identity or your
credentials.

```typescript
let recipients = await lokAPI.getStagingUserAccounts()

recipients.forEach(recipient => {
    await recipient.validateCreation()
})
```


### Validate credit requests

Credit requests (the action of requesting to get some additional
credit on your account) can be issued indirectly by the administrative
backend. There are no direct method on LokAPI to create a credit
request.


#### Check credit requests validation rights

The call to `.hasCreditRequestValidationRights()` will return a
boolean to tell you if any backends has at least one of their user
accounts with `Credit Request Validation` rights.

Please note that this method is available on top-level `lokApi`
instance, and also on `Backend`'s instances (it'll only check for
rights in its own user accounts), and finally directly on each
`UserAccount` instances.

```typescript
let hasRights = await lokAPI.hasCreditRequestValidationRights()

if (hasRights) {
  console.log("I've got the power !")
}
```


#### Get list of credit requests waiting for validation

`.getCreditRequests()` on main instance will query administrative
backend for the list of credit requests that needs validation
from an administrative account.

Please note that it returns `ICreditRequest` objects, allowing you
to query the recipient requesting the credit, and the amount.

Note that `ICreditRequest` are `ITransactions`, so you can expect
the same properties (`amount`, `related`, `currency`, ...)

```typescript
let creditRequests = await lokAPI.getCreditRequests()

creditRequests.forEach(creditRequest => {
    console.log(`${creditRequest.related} has requested a credit of ${creditRequest.amount}.`)
})
```


#### Validate credit request

`.validate()` upon a credit request instance will send the validation
of that credit request (and thus credit the account with the given
amount). The current user logged in need to have credit request
validation rights.

Depending on the backend, you might have to confirm your identity or
your credentials.

```typescript
let creditRequests = await lokAPI.getCreditRequests()

creditRequests.forEach(creditRequest => {
    await creditRequest.validate()
})
```


## Miscellaneous and internal helpers

### Get the backend list

You can query the `lokapi` to get the backend list available on the
administration backend side. A `Backend` instance is the main object
whose instance is responsible for a currency domain. You have a
`Backend` per currency. So in short, `.getBackends()` will give you
the available currencies available.

This function is cached, so it doesn't get updated if you happen
to add a new currency backend in the administration backend.

`.getBackends()` returns an Object mapping a string identifier for the
backend (ie: `cyclos:cyclos.example.com`, `comchain:Lemanopolis`,
...), and the Backend object instance.

```typescript
let backends = await lokAPI.getBackends()

for (const b in backends) {
    console.log(`  Backend ${b}:`, backends[b])
}
```


### Direct request to odoo api

You can use `lokapi` instance to query directly the odoo api trough
the `get`, `post`, `put`, `delete` methods and their authenticated
counterparts, `$get`, `$post`, `$put`, `$delete`.

```typescript
// All 8 methods have this signature:
// type restMethod = (path: string, data?: JsonData, headers?: { [label: string]: string }): Promise<JsonData>

// Notice that the next call is an example, but you don't need to
// use this endpoint as it is used by the lokAPI.login() and
// manages token for you.
lokAPI.post('/auth/authenticate', {
  api_version: 2,
  db: 'mydb',
  params: ['lcc_app']
}, {
  'Authorization': 'XYZ',
})

lokAPI.$post(`/partner/${userId}`)
lokAPI.$put(`/partner/${userId}/favorite/set`)
lokAPI.$get(`/partner/partner_search`, {
  value: "foo"
})
```

Please note that `.get(..)` and `.$get(..)` have same prototype
and usage than other function and do not require you to build a query
string as it'll encode in the URL as a querystring the data you've
provided.
# Changelog


## 0.1.1-alpha.202607211613

### New

* Add authorization check for payment request creation. [Seddik]

* Add ``UserAccount.getContactInformation(caller)`` [Valentin Lab]

* Add ``Recipient.refresh()`` [Valentin Lab]

  Expose ``refresh(caller?)`` that re-fetches the user account's data from
  the administrative backend via the LCC API ``GET /wallet/<ident>/get``
  endpoint and merges it into ``jsonData``. An optional ``caller`` account
  allows an admin to refresh another user's wallet.

* Add ``Recipient.archive()`` [Valentin Lab]

  Expose ``archive()`` that archives the recipient's wallet through the LCC
  API ``POST /wallet/<ident>/archive`` endpoint.

* Add ``Recipient.updateAccount()`` [Valentin Lab]

  Expose ``updateAccount(accountData)`` that updates both the financial and
  administrative backends. The administrative side calls the LCC API
  ``POST /wallet/<ident>/update`` endpoint; the financial side is left as
  an abstract hook (``updateAccountForFinancialBackend``) for
  backend-specific implementations to override.

* Add ``UserAccount.lccApi`` [Valentin Lab]

  Exposes a ``@singleton`` getter that returns an ``LccApiClient``
  pre-bound to this account's ``uri``.  Callers no longer need to
  construct the client by hand or remember to pass the
  ``X-Lokapi-Caller-User-Uri`` header for ``wallet/*`` and
  ``recipient/*`` endpoints — the per-account client owns it.

  The ``features`` argument stays per-call so each endpoint can
  declare its own feature string (e.g. ``"wallet/0"``).

* Expose ``{UserAccount,Recipient}.{uri,ident}`` [Valentin Lab]

  Adds the plugin-specific ``ident`` abstract property on both
  ``UserAccount`` and ``Recipient``, plus matching ``uri`` /
  ``walletUri`` getters that derive the full lokavaluto URI from the
  backend's engine + currency identifier via ``buildUserUri`` /
  ``buildWalletUri``.

  This gives consumers (notably the LCC API identity headers) a single
  source of truth for user / wallet URIs without re-implementing the
  ``engine://currencyIdent/kind/ident`` shape at each call site.

* Support ``uri/0`` feature for currency URI format. [Valentin Lab]

  Add ``src/uri.ts`` with ``buildUri``, ``parseUri``, ``legacyToUri``
  as single source of truth for lokavaluto URI format.

  ``BackendAbstract``: replace ``internalId``/``currencyUri`` with
  ``get uri()`` reading ``currency_uri`` from backend credentials.

  ``getBackendCredentials`` requests ``uri/0 feature-less/0`` feature
  and converts legacy ``type`` format to ``currency_uri`` via
  ``legacyToUri`` when the server does not support ``uri/0``.

  ``authenticate`` declares ``uri/0`` support via ``FeatureOpts``
  so prefetch data may use the new format directly.

* Add ``JsonRESTFeatureClientAbstract`` feature negotiation layer. [Valentin Lab]

  Inserts a new abstract class between ``JsonRESTPersistentClientAbstract``
  and ``OdooRESTAbstract`` in the REST client inheritance chain.

  The ``$get``/``$post``/``$put``/``$delete`` methods now accept an
  optional ``FeatureOpts`` third argument (detected via key presence)
  while remaining fully backward-compatible with plain headers calls.

  When ``FeatureOpts`` is used:
  - injects ``X-Client-Features`` request header
  - fills ``selectedFeatures``/``supportedFeatures`` arrays in-place
    from response headers (case-insensitive lookup)
  - catches HTTP 406 → ``FeatureMismatchError``

  Includes ``parseFeatureSpecifier()`` utility for expanding compact
  specifiers (e.g. ``"cap/0-2,5"`` → individual features).

* Add HTTP request batching for Odoo backend and fix race condition in credential cache. [Valentin Lab]

  - Add ``BatchQueue`` class in ``src/backend/odoo/batch.ts`` that queues HTTP requests within a tick, deduplicates identical requests, and sends directly for single requests or batches via POST ``/batch`` for 2+ requests
  - Add comprehensive test suite (9 tests) in ``src/backend/odoo/batch.test.ts`` covering single bypass, batching, deduplication, and error handling
  - Integrate batching into ``OdooRESTAbstract``: ``_batchQueue`` in constructor, ``_unbatchedRequest()`` for direct calls, ``request()`` override that enqueues into batch queue (skipping ``/auth/authenticate``), GET query string preprocessing, and 401→``AuthenticationRequired`` transformation
  - Fix race condition in ``getBackendCredentials()`` and ``getBackends()``: IIFE now returns the value, outer function uses ``return await`` to capture it from the promise chain directly instead of re-reading from ``this._backendCredentials``/``this._backends`` which could be cleared by concurrent ``clearBackendCache()``

* Add ``CurrencyMismatch`` exception. [Valentin Lab]

  Thrown when a wallet's currency does not match the backend
  it is being registered on (client-side check, distinct from
  ``CurrencyNotAvailable`` which is a server-side rejection).

* Add ``CurrencyNotAvailable`` exception. [Valentin Lab]

  Thrown when a wallet's currency does not match any backend
  configured on the server (e.g. importing a Lokacoin wallet
  on a Lemanopolis-only instance).

* Make validation right check both backends (administrative and financial) [Valentin Lab]

* Add ``Backend.canSearchAllRecipients()`` [Valentin Lab]

* Add ``Lokapi.getReportContactInformation()`` [Valentin Lab]

* Add ``Lokapi.getUserAccountsFromWalletUri(..)`` [Valentin Lab]

* Add ``Backend.searchAllRecipients()`` [Valentin Lab]

* Add ``Recipient.isTransferAllowedByAdministrativeBackend()`` [Valentin Lab]

* Upgrade API types to support the ``.prepareTransfer()`` new process. [Valentin Lab]

* Add new ``PrepareTransfer*`` exceptions. [Valentin Lab]

  These exceptions are to be cast in verification prior to transfer that
  can be made by the client implementation in relation with the
  financial backend.

* Add ``creditRequest.isTopUp`` property. [Seddik]

* Add ``creditRequest.requester`` property. [Seddik]

* Add ``userAccount.isTopUpAllowed`` property. [Seddik]

* Add specific ``CanceledOperation`` exception upon ``Lokapi.getBackends()`` [Seddik]

  This allows calling side to react accordingly (and probably properly
  cancel ongoing actions and silence this exception in a clean way).

* Provide ``BackendUnavailableTransient`` exception. [Valentin Lab]

  This exception can be used by the backend to signify temporary
  unavailability.

* Add definition for split memo support. [Valentin Lab]

* New ``.activateAccount(..)`` entrypoint on financial backend. [Seddik]

* Add ``Backend.safeWalletRecipient`` property. [Seddik]

* Add ``Backend.searchRecipientByUri()`` [Seddik Kadi]

  Data intended as URI is sent to administrative backend
  to get a unique recipient or fail.

* Provide ``Account`` and ``CreditRequest`` base classes to complete in backends. [Seddik Kadi]

  These objects provide the ``odoo`` only functionalities.

* Add ``PaymentConfirmationMissing`` exception. [Valentin Lab]

* Add ``backend.searchRecipients(..)`` to look for recipients in the backend's currency. [Valentin Lab]

### Changes

* Replace HTTP permission checks with prefetched ``authorized_actions`` [Valentin Lab]

  Permission checks on the administrative backend
  (``hasUserAccountValidationRightsForAdministrativeBackend``,
  ``hasCreditRequestValidationRightsForAdministrativeBackend``,
  ``canSearchAllRecipients``) previously issued one HTTP round-trip
  each.  They now read from the ``authorized_actions`` field of
  ``backend_credentials.accounts`` which is prefetched at login —
  no HTTP call.

  Also:

  - add per-user-account permission API in ``UserAccount``:
    ``hasUserAccountValidationRights``,
    ``hasCreditRequestValidationRights``,
    ``canSearchAllRecipients``,
    ``canSetPermissions`` (plus ``*ForFinancialBackend`` hooks for
    backend chains to override)
  - add backend-level ``canSetPermissions`` /
    ``canSetPermissionsForAdministrativeBackend`` /
    ``canSetPermissionsForFinancialBackend``
  - move ``searchAllRecipients`` from ``BackendAbstract`` to
    ``UserAccount`` — it now targets the LCC API
    ``/recipient/search_all`` endpoint with user identity and
    feature headers

* Prefer using ``ITransaction`` as ``IRecipient.transfer()`` output type. [Valentin Lab]

* Convert ``.searchRecipients(..)`` to async generator. [Seddik Kadi]

  Manage batch querying administrative backend to improve responsivness
  in case of large numbers of recipients.

### Fix

* Prevent ``ttlcache`` from caching rejected promises. [Valentin Lab]

  ``ttlcache`` cached whatever the wrapped method returned, including
  rejected promises.  A transient failure (e.g. ``getMyContact()``
  hitting ``TokenRequired`` before authentication) was then replayed
  from cache for the whole TTL window: logging in immediately after
  page load kept serving the poisoned rejection and broke the session
  setup until the entry expired.

  Enable ``noCacheOnReject`` (as ``singleton`` already does) so
  rejections evict the entry and the next call recomputes.

* Make feature compatible with batching. [Valentin Lab]

* Make ``Lokapi.authenticate()`` compatible with wider type of unauthorized response. [Valentin Lab]

  Odoo16 changed their exception message to "Access Denied" instead of
  "Access denied".  The overall process would be stronger if a proper 401
  would be sent, so this is implemented also in this commit.

* Ensure backends objects are singletons even when recomputing the list of them. [Valentin Lab]

  This is important if a financial backend expects to cache information
  in its instance, but we still want to refresh the list of the backends,
  that comes from the administrative backend.

* Add support of old API for account activation as a fallback mechanism. [Valentin Lab]

* Improve error reporting when safe wallet doesn't have a comchain account. [Valentin Lab]

* Catch odoo error about unknown email when calling ``lokAPI.resetPassword(..)`` [Valentin Lab]

  The administrative backend (here "odoo") might translate error
  messages. We need to fix the ``Accept-Language`` header in all
  requests.


## 0.1.0 (2023-05-08)

### New

* Add ``Backend.maxCreditAmount`` property. [Seddik Kadi]

* Add ``.signup(..)``/``.canSignup()`` methods. [Valentin Lab]

* Add ``.resetPassword(..)``/``.canResetPassword()`` methods. [Seddik Kadi]

* Add ``Backend.minCreditAmount`` property. [Valentin Lab]

* Use odoo's ``public_name`` if available as ``contact.name`` [Valentin Lab]

* Add date span capability to ``.getTransactions(..)`` [Valentin Lab]

* Add ``lokAPI.isLogged()`` to query if we are logged in. [Valentin Lab]

* Provide a ``LokAPI.getUserAccounts()`` to list all user accounts. [Valentin Lab]

  User accounts are the one requiring authentication in currently implemented
  backends. And if previously, we didn't advertise these intermediary object
  it seems to be more and more important to do so.

* Make ``.requestLocalPassword(..)`` interface to receive the ``userAccount`` to unlock as second argument. [Valentin Lab]

* Make the ``dbname`` argument optional on instantiation. [Valentin Lab]

  Indeed, there is a default database on the server side that we can
  choose to trust.

* Provide ``InsufficientBalance`` exception for currency backends. [Seddik Kadi]

* Add ``.getCreditRequests(..)`` to get list of pending credit requests. [Valentin Lab]

* Add ``.hasCreditRequestValidationRights()`` check. [Valentin Lab]

* Provide ``requestLocalPassword`` type declaration for backends. [Valentin Lab]

* Provide new general exception and utility functions to be used in backends. [Valentin Lab]

* Add ``.getStagingUserAccounts()`` to get list of newly created account awaiting for validation. [Valentin Lab]

* Provide new ``.hasUserAccountValidationRights()`` async boolean check. [Valentin Lab]

* ``LokAPI.getBackends(..)`` is now public. [Valentin Lab]

  This is now required as ``Backend`` direct access is required to
  ``Backend.createAccount(..)`` and other ``Backend`` level features.

### Changes

* Remove ``relatedUser`` from transaction type definition. [Valentin Lab]

* Store current user contact info from prefetch in cache state. [Valentin Lab]

### Fix

* Fix: enforce correct version of ``@0k.io/type-requests`` for support of ``timeout`` in http requests. [Valentin Lab]

* Prevent data leaking when de-logging or for forcing user accounts refresh. [Valentin Lab]

  This provides a stronger debouncing and caching mecanism along with a
  ``lokAPI.forceRefresh()`` cache clearing function. All this begs for a
  proper caching and debouncing library.

* Let 403 error on odoo backend be not treated as re-login request. [Valentin Lab]

* Remove ``searchProRecipients(..)`` as endpoint was removed. [Valentin Lab]

* Standardize backend type and accounts (api 12) [Valentin Lab]

  The backend id identify a currency and is structured as this
  ``<BACKEND>:<IDCURRENCY>``. It is to the backend discretion to
  make sens of IDCURRENCY. The BACKEND part is used to identify
  the implementation of the backend and instantiate the class
  provided thanks to ``BackendFactories``.

* ``.requestLogin()`` was not called as expected on minified builds. [Valentin Lab]

  When code is minified detection of exception using the
  ``constructor.name`` is deemed to fail. A second problem was
  duplicate (!) declaration of exceptions.

* Avoid exception when trying to ``.makeRecipients()`` in a backend where we don't have an account. [Valentin Lab]


## 0.0.17 (2021-11-17)

### New

* Provide a ``.requestLocalPassword()`` callback for client app. [Valentin Lab]

  This will allow backends to query user through client app UI for a
  local only password. This password is meant to stay on the device
  the app is running, and is typically asked before actually paying.
  It could also be asked before any administrative task.

* Externalize currency backends, ``cyclos`` code exported to ``lokapi-backend-cyclos`` [Valentin Lab]

* Add ``markBackend`` boolean if we have more than one payment backend. [Valentin Lab]

  This is meant to be used in widgets receiving Recipients to know if
  they should display some information about the payment
  backend.

* Support for response header querying. [Valentin Lab]

### Changes

* Specify a little more ``ITransaction`` [Valentin Lab]

  In particular ``.amount`` is a string, and ``.date`` is a javascript
  ``Date`` object. Removed ``relatedKind`` and ``kind`` as these are
  directly coming out from cyclos and didn't find any direct use yet
  in client app.

### Fix

* ``.getTransactions()`` support for complete and correct sorted access. [Valentin Lab]

  Transactions are correctly be sorted between backends, accounts.
  Internal requests are parallel when possible or useful.  Usage is an
  AsyncGenerator that allows client app to request as many as needed to
  implement any type of paging or infinite scrolling independantly from
  the implementation of the backends that can focus on the best strategy
  to optimize requesting.

* Debounce ``getBackendCredentials`` and ``getBackends`` [Valentin Lab]

  Client app may be inducing several call to these entrypoints at
  once. By storing and returning the ongoing promise, we make sure
  to avoid duplicating network requests.


## 0.0.16 (2021-09-08)

### New

* Add ``.searchProRecipients()`` and fix ``.searchRecipients()`` [Valentin Lab]

  These both function are temporarily provided to search on professional
  and non-professional recipients. Note that the internal logic is
  currently taken to the odoo side. Paging is temporarily disabled.

### Changes

* Upgrade to ``lokAPI.searchRecipients()`` [Valentin Lab]

  It'll:
  - return all favorite recipients if search string is empty
  - always sort results by putting favorite first

### Fix

* Catch odoo unauthorized HTML ``HttpErrors`` as expected. [Valentin Lab]

* ``lokAPI.{,$}get(..)`` query-string management support for objects. [Valentin Lab]


## 0.0.14 (2021-08-04)

### New

* Add ``lokAPI.getRecipientsFromUrl(..)`` support. [Valentin Lab]

* Add ``.getCreditUrl()`` support to accounts. [Valentin Lab]

### Changes

* Using new api GET instead of POST for ``partner_search`` [Valentin Lab]

### Fix

* Request login when cyclos backend's token incorrect also. [Valentin Lab]

* ``.{,$}get(..)`` support for array data values. [Valentin Lab]


## 0.0.13 (2021-07-24)

### New

* API changes around object-like usage. [Valentin Lab]

  Please note that:

  - ``lokAPI.searchRecipient()`` was renamed
  ``lokAPI.searchRecipients()`` (added an 's')

  - ``lokAPI.getUserInfo()`` was renamed ``lokAPI.getMyContact()``

    This method doesn't take any argument anymore. We'll introduce a
    ``lokAPI.getContact()`` when it'll appears in user stories.

  - ``lokAPI.transfer(recipient, amount, desc)`` was moved to
    ``recipient.transfer(amount, desc)``

    Note that an ``IRecipient`` is an ``IContact``, but not the reverse,
    so you can use ``.transfer()`` only on ``IRecipient``'s object.

  - ``lokAPI.{set,unset,toggle}Favorite(contact)`` were moved to
    ``contact.{set,unset,toggle}Favorite()``.

    Note that an ``IRecipient`` is an ``IContact``, so you can use these
    on both objects.


## 0.0.12 (2021-07-23)

### New

* ``lokAPI.{unset,set,toggle}Favorite()`` implemented with API 3. [Valentin Lab]

  This API is more efficient and allow to force set/unset. We are keeping
  the ``toggle`` for lib users.

* Add ``http`` support and port specification. [Valentin Lab]


## 0.0.11 (2021-07-21)

### New

* Add ``lokapi.toggleFavorite(..)` [Valentin Lab]

### Changes

* Make `.getUserInfo()` without argument return currently logged in user info. [Valentin Lab]

  This will get important when using only the session token and needing
  to get back some information. We don't have our own `userId`.


## 0.0.9 (2021-07-08)

### New

* Add ``.getTransactions()`` with cyclos implementation. [Valentin Lab]


## 0.0.8 (2021-07-08)

### New

* Add ``lokAPI.transfer(..)`` to management payment. [Valentin Lab]

* Add ``LokAPI.searchRecipient(..)`` [Valentin Lab]


## 0.0.7 (2021-07-08)

### New

* Compatibility with odoo server version 2. [Valentin Lab]

* Catch HTML dev errors of odoo API. [Valentin Lab]

* Add persistent session management. [Valentin Lab]

* ``JsonRESTClientAbstract`` support passing data as querystring when using ``GET`` method. [Valentin Lab]

### Changes

* Refactor down the ``apiToken`` mecanism. [Valentin Lab]

### Fix

* Authentication state incorrectly checked. [Valentin Lab]


## 0.0.6 (2021-07-05)

### Changes

* Prefer ``getMethod()`` syntax to getters as those will be promises. [Valentin Lab]

### Fix

* Add example with arguments in documents. [Valentin Lab]


## 0.0.5 (2021-07-05)

### New

* Provide advanced ``get``, ``post``, ``put``, ``delete`` helpers on ``OdooRESTAbstract`` with doc. [Valentin Lab]

* Can set token directly to access authenticated endpoints. [Valentin Lab]

  This will allow to authenticate without login and password, but
  using only the =apiToken=.

### Changes

* Provide convenience method to request on all backends. [Valentin Lab]


## 0.0.4 (2021-07-01)

### New

* Ignore but log attempts to load a missing backend. [Valentin Lab]

* Manage API version information. [Valentin Lab]

  For now, we'll only issue a warning in console

* Updated to new api version 1. [Valentin Lab]

### Changes

* Changing from mixin to abstract class model. [Valentin Lab]

### Fix

* Still need ``apiToken`` [Valentin Lab]

  We'll be able to remove ``apiToken`` when client will not
  need to use it.


## 0.0.3 (2021-06-30)

### New

* Manage API version information. [Valentin Lab]

  For now, we'll only issue a warning in console

* Updated to new api version 1. [Valentin Lab]

### Fix

* `odoo` property should be public. [Valentin Lab]

  As we removed delegations to odoo, we expect API client to use this
  attribute.


## 0.0.2 (2021-06-27)

### New

* Add backends, accounts, balance and symbol. [Valentin Lab]

* Store ``accounts`` information upon authenticate. [Valentin Lab]

---
_Source: https://npm.io/package/@lokavaluto/lokapi · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
