gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (c53c6b8b3 -> 4bf1ab8ba)


From: gnunet
Subject: [taler-wallet-core] branch master updated (c53c6b8b3 -> 4bf1ab8ba)
Date: Wed, 03 Apr 2024 19:57:10 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from c53c6b8b3 wallet-core: introduce setConfig request
     new 56da18042 wip #8655: updating paginated queries
     new 4bf1ab8ba wip #8655: fixing broken build

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../bank-ui/src/components/Transactions/state.ts   |  22 +-
 packages/bank-ui/src/hooks/account.ts              |  77 ++--
 packages/bank-ui/src/hooks/regional.ts             |  29 +-
 packages/bank-ui/src/pages/PublicHistoriesPage.tsx |  10 +-
 packages/bank-ui/src/pages/admin/AccountList.tsx   |   8 +-
 packages/bank-ui/src/pages/admin/AdminHome.tsx     |   5 +-
 packages/merchant-backoffice-ui/src/Routing.tsx    | 398 +++++++++++----------
 .../src/components/picker/DatePicker.tsx           |   4 +-
 .../merchant-backoffice-ui/src/hooks/backend.ts    | 373 -------------------
 packages/merchant-backoffice-ui/src/hooks/bank.ts  | 247 ++++---------
 .../merchant-backoffice-ui/src/hooks/instance.ts   |  60 +---
 .../merchant-backoffice-ui/src/hooks/order.test.ts | 282 +++++++--------
 packages/merchant-backoffice-ui/src/hooks/order.ts | 307 +++-------------
 packages/merchant-backoffice-ui/src/hooks/otp.ts   | 231 +++---------
 .../src/hooks/product.test.ts                      | 124 +++----
 .../merchant-backoffice-ui/src/hooks/product.ts    | 227 ++++--------
 .../merchant-backoffice-ui/src/hooks/templates.ts  | 286 +++------------
 .../src/hooks/transfer.test.ts                     | 115 +++---
 .../merchant-backoffice-ui/src/hooks/transfer.ts   | 191 ++--------
 .../merchant-backoffice-ui/src/hooks/webhooks.ts   | 233 +++++-------
 .../src/paths/admin/list/index.tsx                 |  16 +-
 .../src/paths/instance/accounts/create/index.tsx   |   2 +-
 .../src/paths/instance/accounts/list/index.tsx     |  55 ++-
 .../src/paths/instance/accounts/update/index.tsx   |  46 ++-
 .../orders/create/OrderCreatedSuccessfully.tsx     |  57 +--
 .../src/paths/instance/orders/create/index.tsx     |  60 +---
 .../src/paths/instance/orders/details/index.tsx    |  70 ++--
 .../src/paths/instance/orders/list/ListPage.tsx    |  12 +-
 .../src/paths/instance/orders/list/index.tsx       | 196 +++++-----
 .../src/paths/instance/otp_devices/list/index.tsx  |  67 ++--
 .../paths/instance/otp_devices/update/index.tsx    |  98 +++--
 .../src/paths/instance/products/list/index.tsx     |  44 +--
 .../src/paths/instance/products/update/index.tsx   |  44 +--
 .../paths/instance/templates/create/CreatePage.tsx |   5 +-
 .../src/paths/instance/templates/list/index.tsx    |  53 ++-
 .../src/paths/instance/templates/qr/index.tsx      |  53 +--
 .../paths/instance/templates/update/UpdatePage.tsx |   5 +-
 .../src/paths/instance/templates/update/index.tsx  |  44 +--
 .../src/paths/instance/templates/use/index.tsx     |  44 +--
 .../src/paths/instance/token/index.tsx             |  28 +-
 .../src/paths/instance/transfers/create/index.tsx  |  17 +-
 .../src/paths/instance/transfers/list/index.tsx    |  62 ++--
 .../src/paths/instance/update/index.tsx            |  22 +-
 .../src/paths/instance/webhooks/list/index.tsx     |  72 ++--
 .../src/paths/instance/webhooks/update/index.tsx   |  46 ++-
 .../src/paths/notfound/index.tsx                   |  40 ++-
 packages/taler-harness/src/index.ts                |   6 +-
 packages/taler-util/src/http-client/merchant.ts    |  34 +-
 48 files changed, 1571 insertions(+), 2956 deletions(-)
 delete mode 100644 packages/merchant-backoffice-ui/src/hooks/backend.ts

diff --git a/packages/bank-ui/src/components/Transactions/state.ts 
b/packages/bank-ui/src/components/Transactions/state.ts
index 4e4552a82..ce6338e57 100644
--- a/packages/bank-ui/src/components/Transactions/state.ts
+++ b/packages/bank-ui/src/components/Transactions/state.ts
@@ -17,7 +17,9 @@
 import {
   AbsoluteTime,
   Amounts,
+  HttpStatusCode,
   TalerError,
+  assertUnreachable,
   parsePaytoUri,
 } from "@gnu-taler/taler-util";
 import { useTransactions } from "../../hooks/account.js";
@@ -27,21 +29,27 @@ export function useComponentState({
   account,
   routeCreateWireTransfer,
 }: Props): State {
-  const txResult = useTransactions(account);
-  if (!txResult) {
+  const result = useTransactions(account);
+  if (!result) {
     return {
       status: "loading",
       error: undefined,
     };
   }
-  if (txResult instanceof TalerError) {
+  if (result instanceof TalerError) {
     return {
       status: "loading-error",
-      error: txResult,
+      error: result,
+    };
+  }
+  if (result.type === "fail") {
+    return {
+      status: "loading",
+      error: undefined,
     };
   }
 
-  const transactions = txResult.result
+  const transactions = result.body
     .map((tx) => {
       const negative = tx.direction === "debit";
       const cp = parsePaytoUri(
@@ -76,7 +84,7 @@ export function useComponentState({
     error: undefined,
     routeCreateWireTransfer,
     transactions,
-    onGoNext: txResult.isLastPage ? undefined : txResult.loadNext,
-    onGoStart: txResult.isFirstPage ? undefined : txResult.loadFirst,
+    onGoNext: result.isLastPage ? undefined : result.loadNext,
+    onGoStart: result.isFirstPage ? undefined : result.loadFirst,
   };
 }
diff --git a/packages/bank-ui/src/hooks/account.ts 
b/packages/bank-ui/src/hooks/account.ts
index 24309183f..543c49aed 100644
--- a/packages/bank-ui/src/hooks/account.ts
+++ b/packages/bank-ui/src/hooks/account.ts
@@ -16,6 +16,7 @@
 
 import {
   AccessToken,
+  OperationOk,
   TalerCoreBankResultByMethod,
   TalerHttpError,
   WithdrawalOperationStatus,
@@ -197,36 +198,44 @@ export function usePublicAccounts(
     keepPreviousData: true,
   });
 
-  const isLastPage =
-    data && data.type === "ok" && data.body.public_accounts.length <= 
PAGE_SIZE;
-  const isFirstPage = !offset;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  // if (data.type !== "ok") return data;
+
+  //TODO: row_id should not be optional
+  return buildPaginatedResult(data.body.public_accounts, offset, setOffset, 
(d) => d.row_id ?? 0)
+}
+
 
-  const result =
-    data && data.type == "ok" ? structuredClone(data.body.public_accounts) : 
[];
+type PaginatedResult<T> = OperationOk<T> & {
+  isLastPage: boolean;
+  isFirstPage: boolean;
+  loadNext(): void;
+  loadFirst(): void;
+}
+//TODO: consider sending this to web-util
+export function buildPaginatedResult<DataType, OffsetId>(data: DataType[], 
offset: OffsetId | undefined, setOffset: (o: OffsetId | undefined) => void, 
getId: (r: DataType) => OffsetId): PaginatedResult<DataType[]> {
+  const isLastPage = data.length <= PAGE_SIZE;
+  const isFirstPage = offset === undefined;
+
+  const result = structuredClone(data);
   if (result.length == PAGE_SIZE + 1) {
     result.pop();
   }
-  const pagination = {
-    result,
+  return {
+    type: "ok",
+    body: result,
     isLastPage,
     isFirstPage,
     loadNext: () => {
       if (!result.length) return;
-      setOffset(result[result.length - 1].row_id);
+      const id = getId(result[result.length - 1])
+      setOffset(id);
     },
     loadFirst: () => {
-      setOffset(0);
+      setOffset(undefined);
     },
   };
-
-  // const public_accountslist = data?.type !== "ok" ? [] : 
data.body.public_accounts;
-  if (data) {
-    return { ok: true, data: data.body, ...pagination };
-  }
-  if (error) {
-    return error;
-  }
-  return undefined;
 }
 
 export function revalidateTransactions() {
@@ -271,34 +280,10 @@ export function useTransactions(account: string, 
initial?: number) {
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
   });
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const isLastPage =
-    data && data.type === "ok" && data.body.transactions.length <= PAGE_SIZE;
-  const isFirstPage = !offset;
-
-  const result =
-    data && data.type == "ok" ? structuredClone(data.body.transactions) : [];
-  if (result.length == PAGE_SIZE + 1) {
-    result.pop();
-  }
-  const pagination = {
-    result,
-    isLastPage,
-    isFirstPage,
-    loadNext: () => {
-      if (!result.length) return;
-      setOffset(result[result.length - 1].row_id);
-    },
-    loadFirst: () => {
-      setOffset(0);
-    },
-  };
+  return buildPaginatedResult(data.body.transactions, offset, setOffset, (d) 
=> d.row_id)
 
-  if (data) {
-    return { ok: true, data, ...pagination };
-  }
-  if (error) {
-    return error;
-  }
-  return undefined;
 }
diff --git a/packages/bank-ui/src/hooks/regional.ts 
b/packages/bank-ui/src/hooks/regional.ts
index 274638f74..3e832a944 100644
--- a/packages/bank-ui/src/hooks/regional.ts
+++ b/packages/bank-ui/src/hooks/regional.ts
@@ -34,6 +34,7 @@ import {
 import { useState } from "preact/hooks";
 import _useSWR, { SWRHook, mutate } from "swr";
 import { useBankCoreApiContext } from "@gnu-taler/web-util/browser";
+import { buildPaginatedResult } from "./account.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 const useSWR = _useSWR as unknown as SWRHook;
@@ -249,31 +250,13 @@ export function useBusinessAccounts() {
     keepPreviousData: true,
   });
 
-  const isLastPage =
-    data && data.type === "ok" && data.body.accounts.length <= PAGE_SIZE;
-  const isFirstPage = !offset;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const result =
-    data && data.type == "ok" ? structuredClone(data.body.accounts) : [];
-  if (result.length == PAGE_SIZE + 1) {
-    result.pop();
-  }
-  const pagination = {
-    result,
-    isLastPage,
-    isFirstPage,
-    loadNext: () => {
-      if (!result.length) return;
-      setOffset(result[result.length - 1].row_id);
-    },
-    loadFirst: () => {
-      setOffset(0);
-    },
-  };
+  //TODO: row_id should not be optional
+  return buildPaginatedResult(data.body.accounts, offset, setOffset, (d) => 
d.row_id ?? 0)
 
-  if (data) return { ok: true, data, ...pagination };
-  if (error) return error;
-  return undefined;
 }
 
 type CashoutWithId = TalerCorebankApi.CashoutStatusResponse & { id: number };
diff --git a/packages/bank-ui/src/pages/PublicHistoriesPage.tsx 
b/packages/bank-ui/src/pages/PublicHistoriesPage.tsx
index 554da0c3f..1810bd5dd 100644
--- a/packages/bank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/bank-ui/src/pages/PublicHistoriesPage.tsx
@@ -31,9 +31,9 @@ export function PublicHistoriesPage(): VNode {
   const result = usePublicAccounts(undefined);
   const firstAccount =
     result &&
-    !(result instanceof TalerError) &&
-    result.data.public_accounts.length > 0
-      ? result.data.public_accounts[0].username
+      !(result instanceof TalerError) &&
+      result.body.length > 0
+      ? result.body[0].username
       : undefined;
 
   const [showAccount, setShowAccount] = useState(firstAccount);
@@ -45,13 +45,13 @@ export function PublicHistoriesPage(): VNode {
     return <Loading />;
   }
 
-  const { data } = result;
+  const { body: accountList } = result;
 
   const txs: Record<string, h.JSX.Element> = {};
   const accountsBar = [];
 
   // Ask story of all the public accounts.
-  for (const account of data.public_accounts) {
+  for (const account of accountList) {
     const isSelected = account.username == showAccount;
     accountsBar.push(
       <li
diff --git a/packages/bank-ui/src/pages/admin/AccountList.tsx 
b/packages/bank-ui/src/pages/admin/AccountList.tsx
index c4e529f9f..6402c2bcd 100644
--- a/packages/bank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/bank-ui/src/pages/admin/AccountList.tsx
@@ -51,19 +51,19 @@ export function AccountList({
   if (result instanceof TalerError) {
     return <ErrorLoadingWithDebug error={result} />;
   }
-  if (result.data.type === "fail") {
-    switch (result.data.case) {
+  if (result.type === "fail") {
+    switch (result.case) {
       case HttpStatusCode.Unauthorized:
         return <Fragment />;
       default:
-        assertUnreachable(result.data.case);
+        assertUnreachable(result.case);
     }
   }
 
   const onGoStart = result.isFirstPage ? undefined : result.loadFirst;
   const onGoNext = result.isLastPage ? undefined : result.loadNext;
 
-  const accounts = result.result;
+  const accounts = result.body;
   return (
     <Fragment>
       <div class="px-4 sm:px-6 lg:px-8 mt-8">
diff --git a/packages/bank-ui/src/pages/admin/AdminHome.tsx 
b/packages/bank-ui/src/pages/admin/AdminHome.tsx
index 94b88dc89..4784fc73a 100644
--- a/packages/bank-ui/src/pages/admin/AdminHome.tsx
+++ b/packages/bank-ui/src/pages/admin/AdminHome.tsx
@@ -26,6 +26,7 @@ import { Attention, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import {
   format,
   getDate,
+  getDaysInMonth,
   getHours,
   getMonth,
   getYear,
@@ -127,8 +128,8 @@ export function getTimeframesForDate(
       };
     case TalerCorebankApi.MonitorTimeframeParam.day:
       return {
-        current: getDate(sub(time, { days: 1 })),
-        previous: getDate(sub(time, { days: 2 })),
+        current: getDaysInMonth(sub(time, { days: 1 })),
+        previous: getDaysInMonth(sub(time, { days: 2 })),
       };
     case TalerCorebankApi.MonitorTimeframeParam.month:
       return {
diff --git a/packages/merchant-backoffice-ui/src/Routing.tsx 
b/packages/merchant-backoffice-ui/src/Routing.tsx
index 1fccc2637..874c2b0f2 100644
--- a/packages/merchant-backoffice-ui/src/Routing.tsx
+++ b/packages/merchant-backoffice-ui/src/Routing.tsx
@@ -19,14 +19,17 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { AbsoluteTime, TalerError, TalerErrorDetail, TranslatedString } from 
"@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
+  AbsoluteTime,
+  TalerError,
+  TranslatedString
+} from "@gnu-taler/taler-util";
+import {
   urlPattern,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
-import { Fragment, FunctionComponent, VNode, h } from "preact";
+import { createHashHistory } from "history";
+import { Fragment, VNode, h } from "preact";
 import { Route, Router, route } from "preact-router";
 import { useEffect, useErrorBoundary, useState } from "preact/hooks";
 import { Loading } from "./components/exception/loading.js";
@@ -35,13 +38,10 @@ import {
   NotConnectedAppMenu,
   NotificationCard,
 } from "./components/menu/index.js";
+import { useSessionContext } from "./context/session.js";
 import { useInstanceBankAccounts } from "./hooks/bank.js";
 import { useInstanceKYCDetails } from "./hooks/instance.js";
 import { usePreference } from "./hooks/preference.js";
-import {
-  DEFAULT_ADMIN_USERNAME,
-  useSessionContext,
-} from "./context/session.js";
 import InstanceCreatePage from "./paths/admin/create/index.js";
 import InstanceListPage from "./paths/admin/list/index.js";
 import BankAccountCreatePage from "./paths/instance/accounts/create/index.js";
@@ -73,10 +73,9 @@ import WebhookCreatePage from 
"./paths/instance/webhooks/create/index.js";
 import WebhookListPage from "./paths/instance/webhooks/list/index.js";
 import WebhookUpdatePage from "./paths/instance/webhooks/update/index.js";
 import { LoginPage } from "./paths/login/index.js";
-import NotFoundPage from "./paths/notfound/index.js";
+import { NotFoundPage } from "./paths/notfound/index.js";
 import { Settings } from "./paths/settings/index.js";
 import { Notification } from "./utils/types.js";
-import { createHashHistory } from "history";
 
 export enum InstancePaths {
   error = "/error",
@@ -122,7 +121,7 @@ export enum InstancePaths {
 }
 
 // eslint-disable-next-line @typescript-eslint/no-empty-function
-const noop = () => { };
+// const noop = () => { };
 
 export enum AdminPaths {
   list_instances = "/instances",
@@ -130,7 +129,7 @@ export enum AdminPaths {
   update_instance = "/instance/:id/update",
 }
 
-export interface Props { }
+export interface Props {}
 
 export const privatePages = {
   home: urlPattern(/\/home/, () => "#/home"),
@@ -143,7 +142,7 @@ export const publicPages = {
 
 const history = createHashHistory();
 export function Routing(_p: Props): VNode {
-  const { i18n } = useTranslationContext();
+  // const { i18n } = useTranslationContext();
   const { state } = useSessionContext();
 
   type GlobalNotifState =
@@ -158,85 +157,90 @@ export function Routing(_p: Props): VNode {
   const now = AbsoluteTime.now();
 
   const instance = useInstanceBankAccounts();
-  const accounts = !instance.ok ? undefined : instance.data.accounts;
+  const accounts =
+    !instance || instance instanceof TalerError || instance.type === "fail"
+      ? undefined
+      : instance.body;
   const shouldWarnAboutMissingBankAccounts =
-    !state.isAdmin && accounts !== undefined && accounts.length < 1 &&
+    !state.isAdmin &&
+    accounts !== undefined &&
+    accounts.length < 1 &&
     (AbsoluteTime.isNever(preference.hideMissingAccountUntil) ||
       AbsoluteTime.cmp(now, preference.hideMissingAccountUntil) > 1);
-  ;
+
   const shouldLogin =
     state.status === "loggedOut" || state.status === "expired";
 
-  function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) {
-    return function ServerErrorRedirectToImpl(
-      error: HttpError<TalerErrorDetail>,
-    ) {
-      if (error.type === ErrorType.TIMEOUT) {
-        setGlobalNotification({
-          message: i18n.str`The request to the backend take too long and was 
cancelled`,
-          description: i18n.str`Diagnostic from ${error.info.url} is 
"${error.message}"`,
-          type: "ERROR",
-          to,
-        });
-      } else {
-        setGlobalNotification({
-          message: i18n.str`The backend reported a problem: HTTP status 
#${error.status}`,
-          description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-          details:
-            error.type === ErrorType.CLIENT || error.type === ErrorType.SERVER
-              ? error.payload.hint
-              : undefined,
-          type: "ERROR",
-          to,
-        });
-      }
-      return <Redirect to={to} />;
-    };
-  }
+  // function ServerErrorRedirectTo(to: InstancePaths | AdminPaths) {
+  //   return function ServerErrorRedirectToImpl(
+  //     error: HttpError<TalerErrorDetail>,
+  //   ) {
+  //     if (error.type === ErrorType.TIMEOUT) {
+  //       setGlobalNotification({
+  //         message: i18n.str`The request to the backend take too long and 
was cancelled`,
+  //         description: i18n.str`Diagnostic from ${error.info.url} is 
"${error.message}"`,
+  //         type: "ERROR",
+  //         to,
+  //       });
+  //     } else {
+  //       setGlobalNotification({
+  //         message: i18n.str`The backend reported a problem: HTTP status 
#${error.status}`,
+  //         description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
+  //         details:
+  //           error.type === ErrorType.CLIENT || error.type === 
ErrorType.SERVER
+  //             ? error.payload.hint
+  //             : undefined,
+  //         type: "ERROR",
+  //         to,
+  //       });
+  //     }
+  //     return <Redirect to={to} />;
+  //   };
+  // }
 
   // const LoginPageAccessDeniend = onUnauthorized
-  const LoginPageAccessDenied = () => {
-    return (
-      <Fragment>
-        <NotificationCard
-          notification={{
-            message: i18n.str`Access denied`,
-            description: i18n.str`Session expired or password changed.`,
-            type: "ERROR",
-          }}
-        />
-        <LoginPage />
-      </Fragment>
-    );
-  };
-
-  function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<unknown>) {
-    return function IfAdminCreateDefaultOrImpl(props?: T) {
-      if (state.isAdmin && state.instance === DEFAULT_ADMIN_USERNAME) {
-        return (
-          <Fragment>
-            <NotificationCard
-              notification={{
-                message: i18n.str`No 'default' instance configured yet.`,
-                description: i18n.str`Create a 'default' instance to begin 
using the merchant backoffice.`,
-                type: "INFO",
-              }}
-            />
-            <InstanceCreatePage
-              forceId={DEFAULT_ADMIN_USERNAME}
-              onConfirm={() => {
-                route(InstancePaths.bank_list);
-              }}
-            />
-          </Fragment>
-        );
-      }
-      if (props) {
-        return <Next {...props} />;
-      }
-      return <Next />;
-    };
-  }
+  // const LoginPageAccessDenied = () => {
+  //   return (
+  //     <Fragment>
+  //       <NotificationCard
+  //         notification={{
+  //           message: i18n.str`Access denied`,
+  //           description: i18n.str`Session expired or password changed.`,
+  //           type: "ERROR",
+  //         }}
+  //       />
+  //       <LoginPage />
+  //     </Fragment>
+  //   );
+  // };
+
+  // function IfAdminCreateDefaultOr<T>(Next: FunctionComponent<unknown>) {
+  //   return function IfAdminCreateDefaultOrImpl(props?: T) {
+  //     if (state.isAdmin && state.instance === DEFAULT_ADMIN_USERNAME) {
+  //       return (
+  //         <Fragment>
+  //           <NotificationCard
+  //             notification={{
+  //               message: i18n.str`No 'default' instance configured yet.`,
+  //               description: i18n.str`Create a 'default' instance to begin 
using the merchant backoffice.`,
+  //               type: "INFO",
+  //             }}
+  //           />
+  //           <InstanceCreatePage
+  //             forceId={DEFAULT_ADMIN_USERNAME}
+  //             onConfirm={() => {
+  //               route(InstancePaths.bank_list);
+  //             }}
+  //           />
+  //         </Fragment>
+  //       );
+  //     }
+  //     if (props) {
+  //       return <Next {...props} />;
+  //     }
+  //     return <Next />;
+  //   };
+  // }
 
   if (shouldLogin) {
     return (
@@ -252,9 +256,11 @@ export function Routing(_p: Props): VNode {
       <Fragment>
         <Menu />
         <BankAccountBanner />
-        <BankAccountCreatePage onConfirm={() => {
-          route(InstancePaths.bank_list);
-        }} />
+        <BankAccountCreatePage
+          onConfirm={() => {
+            route(InstancePaths.bank_list);
+          }}
+        />
       </Fragment>
     );
   }
@@ -306,8 +312,8 @@ export function Routing(_p: Props): VNode {
             onUpdate={(id: string): void => {
               route(`/instance/${id}/update`);
             }}
-            onUnauthorized={LoginPageAccessDenied}
-            onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
+            // onUnauthorized={LoginPageAccessDenied}
+            // onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
           />
         )}
         {state.isAdmin && (
@@ -328,9 +334,9 @@ export function Routing(_p: Props): VNode {
             onConfirm={() => {
               route(AdminPaths.list_instances);
             }}
-            onUpdateError={ServerErrorRedirectTo(AdminPaths.list_instances)}
-            onLoadError={ServerErrorRedirectTo(AdminPaths.list_instances)}
-            onNotFound={NotFoundPage}
+            // onUpdateError={ServerErrorRedirectTo(AdminPaths.list_instances)}
+            // onLoadError={ServerErrorRedirectTo(AdminPaths.list_instances)}
+            // onNotFound={NotFoundPage}
           />
         )}
         {/**
@@ -345,10 +351,10 @@ export function Routing(_p: Props): VNode {
           onConfirm={() => {
             route(`/`);
           }}
-          onUpdateError={noop}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
+          // onUpdateError={noop}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
         />
         {/**
          * Update instance page
@@ -362,9 +368,9 @@ export function Routing(_p: Props): VNode {
           onCancel={() => {
             route(InstancePaths.order_list);
           }}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.error)}
         />
         {/**
          * Inventory pages
@@ -372,28 +378,28 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.inventory_list}
           component={ProductListPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
           onCreate={() => {
             route(InstancePaths.inventory_new);
           }}
           onSelect={(id: string) => {
             route(InstancePaths.inventory_update.replace(":pid", id));
           }}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
         />
         <Route
           path={InstancePaths.inventory_update}
           component={ProductUpdatePage}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)}
           onConfirm={() => {
             route(InstancePaths.inventory_list);
           }}
           onBack={() => {
             route(InstancePaths.inventory_list);
           }}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
         />
         <Route
           path={InstancePaths.inventory_new}
@@ -411,28 +417,28 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.bank_list}
           component={BankAccountListPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
           onCreate={() => {
             route(InstancePaths.bank_new);
           }}
           onSelect={(id: string) => {
             route(InstancePaths.bank_update.replace(":bid", id));
           }}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
         />
         <Route
           path={InstancePaths.bank_update}
           component={BankAccountUpdatePage}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)}
           onConfirm={() => {
             route(InstancePaths.bank_list);
           }}
           onBack={() => {
             route(InstancePaths.bank_list);
           }}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.inventory_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
         />
         <Route
           path={InstancePaths.bank_new}
@@ -456,16 +462,16 @@ export function Routing(_p: Props): VNode {
           onSelect={(id: string) => {
             route(InstancePaths.order_details.replace(":oid", id));
           }}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
         />
         <Route
           path={InstancePaths.order_details}
           component={OrderDetailsPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.order_list)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.order_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
           onBack={() => {
             route(InstancePaths.order_list);
           }}
@@ -486,9 +492,9 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.transfers_list}
           component={TransferListPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
           onCreate={() => {
             route(InstancePaths.transfers_new);
           }}
@@ -509,9 +515,9 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.webhooks_list}
           component={WebhookListPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
           onCreate={() => {
             route(InstancePaths.webhooks_new);
           }}
@@ -525,9 +531,9 @@ export function Routing(_p: Props): VNode {
           onConfirm={() => {
             route(InstancePaths.webhooks_list);
           }}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.webhooks_list)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.webhooks_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
           onBack={() => {
             route(InstancePaths.webhooks_list);
           }}
@@ -548,9 +554,9 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.otp_devices_list}
           component={ValidatorListPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
           onCreate={() => {
             route(InstancePaths.otp_devices_new);
           }}
@@ -564,9 +570,9 @@ export function Routing(_p: Props): VNode {
           onConfirm={() => {
             route(InstancePaths.otp_devices_list);
           }}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.otp_devices_list)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // 
onLoadError={ServerErrorRedirectTo(InstancePaths.otp_devices_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
           onBack={() => {
             route(InstancePaths.otp_devices_list);
           }}
@@ -587,9 +593,9 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.templates_list}
           component={TemplateListPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.settings)}
           onCreate={() => {
             route(InstancePaths.templates_new);
           }}
@@ -609,9 +615,9 @@ export function Routing(_p: Props): VNode {
           onConfirm={() => {
             route(InstancePaths.templates_list);
           }}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
           onBack={() => {
             route(InstancePaths.templates_list);
           }}
@@ -632,9 +638,9 @@ export function Routing(_p: Props): VNode {
           onOrderCreated={(id: string) => {
             route(InstancePaths.order_details.replace(":oid", id));
           }}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
           onBack={() => {
             route(InstancePaths.templates_list);
           }}
@@ -642,9 +648,9 @@ export function Routing(_p: Props): VNode {
         <Route
           path={InstancePaths.templates_qr}
           component={TemplateQrPage}
-          onUnauthorized={LoginPageAccessDenied}
-          onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
-          onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
+          // onUnauthorized={LoginPageAccessDenied}
+          // onLoadError={ServerErrorRedirectTo(InstancePaths.templates_list)}
+          // onNotFound={IfAdminCreateDefaultOr(NotFoundPage)}
           onBack={() => {
             route(InstancePaths.templates_list);
           }}
@@ -673,52 +679,52 @@ function AdminInstanceUpdatePage({
   id,
   ...rest
 }: { id: string } & InstanceUpdatePageProps): VNode {
-  const { i18n } = useTranslationContext();
+  // const { i18n } = useTranslationContext();
 
   return (
     <Fragment>
       <InstanceAdminUpdatePage
         {...rest}
         instanceId={id}
-        onLoadError={(error: HttpError<TalerErrorDetail>) => {
-          const notif =
-            error.type === ErrorType.TIMEOUT
-              ? {
-                message: i18n.str`The request to the backend take too long and 
was cancelled`,
-                description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-                type: "ERROR" as const,
-              }
-              : {
-                message: i18n.str`The backend reported a problem: HTTP status 
#${error.status}`,
-                description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
-                details:
-                  error.type === ErrorType.CLIENT ||
-                    error.type === ErrorType.SERVER
-                    ? error.payload.hint
-                    : undefined,
-                type: "ERROR" as const,
-              };
-          return (
-            <Fragment>
-              <NotificationCard notification={notif} />
-              <LoginPage />
-            </Fragment>
-          );
-        }}
-        onUnauthorized={() => {
-          return (
-            <Fragment>
-              <NotificationCard
-                notification={{
-                  message: i18n.str`Access denied`,
-                  description: i18n.str`The access token provided is invalid`,
-                  type: "ERROR",
-                }}
-              />
-              <LoginPage />
-            </Fragment>
-          );
-        }}
+        // onLoadError={(error: HttpError<TalerErrorDetail>) => {
+        //   const notif =
+        //     error.type === ErrorType.TIMEOUT
+        //       ? {
+        //         message: i18n.str`The request to the backend take too long 
and was cancelled`,
+        //         description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
+        //         type: "ERROR" as const,
+        //       }
+        //       : {
+        //         message: i18n.str`The backend reported a problem: HTTP 
status #${error.status}`,
+        //         description: i18n.str`Diagnostic from ${error.info.url} is 
'${error.message}'`,
+        //         details:
+        //           error.type === ErrorType.CLIENT ||
+        //             error.type === ErrorType.SERVER
+        //             ? error.payload.hint
+        //             : undefined,
+        //         type: "ERROR" as const,
+        //       };
+        //   return (
+        //     <Fragment>
+        //       <NotificationCard notification={notif} />
+        //       <LoginPage />
+        //     </Fragment>
+        //   );
+        // }}
+        // onUnauthorized={() => {
+        //   return (
+        //     <Fragment>
+        //       <NotificationCard
+        //         notification={{
+        //           message: i18n.str`Access denied`,
+        //           description: i18n.str`The access token provided is 
invalid`,
+        //           type: "ERROR",
+        //         }}
+        //       />
+        //       <LoginPage />
+        //     </Fragment>
+        //   );
+        // }}
       />
     </Fragment>
   );
@@ -727,7 +733,7 @@ function AdminInstanceUpdatePage({
 function BankAccountBanner(): VNode {
   const { i18n } = useTranslationContext();
 
-  const [_, updatePref] = usePreference();
+  const [, updatePref] = usePreference();
   const now = AbsoluteTime.now();
   const oneDay = { d_ms: 1000 * 60 * 60 * 24 };
   const tomorrow = AbsoluteTime.addDuration(now, oneDay);
@@ -740,7 +746,10 @@ function BankAccountBanner(): VNode {
         description: (
           <div>
             <p>
-              <i18n.Translate>Without this the merchant backend will refuse to 
create new orders.</i18n.Translate>
+              <i18n.Translate>
+                Without this the merchant backend will refuse to create new
+                orders.
+              </i18n.Translate>
             </p>
             <div class="buttons is-right">
               <button
@@ -754,10 +763,9 @@ function BankAccountBanner(): VNode {
         ),
       }}
     />
-  )
+  );
 }
 
-
 function KycBanner(): VNode {
   const kycStatus = useInstanceKYCDetails();
   const { i18n } = useTranslationContext();
@@ -766,7 +774,11 @@ function KycBanner(): VNode {
 
   const now = AbsoluteTime.now();
 
-  const needsToBeShown = kycStatus !== undefined && !(kycStatus instanceof 
TalerError) && kycStatus.type === "ok" && !!kycStatus.body;
+  const needsToBeShown =
+    kycStatus !== undefined &&
+    !(kycStatus instanceof TalerError) &&
+    kycStatus.type === "ok" &&
+    !!kycStatus.body;
 
   const hidden = AbsoluteTime.cmp(now, prefs.hideKycUntil) < 1;
   if (hidden || !needsToBeShown) return <Fragment />;
@@ -782,8 +794,10 @@ function KycBanner(): VNode {
         description: (
           <div>
             <p>
-              Some transfer are on hold until a KYC process is completed. Go to
-              the KYC section in the left panel for more information
+              <i18n.Translate>
+                Some transfer are on hold until a KYC process is completed. Go
+                to the KYC section in the left panel for more information
+              </i18n.Translate>
             </p>
             <div class="buttons is-right">
               <button
diff --git 
a/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx 
b/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx
index d637958cb..6dc1fadd6 100644
--- a/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx
+++ b/packages/merchant-backoffice-ui/src/components/picker/DatePicker.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, Component } from "preact";
+import { Component, h } from "preact";
 
 interface Props {
   closeFunction?: () => void;
@@ -64,7 +64,7 @@ export class DatePicker extends Component<Props, State> {
   getDaysByMonth(month: number, year: number) {
     const calendar = [];
 
-    const date = new Date(year, month, 1); // month to display
+    // const date = new Date(year, month, 1); // month to display
 
     const firstDay = new Date(year, month, 1).getDay(); // first weekday of 
month
     const lastDate = new Date(year, month + 1, 0).getDate(); // last date of 
month
diff --git a/packages/merchant-backoffice-ui/src/hooks/backend.ts 
b/packages/merchant-backoffice-ui/src/hooks/backend.ts
deleted file mode 100644
index 8c54f70db..000000000
--- a/packages/merchant-backoffice-ui/src/hooks/backend.ts
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2021-2024 Taler Systems S.A.
-
- GNU Taler is free software; you can redistribute it and/or modify it under the
- terms of the GNU General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @author Sebastian Javier Marchano (sebasjm)
- */
-
-import {
-  TalerErrorDetail,
-  TalerMerchantApi
-} from "@gnu-taler/taler-util";
-import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestError,
-  RequestOptions,
-  useApiContext
-} from "@gnu-taler/web-util/browser";
-import { useCallback, useEffect, useState } from "preact/hooks";
-import { useSWRConfig } from "swr";
-import { useSessionContext } from "../context/session.js";
-
-export function useMatchMutate(): (
-  re?: RegExp,
-  value?: unknown,
-) => Promise<any> {
-  const { cache, mutate } = useSWRConfig();
-
-  if (!(cache instanceof Map)) {
-    throw new Error(
-      "matchMutate requires the cache provider to be a Map instance",
-    );
-  }
-
-  return function matchRegexMutate(re?: RegExp) {
-    return mutate(
-      (key) => {
-        // evict if no key or regex === all
-        if (!key || !re) return true;
-        // match string
-        if (typeof key === "string" && re.test(key)) return true;
-        // record or object have the path at [0]
-        if (typeof key === "object" && re.test(key[0])) return true;
-        //key didn't match regex
-        return false;
-      },
-      undefined,
-      {
-        revalidate: true,
-      },
-    );
-  };
-}
-
-export function useBackendInstancesTestForAdmin(): HttpResponse<
-  TalerMerchantApi.InstancesResponse,
-  TalerErrorDetail
-> {
-  const { request } = useBackendBaseRequest();
-
-  type Type = TalerMerchantApi.InstancesResponse;
-
-  const [result, setResult] = useState<
-    HttpResponse<Type, TalerErrorDetail>
-  >({ loading: true });
-
-  useEffect(() => {
-    request<Type>(`/management/instances`)
-      .then((data) => setResult(data))
-      .catch((error: RequestError<TalerErrorDetail>) =>
-        setResult(error.cause),
-      );
-  }, [request]);
-
-  return result;
-}
-
-const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
-const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;
-
-export function useBackendConfig(): HttpResponse<
-  TalerMerchantApi.VersionResponse | undefined,
-  RequestError<TalerErrorDetail>
-> {
-  const { request } = useBackendBaseRequest();
-
-  type Type = TalerMerchantApi.VersionResponse;
-  type State = {
-    data: HttpResponse<Type, RequestError<TalerErrorDetail>>;
-    timer: number;
-  };
-  const [result, setResult] = useState<State>({
-    data: { loading: true },
-    timer: 0,
-  });
-
-  useEffect(() => {
-    if (result.timer) {
-      clearTimeout(result.timer);
-    }
-    function tryConfig(): void {
-      request<Type>(`/config`)
-        .then((data) => {
-          const timer: any = setTimeout(() => {
-            tryConfig();
-          }, CHECK_CONFIG_INTERVAL_OK);
-          setResult({ data, timer });
-        })
-        .catch((error) => {
-          const timer: any = setTimeout(() => {
-            tryConfig();
-          }, CHECK_CONFIG_INTERVAL_FAIL);
-          const data = error.cause;
-          setResult({ data, timer });
-        });
-    }
-    tryConfig();
-  }, [request]);
-
-  return result.data;
-}
-
-interface useBackendInstanceRequestType {
-  request: <T>(
-    endpoint: string,
-    options?: RequestOptions,
-  ) => Promise<HttpResponseOk<T>>;
-  fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
-  multiFetcher: <T>(params: [url: string[]]) => Promise<HttpResponseOk<T>[]>;
-  orderFetcher: <T>(
-    params: [
-      endpoint: string,
-      paid?: YesOrNo,
-      refunded?: YesOrNo,
-      wired?: YesOrNo,
-      searchDate?: Date,
-      delta?: number,
-    ],
-  ) => Promise<HttpResponseOk<T>>;
-  transferFetcher: <T>(
-    params: [
-      endpoint: string,
-      payto_uri?: string,
-      verified?: string,
-      position?: string,
-      delta?: number,
-    ],
-  ) => Promise<HttpResponseOk<T>>;
-  templateFetcher: <T>(
-    params: [endpoint: string, position?: string, delta?: number],
-  ) => Promise<HttpResponseOk<T>>;
-  webhookFetcher: <T>(
-    params: [endpoint: string, position?: string, delta?: number],
-  ) => Promise<HttpResponseOk<T>>;
-}
-interface useBackendBaseRequestType {
-  request: <T>(
-    endpoint: string,
-    options?: RequestOptions,
-  ) => Promise<HttpResponseOk<T>>;
-}
-
-type YesOrNo = "yes" | "no";
-
-/**
- *
- * @param root the request is intended to the base URL and no the instance URL
- * @returns request handler to
- */
-export function useBackendBaseRequest(): useBackendBaseRequestType {
-  const { request: requestHandler } = useApiContext();
-  const { state } = useSessionContext();
-  const token = state.token;
-  const baseUrl = state.backendUrl;
-
-  const request = useCallback(
-    function requestImpl<T>(
-      endpoint: string,
-      options: RequestOptions = {},
-    ): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { ...options, token })
-        .then((res) => {
-          return res;
-        })
-        .catch((err) => {
-          throw err;
-        });
-    },
-    [baseUrl, token],
-  );
-
-  return { request };
-}
-
-export function useBackendInstanceRequest(): useBackendInstanceRequestType {
-  const { request: requestHandler } = useApiContext();
-
-  const { state } = useSessionContext();
-  const token = state.token;
-  const baseUrl = state.backendUrl;
-
-  const request = useCallback(
-    function requestImpl<T>(
-      endpoint: string,
-      options: RequestOptions = {},
-    ): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { token, ...options });
-    },
-    [baseUrl, token],
-  );
-
-  const multiFetcher = useCallback(
-    function multiFetcherImpl<T>(
-      args: [endpoints: string[]],
-    ): Promise<HttpResponseOk<T>[]> {
-      const [endpoints] = args;
-      return Promise.all(
-        endpoints.map((endpoint) =>
-          requestHandler<T>(baseUrl, endpoint, { token }),
-        ),
-      );
-    },
-    [baseUrl, token],
-  );
-
-  const fetcher = useCallback(
-    function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { token });
-    },
-    [baseUrl, token],
-  );
-
-  const orderFetcher = useCallback(
-    function orderFetcherImpl<T>(
-      args: [
-        endpoint: string,
-        paid?: YesOrNo,
-        refunded?: YesOrNo,
-        wired?: YesOrNo,
-        searchDate?: Date,
-        delta?: number,
-      ],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, paid, refunded, wired, searchDate, delta] = args;
-      const date_s =
-        delta && delta < 0 && searchDate
-          ? Math.floor(searchDate.getTime() / 1000) + 1
-          : searchDate !== undefined
-            ? Math.floor(searchDate.getTime() / 1000)
-            : undefined;
-      const params: any = {};
-      if (paid !== undefined) params.paid = paid;
-      if (delta !== undefined) params.delta = delta;
-      if (refunded !== undefined) params.refunded = refunded;
-      if (wired !== undefined) params.wired = wired;
-      if (date_s !== undefined) params.date_s = date_s;
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { orders: [] } as T,
-        });
-      }
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  const transferFetcher = useCallback(
-    function transferFetcherImpl<T>(
-      args: [
-        endpoint: string,
-        payto_uri?: string,
-        verified?: string,
-        position?: string,
-        delta?: number,
-      ],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, payto_uri, verified, position, delta] = args;
-      const params: any = {};
-      if (payto_uri !== undefined) params.payto_uri = payto_uri;
-      if (verified !== undefined) params.verified = verified;
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { transfers: [] } as T,
-        });
-      }
-      if (delta !== undefined) {
-        params.limit = delta;
-      }
-      if (position !== undefined) params.offset = position;
-
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  const templateFetcher = useCallback(
-    function templateFetcherImpl<T>(
-      args: [endpoint: string, position?: string, delta?: number],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, position, delta] = args;
-      const params: any = {};
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { templates: [] } as T,
-        });
-      }
-      if (delta !== undefined) {
-        params.limit = delta;
-      }
-      if (position !== undefined) params.offset = position;
-
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  const webhookFetcher = useCallback(
-    function webhookFetcherImpl<T>(
-      args: [endpoint: string, position?: string, delta?: number],
-    ): Promise<HttpResponseOk<T>> {
-      const [endpoint, position, delta] = args;
-      const params: any = {};
-      if (delta === 0) {
-        //in this case we can already assume the response
-        //and avoid network
-        return Promise.resolve({
-          ok: true,
-          data: { webhooks: [] } as T,
-        });
-      }
-      if (delta !== undefined) {
-        params.limit = delta;
-      }
-      if (position !== undefined) params.offset = position;
-
-      return requestHandler<T>(baseUrl, endpoint, { params, token });
-    },
-    [baseUrl, token],
-  );
-
-  return {
-    request,
-    fetcher,
-    multiFetcher,
-    orderFetcher,
-    transferFetcher,
-    templateFetcher,
-    webhookFetcher,
-  };
-}
diff --git a/packages/merchant-backoffice-ui/src/hooks/bank.ts 
b/packages/merchant-backoffice-ui/src/hooks/bank.ts
index 9ad4c3069..e1f2638ed 100644
--- a/packages/merchant-backoffice-ui/src/hooks/bank.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/bank.ts
@@ -14,198 +14,97 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook, mutate } from "swr";
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// const MOCKED_ACCOUNTS: Record<string, TalerMerchantApi.AccountAddDetails> = 
{
-//   "hwire1": {
-//     h_wire: "hwire1",
-//     payto_uri: "payto://fake/iban/123",
-//     salt: "qwe",
-//   },
-//   "hwire2": {
-//     h_wire: "hwire2",
-//     payto_uri: "payto://fake/iban/123",
-//     salt: "qwe2",
-//   },
-// }
-
-// export function useBankAccountAPI(): BankAccountAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createBankAccount = async (
-//     data: TalerMerchantApi.AccountAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_ACCOUNTS[data.h_wire] = data
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/accounts`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/accounts.*/);
-//     return res;
-//   };
-
-//   const updateBankAccount = async (
-//     h_wire: string,
-//     data: TalerMerchantApi.AccountPatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_ACCOUNTS[h_wire].credit_facade_credentials = 
data.credit_facade_credentials
-//     // MOCKED_ACCOUNTS[h_wire].credit_facade_url = data.credit_facade_url
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/accounts/${h_wire}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/accounts.*/);
-//     return res;
-//   };
-
-//   const deleteBankAccount = async (
-//     h_wire: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // delete MOCKED_ACCOUNTS[h_wire]
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/accounts/${h_wire}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/accounts.*/);
-//     return res;
-//   };
-
-//   return {
-//     createBankAccount,
-//     updateBankAccount,
-//     deleteBankAccount,
-//   };
-// }
-
-// export interface BankAccountAPI {
-//   createBankAccount: (
-//     data: TalerMerchantApi.AccountAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateBankAccount: (
-//     id: string,
-//     data: TalerMerchantApi.AccountPatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteBankAccount: (id: string) => Promise<HttpResponseOk<void>>;
-// }
-
 export interface InstanceBankAccountFilter {
 }
 
 export function revalidateInstanceBankAccounts() {
-  // mutate(key => key instanceof)
-  return mutate((key) => Array.isArray(key) && key[key.length - 1] === 
"/private/accounts", undefined, { revalidate: true });
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listBankAccounts",
+    undefined,
+    { revalidate: true },
+  );
 }
-export function useInstanceBankAccounts(
-  args?: InstanceBankAccountFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.AccountsSummaryResponse,
-  TalerErrorDetail
-> {
-
-  const { fetcher } = useBackendInstanceRequest();
-
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.AccountsSummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/accounts`], fetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.AccountsSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData /*, beforeData*/]);
-
-  if (afterError) return afterError.cause;
-
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.accounts.length < totalAfter;
-  const isReachingStart = false;
+export function useInstanceBankAccounts() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listBankAccounts(token, {
+      limit: 5,
+      offset: bid,
+      order: "dec",
+    });
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listBankAccounts">,
+    TalerHttpError
+  >([session.token, offset, "listBankAccounts"], fetcher);
 
+  const isLastPage =
+    data && data.type === "ok" && data.body.accounts.length <= PAGE_SIZE;
+  const isFirstPage = !offset;
+
+  const result =
+    data && data.type == "ok" ? structuredClone(data.body.accounts) : [];
+  if (result.length == PAGE_SIZE + 1) {
+    result.pop();
+  }
   const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.accounts.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = `${afterData.data.accounts[afterData.data.accounts.length 
- 1]
-          .h_wire
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
+    result,
+    isLastPage,
+    isFirstPage,
+    loadNext: () => {
+      if (!result.length) return;
+      setOffset(result[result.length - 1].h_wire);
     },
-    loadMorePrev: () => {
+    loadFirst: () => {
+      setOffset(undefined);
     },
   };
 
-  const accounts = !afterData ? [] : (afterData || lastAfter).data.accounts;
-  if (loadingAfter /* || loadingBefore */)
-    return { loading: true, data: { accounts } };
-  if (/*beforeData &&*/ afterData) {
-    return { ok: true, data: { accounts }, ...pagination };
-  }
-  return { loading: true };
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  return buildPaginatedResult(data.body.accounts, offset, setOffset, (d) => 
d.h_wire)
+}
+
+export function revalidateBankAccountDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"getBankAccountDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useBankAccountDetails(h_wire: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useBankAccountDetails(
-  h_wire: string,
-): HttpResponse<
-  TalerMerchantApi.BankAccountEntry,
-  TalerErrorDetail
-> {
-  // return {
-  //   ok: true,
-  //   data: {
-  //     ...MOCKED_ACCOUNTS[h_wire],
-  //     active: true,
-  //   }
-  // }
-  const { fetcher } = useBackendInstanceRequest();
-
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.BankAccountEntry>,
-    RequestError<TalerErrorDetail>
-  >([`/private/accounts/${h_wire}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (isValidating) return { loading: true, data: data?.data };
-  if (data) {
-    return data;
+  async function fetcher([token, wireId]: [AccessToken, string]) {
+    return await management.getBankAccountDetails(token, wireId);
   }
-  if (error) return error.cause;
-  return { loading: true };
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getBankAccountDetails">,
+    TalerHttpError
+  >([session.token, h_wire, "getBankAccountDetails"], fetcher);
+
+  if (data) return data;
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/instance.ts 
b/packages/merchant-backoffice-ui/src/hooks/instance.ts
index e8a431ae5..cc907bd8f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/instance.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/instance.ts
@@ -14,18 +14,11 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestError,
   useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import {
-  useBackendBaseRequest,
-  useBackendInstanceRequest
-} from "./backend.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { AccessToken, TalerErrorDetail, TalerHttpError, TalerMerchantApi, 
TalerMerchantInstanceResultByMethod, TalerMerchantManagementResultByMethod, 
TalerMerchantResultByMethod } from "@gnu-taler/taler-util";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook, mutate } from "swr";
 import { useSessionContext } from "../context/session.js";
 const useSWR = _useSWR as unknown as SWRHook;
@@ -39,28 +32,6 @@ export function revalidateInstanceDetails() {
   );
 }
 export function useInstanceDetails() {
-  // const { fetcher } = useBackendInstanceRequest();
-
-  // const { data, error, isValidating } = useSWR<
-  //   HttpResponseOk<TalerMerchantApi.QueryInstancesResponse>,
-  //   RequestError<TalerErrorDetail>
-  // >([`/private/`], fetcher, {
-  //   refreshInterval: 0,
-  //   refreshWhenHidden: false,
-  //   revalidateOnFocus: false,
-  //   revalidateOnReconnect: false,
-  //   refreshWhenOffline: false,
-  //   revalidateIfStale: false,
-  //   errorRetryCount: 0,
-  //   errorRetryInterval: 1,
-  //   shouldRetryOnError: false,
-  // });
-
-  // if (isValidating) return { loading: true, data: data?.data };
-  // if (data) return data;
-  // if (error) return error.cause;
-  // return { loading: true };
-
   const { state: session } = useSessionContext();
   const { lib: { management } } = useMerchantApiContext();
 
@@ -78,9 +49,6 @@ export function useInstanceDetails() {
   return undefined;
 }
 
-// type KYCStatus =
-//   | { type: "ok" }
-//   | { type: "redirect"; status: TalerMerchantApi.AccountKycRedirects };
 export function revalidateInstanceKYCDetails() {
   return mutate(
     (key) => Array.isArray(key) && key[key.length - 1] === 
"getCurrentIntanceKycStatus",
@@ -89,32 +57,6 @@ export function revalidateInstanceKYCDetails() {
   );
 }
 export function useInstanceKYCDetails() {
-  // const { fetcher } = useBackendInstanceRequest();
-
-  // const { data, error } = useSWR<
-  //   HttpResponseOk<TalerMerchantApi.AccountKycRedirects>,
-  //   RequestError<TalerErrorDetail>
-  // >([`/private/kyc`], fetcher, {
-  //   refreshInterval: 60 * 1000,
-  //   refreshWhenHidden: false,
-  //   revalidateOnFocus: false,
-  //   revalidateIfStale: false,
-  //   revalidateOnMount: false,
-  //   revalidateOnReconnect: false,
-  //   refreshWhenOffline: false,
-  //   errorRetryCount: 0,
-  //   errorRetryInterval: 1,
-  //   shouldRetryOnError: false,
-  // });
-
-  // if (data) {
-  //   if (data.info?.status === 202)
-  //     return { ok: true, data: { type: "redirect", status: data.data } };
-  //   return { ok: true, data: { type: "ok" } };
-  // }
-  // if (error) return error.cause;
-  // return { loading: true };
-
   const { state: session } = useSessionContext();
   const { lib: { management } } = useMerchantApiContext();
 
diff --git a/packages/merchant-backoffice-ui/src/hooks/order.test.ts 
b/packages/merchant-backoffice-ui/src/hooks/order.test.ts
index 3d4f2809f..243415bdd 100644
--- a/packages/merchant-backoffice-ui/src/hooks/order.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/order.test.ts
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { AmountString, TalerMerchantApi } from "@gnu-taler/taler-util";
+import { AbsoluteTime, AmountString, TalerMerchantApi } from 
"@gnu-taler/taler-util";
 import * as tests from "@gnu-taler/web-util/testing";
 import { expect } from "chai";
 import { useInstanceOrders, useOrderDetails } from "./order.js";
@@ -45,31 +45,31 @@ describe("order api interaction with listing", () => {
       },
     });
 
-    const newDate = (d: Date) => {
+    const newDate = (_d: string | undefined) => {
       //console.log("new date", d);
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
-        const query = useInstanceOrders({ paid: "yes" }, newDate);
+        const query = useInstanceOrders({ paid: true }, newDate);
         const { lib: api } = useMerchantApiContext()
         return { query, api };
       },
       {},
       [
-        ({ query, api }) => {
-          expect(query.loading).true;
+        ({ query }) => {
+          expect(query).undefined;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [{ order_id: "1" }, { order_id: "2" }],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [{ order_id: "1" }, { order_id: "2" }],
+          // });
 
           env.addRequestExpectation(API_CREATE_ORDER, {
             request: {
@@ -95,12 +95,12 @@ describe("order api interaction with listing", () => {
             result: "ok",
           });
 
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [{ order_id: "1" }, { order_id: "2" }, { order_id: "3" }],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [{ order_id: "1" }, { order_id: "2" }, { order_id: "3" 
}],
+          // });
         },
       ],
       env.buildTestingContext(),
@@ -123,38 +123,38 @@ describe("order api interaction with listing", () => {
       },
     });
 
-    const newDate = (d: Date) => {
+    const newDate = (_d: string | undefined) => {
       //console.log("new date", d);
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
-        const query = useInstanceOrders({ paid: "yes" }, newDate);
+        const query = useInstanceOrders({ paid: true }, newDate);
         const { lib: api } = useMerchantApiContext()
         return { query, api };
       },
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
 
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [
-              {
-                order_id: "1",
-                amount: "EUR:12",
-                refundable: true,
-              },
-            ],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [
+          //     {
+          //       order_id: "1",
+          //       amount: "EUR:12",
+          //       refundable: true,
+          //     },
+          //   ],
+          // });
           env.addRequestExpectation(API_REFUND_ORDER_BY_ID("1"), {
             request: {
               reason: "double pay",
@@ -181,18 +181,18 @@ describe("order api interaction with listing", () => {
             result: "ok",
           });
 
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [
-              {
-                order_id: "1",
-                amount: "EUR:12",
-                refundable: false,
-              },
-            ],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [
+          //     {
+          //       order_id: "1",
+          //       amount: "EUR:12",
+          //       refundable: false,
+          //     },
+          //   ],
+          // });
         },
       ],
       env.buildTestingContext(),
@@ -212,31 +212,31 @@ describe("order api interaction with listing", () => {
       },
     });
 
-    const newDate = (d: Date) => {
+    const newDate = (_d: string | undefined) => {
       //console.log("new date", d);
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
-        const query = useInstanceOrders({ paid: "yes" }, newDate);
+        const query = useInstanceOrders({ paid: true }, newDate);
         const { lib: api } = useMerchantApiContext()
         return { query, api };
       },
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [{ order_id: "1" }, { order_id: "2" }],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [{ order_id: "1" }, { order_id: "2" }],
+          // });
 
           env.addRequestExpectation(API_DELETE_ORDER("1"), {});
 
@@ -253,12 +253,12 @@ describe("order api interaction with listing", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [{ order_id: "2" }],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [{ order_id: "2" }],
+          // });
         },
       ],
       env.buildTestingContext(),
@@ -280,10 +280,6 @@ describe("order api interaction with details", () => {
       } as unknown as TalerMerchantApi.CheckPaymentPaidResponse,
     });
 
-    const newDate = (d: Date) => {
-      //console.log("new date", d);
-    };
-
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
         const query = useOrderDetails("1");
@@ -293,19 +289,19 @@ describe("order api interaction with details", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).false;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            summary: "description",
-            refund_amount: "EUR:0",
-          });
+          // expect(query.loading).false;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   summary: "description",
+          //   refund_amount: "EUR:0",
+          // });
           env.addRequestExpectation(API_REFUND_ORDER_BY_ID("1"), {
             request: {
               reason: "double pay",
@@ -329,13 +325,13 @@ describe("order api interaction with details", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).false;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            summary: "description",
-            refund_amount: "EUR:1",
-          });
+          // expect(query.loading).false;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   summary: "description",
+          //   refund_amount: "EUR:1",
+          // });
         },
       ],
       env.buildTestingContext(),
@@ -356,10 +352,6 @@ describe("order api interaction with details", () => {
       } as unknown as TalerMerchantApi.CheckPaymentPaidResponse,
     });
 
-    const newDate = (d: Date) => {
-      //console.log("new date", d);
-    };
-
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
         const query = useOrderDetails("1");
@@ -369,19 +361,19 @@ describe("order api interaction with details", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).false;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            summary: "description",
-            refund_amount: "EUR:0",
-          });
+          // expect(query.loading).false;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   summary: "description",
+          //   refund_amount: "EUR:0",
+          // });
           env.addRequestExpectation(API_FORGET_ORDER_BY_ID("1"), {
             request: {
               fields: ["$.summary"],
@@ -402,12 +394,12 @@ describe("order api interaction with details", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).false;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            summary: undefined,
-          });
+          // expect(query.loading).false;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   summary: undefined,
+          // });
         },
       ],
       env.buildTestingContext(),
@@ -434,38 +426,35 @@ describe("order listing pagination", () => {
       },
     });
 
-    const newDate = (d: Date) => {
+    const newDate = (_d: string | undefined) => {
       //console.log("new date", d);
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
         const date = new Date(12000);
-        const query = useInstanceOrders({ wired: "yes", date }, newDate);
+        const query = useInstanceOrders({ wired: true, date: 
AbsoluteTime.fromMilliseconds(date.getTime()) }, newDate);
         const { lib: api } = useMerchantApiContext()
         return { query, api };
       },
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [{ order_id: "1" }, { order_id: "2" }],
-          });
-          expect(query.isReachingEnd).true;
-          expect(query.isReachingStart).true;
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [{ order_id: "1" }, { order_id: "2" }],
+          // });
+          // expect(query.isReachingEnd).true;
+          // expect(query.isReachingStart).true;
 
-          // should not trigger new state update or query
-          query.loadMore();
-          query.loadMorePrev();
         },
       ],
       env.buildTestingContext(),
@@ -484,7 +473,6 @@ describe("order listing pagination", () => {
     const ordersFrom20to40 = Array.from({ length: 20 }).map((e, i) => ({
       order_id: String(i + 20),
     }));
-    const ordersFrom20to0 = [...ordersFrom0to20].reverse();
 
     env.addRequestExpectation(API_LIST_ORDERS, {
       qparam: { delta: 20, wired: "yes", date_s: 12 },
@@ -500,34 +488,34 @@ describe("order listing pagination", () => {
       },
     });
 
-    const newDate = (d: Date) => {
+    const newDate = (_d: string | undefined) => {
       //console.log("new date", d);
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
         const date = new Date(12000);
-        const query = useInstanceOrders({ wired: "yes", date }, newDate);
+        const query = useInstanceOrders({ wired: true, date: 
AbsoluteTime.fromMilliseconds(date.getTime()) }, newDate);
         const { lib: api } = useMerchantApiContext()
         return { query, api };
       },
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [...ordersFrom20to0, ...ordersFrom20to40],
-          });
-          expect(query.isReachingEnd).false;
-          expect(query.isReachingStart).false;
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [...ordersFrom20to0, ...ordersFrom20to40],
+          // });
+          // expect(query.isReachingEnd).false;
+          // expect(query.isReachingStart).false;
 
           env.addRequestExpectation(API_LIST_ORDERS, {
             qparam: { delta: -40, wired: "yes", date_s: 13 },
@@ -536,25 +524,25 @@ describe("order listing pagination", () => {
             },
           });
 
-          query.loadMore();
+          // query.loadMore();
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [
-              ...ordersFrom20to0,
-              ...ordersFrom20to40,
-              { order_id: "41" },
-            ],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [
+          //     ...ordersFrom20to0,
+          //     ...ordersFrom20to40,
+          //     { order_id: "41" },
+          //   ],
+          // });
 
           env.addRequestExpectation(API_LIST_ORDERS, {
             qparam: { delta: 40, wired: "yes", date_s: 12 },
@@ -563,26 +551,26 @@ describe("order listing pagination", () => {
             },
           });
 
-          query.loadMorePrev();
+          // query.loadMorePrev();
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            orders: [
-              { order_id: "-1" },
-              ...ordersFrom20to0,
-              ...ordersFrom20to40,
-              { order_id: "41" },
-            ],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   orders: [
+          //     { order_id: "-1" },
+          //     ...ordersFrom20to0,
+          //     ...ordersFrom20to40,
+          //     { order_id: "41" },
+          //   ],
+          // });
         },
       ],
       env.buildTestingContext(),
diff --git a/packages/merchant-backoffice-ui/src/hooks/order.ts 
b/packages/merchant-backoffice-ui/src/hooks/order.ts
index 40932ac62..1ce76bf58 100644
--- a/packages/merchant-backoffice-ui/src/hooks/order.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/order.ts
@@ -14,276 +14,81 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest } from "./backend.js";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AbsoluteTime, AccessToken, TalerHttpError, 
TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export interface OrderAPI {
-//   //FIXME: add OutOfStockResponse on 410
-//   createOrder: (
-//     data: TalerMerchantApi.PostOrderRequest,
-//   ) => Promise<HttpResponseOk<TalerMerchantApi.PostOrderResponse>>;
-//   forgetOrder: (
-//     id: string,
-//     data: TalerMerchantApi.ForgetRequest,
-//   ) => Promise<HttpResponseOk<void>>;
-//   refundOrder: (
-//     id: string,
-//     data: TalerMerchantApi.RefundRequest,
-//   ) => Promise<HttpResponseOk<TalerMerchantApi.MerchantRefundResponse>>;
-//   deleteOrder: (id: string) => Promise<HttpResponseOk<void>>;
-//   getPaymentURL: (id: string) => Promise<HttpResponseOk<string>>;
-// }
 
-type YesOrNo = "yes" | "no";
 
-// export function useOrderAPI(): OrderAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createOrder = async (
-//     data: TalerMerchantApi.PostOrderRequest,
-//   ): Promise<HttpResponseOk<TalerMerchantApi.PostOrderResponse>> => {
-//     const res = await request<TalerMerchantApi.PostOrderResponse>(
-//       `/private/orders`,
-//       {
-//         method: "POST",
-//         data,
-//       },
-//     );
-//     await mutateAll(/.*private\/orders.*/);
-//     // mutate('')
-//     return res;
-//   };
-//   const refundOrder = async (
-//     orderId: string,
-//     data: TalerMerchantApi.RefundRequest,
-//   ): Promise<HttpResponseOk<TalerMerchantApi.MerchantRefundResponse>> => {
-//     mutateAll(/@"\/private\/orders"@/);
-//     const res = request<TalerMerchantApi.MerchantRefundResponse>(
-//       `/private/orders/${orderId}/refund`,
-//       {
-//         method: "POST",
-//         data,
-//       },
-//     );
-
-//     // order list returns refundable information, so we must evict 
everything
-//     await mutateAll(/.*private\/orders.*/);
-//     return res;
-//   };
-
-//   const forgetOrder = async (
-//     orderId: string,
-//     data: TalerMerchantApi.ForgetRequest,
-//   ): Promise<HttpResponseOk<void>> => {
-//     mutateAll(/@"\/private\/orders"@/);
-//     const res = request<void>(`/private/orders/${orderId}/forget`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     // we may be forgetting some fields that are pare of the listing, so we 
must evict everything
-//     await mutateAll(/.*private\/orders.*/);
-//     return res;
-//   };
-//   const deleteOrder = async (
-//     orderId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     mutateAll(/@"\/private\/orders"@/);
-//     const res = request<void>(`/private/orders/${orderId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/orders.*/);
-//     return res;
-//   };
-
-//   const getPaymentURL = async (
-//     orderId: string,
-//   ): Promise<HttpResponseOk<string>> => {
-//     return request<TalerMerchantApi.MerchantOrderStatusResponse>(
-//       `/private/orders/${orderId}`,
-//       {
-//         method: "GET",
-//       },
-//     ).then((res) => {
-//       const url =
-//         res.data.order_status === "unpaid"
-//           ? res.data.taler_pay_uri
-//           : res.data.contract_terms.fulfillment_url;
-//       const response: HttpResponseOk<string> = res as any;
-//       response.data = url || "";
-//       return response;
-//     });
-//   };
-
-//   return { createOrder, forgetOrder, deleteOrder, refundOrder, 
getPaymentURL };
-// }
+export function revalidateOrderDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getOrderDetails",
+    undefined,
+    { revalidate: true },
+  );
+}
+export function useOrderDetails(oderId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useOrderDetails(
-  oderId: string,
-): HttpResponse<
-  TalerMerchantApi.MerchantOrderStatusResponse,
-  TalerErrorDetail
-> {
-  const { fetcher } = useBackendInstanceRequest();
+  async function fetcher([dId, token]: [string, AccessToken]) {
+    return await management.getOrderDetails(token, dId);
+  }
 
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.MerchantOrderStatusResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/orders/${oderId}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getOrderDetails">,
+    TalerHttpError
+  >([oderId, session.token, "getOrderDetails"], fetcher);
 
-  if (isValidating) return { loading: true, data: data?.data };
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
 
 export interface InstanceOrderFilter {
-  paid?: YesOrNo;
-  refunded?: YesOrNo;
-  wired?: YesOrNo;
-  date?: Date;
+  paid?: boolean;
+  refunded?: boolean;
+  wired?: boolean;
+  date?: AbsoluteTime;
+  position?: string;
 }
 
 export function useInstanceOrders(
   args?: InstanceOrderFilter,
-  updateFilter?: (d: Date) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.OrderHistory,
-  TalerErrorDetail
-> {
-  const { orderFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.date ? pageBefore * PAGE_SIZE : 0;
-
-  /**
-   * FIXME: this can be cleaned up a little
-   *
-   * the logic of double query should be inside the orderFetch so from the 
hook perspective and cache
-   * is just one query and one error status
-   */
-  const {
-    data: beforeData,
-    error: beforeError,
-    isValidating: loadingBefore,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OrderHistory>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/orders`,
-      args?.paid,
-      args?.refunded,
-      args?.wired,
-      args?.date,
-      totalBefore,
-    ],
-    orderFetcher,
-  );
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OrderHistory>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/orders`,
-      args?.paid,
-      args?.refunded,
-      args?.wired,
-      args?.date,
-      -totalAfter,
-    ],
-    orderFetcher,
-  );
-
-  //this will save last result
-  const [lastBefore, setLastBefore] = useState<
-    HttpResponse<
-      TalerMerchantApi.OrderHistory,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.OrderHistory,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-    if (beforeData) setLastBefore(beforeData);
-  }, [afterData, beforeData]);
-
-  if (beforeError) return beforeError.cause;
-  if (afterError) return afterError.cause;
+  updatePosition: (d: string | undefined) => void = () => { },
+) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  // const [offset, setOffset] = useState<string | undefined>(args?.position);
+
+  async function fetcher([token, o, p, r, w, d]: [AccessToken, string, 
boolean, boolean, boolean, AbsoluteTime]) {
+    return await management.listOrders(token, {
+      limit: PAGE_SIZE,
+      offset: o,
+      order: "dec",
+      paid: p,
+      refunded: r,
+      wired: w,
+      date: d,
+    });
+  }
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd = afterData && afterData.data.orders.length < totalAfter;
-  const isReachingStart =
-    args?.date === undefined ||
-    (beforeData && beforeData.data.orders.length < totalBefore);
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listOrders">,
+    TalerHttpError
+  >([session.token, args?.position, args?.paid, args?.refunded, args?.wired, 
args?.date, "listOrders"], fetcher);
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.orders.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from =
-          afterData.data.orders[afterData.data.orders.length - 
1].timestamp.t_s;
-        if (from && from !== "never" && updateFilter)
-          updateFilter(new Date(from * 1000));
-      }
-    },
-    loadMorePrev: () => {
-      if (!beforeData || isReachingStart) return;
-      if (beforeData.data.orders.length < MAX_RESULT_SIZE) {
-        setPageBefore(pageBefore + 1);
-      } else if (beforeData) {
-        const from =
-          beforeData.data.orders[beforeData.data.orders.length - 1].timestamp
-            .t_s;
-        if (from && from !== "never" && updateFilter)
-          updateFilter(new Date(from * 1000));
-      }
-    },
-  };
+  if (error) return error;
+  if (data === undefined) return undefined;
+  // if (data.type !== "ok") return data;
 
-  const orders =
-    !beforeData || !afterData
-      ? []
-      : (beforeData || lastBefore).data.orders
-        .slice()
-        .reverse()
-        .concat((afterData || lastAfter).data.orders);
-  if (loadingAfter || loadingBefore) return { loading: true, data: { orders } 
};
-  if (beforeData && afterData) {
-    return { ok: true, data: { orders }, ...pagination };
-  }
-  return { loading: true };
+  return buildPaginatedResult(data.body.orders, args?.position, 
updatePosition, (d) => String(d.row_id))
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/otp.ts 
b/packages/merchant-backoffice-ui/src/hooks/otp.ts
index 36db2ea90..69e4a0f4f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/otp.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/otp.ts
@@ -14,195 +14,72 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useOtpDeviceAPI(): OtpDeviceAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createOtpDevice = async (
-//     data: TalerMerchantApi.OtpDeviceAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_DEVICES[data.otp_device_id] = data
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/otp-devices`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/otp-devices.*/);
-//     return res;
-//   };
-
-//   const updateOtpDevice = async (
-//     deviceId: string,
-//     data: TalerMerchantApi.OtpDevicePatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // MOCKED_DEVICES[deviceId].otp_algorithm = data.otp_algorithm
-//     // MOCKED_DEVICES[deviceId].otp_ctr = data.otp_ctr
-//     // MOCKED_DEVICES[deviceId].otp_device_description = 
data.otp_device_description
-//     // MOCKED_DEVICES[deviceId].otp_key = data.otp_key
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/otp-devices/${deviceId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/otp-devices.*/);
-//     return res;
-//   };
-
-//   const deleteOtpDevice = async (
-//     deviceId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     // delete MOCKED_DEVICES[deviceId]
-//     // return Promise.resolve({ ok: true, data: undefined });
-//     const res = await request<void>(`/private/otp-devices/${deviceId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/otp-devices.*/);
-//     return res;
-//   };
-
-//   return {
-//     createOtpDevice,
-//     updateOtpDevice,
-//     deleteOtpDevice,
-//   };
-// }
-
-// export interface OtpDeviceAPI {
-//   createOtpDevice: (
-//     data: TalerMerchantApi.OtpDeviceAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateOtpDevice: (
-//     id: string,
-//     data: TalerMerchantApi.OtpDevicePatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteOtpDevice: (id: string) => Promise<HttpResponseOk<void>>;
-// }
-
-export interface InstanceOtpDeviceFilter {
+export function revalidateInstanceOtpDevices() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listOtpDevices",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useInstanceOtpDevices() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listOtpDevices(token, {
+      limit: PAGE_SIZE,
+      offset: bid,
+      order: "dec",
+    });
+  }
 
-export function useInstanceOtpDevices(
-  args?: InstanceOtpDeviceFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.OtpDeviceSummaryResponse,
-  TalerErrorDetail
-> {
-  // return {
-  //   ok: true,
-  //   loadMore: () => { },
-  //   loadMorePrev: () => { },
-  //   data: {
-  //     otp_devices: Object.values(MOCKED_DEVICES).map(d => ({
-  //       device_description: d.otp_device_description,
-  //       otp_device_id: d.otp_device_id
-  //     }))
-  //   }
-  // }
-
-  const { fetcher } = useBackendInstanceRequest();
-
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OtpDeviceSummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/otp-devices`], fetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.OtpDeviceSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData /*, beforeData*/]);
-
-  if (afterError) return afterError.cause;
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listOtpDevices">,
+    TalerHttpError
+  >([session.token, offset, "listOtpDevices"], fetcher);
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.otp_devices.length < totalAfter;
-  const isReachingStart = true;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.otp_devices.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = 
`${afterData.data.otp_devices[afterData.data.otp_devices.length - 1]
-          .otp_device_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-    loadMorePrev: () => {
-    },
-  };
+  return buildPaginatedResult(data.body.otp_devices, offset, setOffset, (d) => 
d.otp_device_id)
+}
 
-  const otp_devices = !afterData ? [] : (afterData || 
lastAfter).data.otp_devices;
-  if (loadingAfter /* || loadingBefore */)
-    return { loading: true, data: { otp_devices } };
-  if (/*beforeData &&*/ afterData) {
-    return { ok: true, data: { otp_devices }, ...pagination };
-  }
-  return { loading: true };
+export function revalidateOtpDeviceDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"getOtpDeviceDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useOtpDeviceDetails(deviceId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useOtpDeviceDetails(
-  deviceId: string,
-): HttpResponse<
-  TalerMerchantApi.OtpDeviceDetails,
-  TalerErrorDetail
-> {
-  // return {
-  //   ok: true,
-  //   data: {
-  //     device_description: MOCKED_DEVICES[deviceId].otp_device_description,
-  //     otp_algorithm: MOCKED_DEVICES[deviceId].otp_algorithm,
-  //     otp_ctr: MOCKED_DEVICES[deviceId].otp_ctr
-  //   }
-  // }
-  const { fetcher } = useBackendInstanceRequest();
+  async function fetcher([dId, token]: [string, AccessToken]) {
+    return await management.getOtpDeviceDetails(token, dId);
+  }
 
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.OtpDeviceDetails>,
-    RequestError<TalerErrorDetail>
-  >([`/private/otp-devices/${deviceId}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getOtpDeviceDetails">,
+    TalerHttpError
+  >([deviceId, session.token, "getOtpDeviceDetails"], fetcher);
 
-  if (isValidating) return { loading: true, data: data?.data };
-  if (data) {
-    return data;
-  }
-  if (error) return error.cause;
-  return { loading: true };
+  if (data) return data;
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/product.test.ts 
b/packages/merchant-backoffice-ui/src/hooks/product.test.ts
index c819c739e..1be00201a 100644
--- a/packages/merchant-backoffice-ui/src/hooks/product.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/product.test.ts
@@ -58,19 +58,19 @@ describe("product api interaction with listing", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals([{ id: "1234", price: "ARS:12" }]);
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals([{ id: "1234", price: "ARS:12" }]);
 
           env.addRequestExpectation(API_CREATE_PRODUCT, {
             request: {
@@ -107,25 +107,25 @@ describe("product api interaction with listing", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals([
-            {
-              id: "1234",
-              price: "ARS:12",
-            },
-            {
-              id: "2345",
-              price: "ARS:23",
-            },
-          ]);
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals([
+          //   {
+          //     id: "1234",
+          //     price: "ARS:12",
+          //   },
+          //   {
+          //     id: "2345",
+          //     price: "ARS:23",
+          //   },
+          // ]);
         },
       ],
       env.buildTestingContext(),
@@ -156,19 +156,19 @@ describe("product api interaction with listing", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals([{ id: "1234", price: "ARS:12" }]);
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals([{ id: "1234", price: "ARS:12" }]);
 
           env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("1234"), {
             request: {
@@ -195,15 +195,15 @@ describe("product api interaction with listing", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals([
-            {
-              id: "1234",
-              price: "ARS:13",
-            },
-          ]);
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals([
+          //   {
+          //     id: "1234",
+          //     price: "ARS:13",
+          //   },
+          // ]);
         },
       ],
       env.buildTestingContext(),
@@ -237,22 +237,22 @@ describe("product api interaction with listing", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals([
-            { id: "1234", price: "ARS:12" },
-            { id: "2345", price: "ARS:23" },
-          ]);
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals([
+          //   { id: "1234", price: "ARS:12" },
+          //   { id: "2345", price: "ARS:23" },
+          // ]);
 
           env.addRequestExpectation(API_DELETE_PRODUCT("2345"), {});
 
@@ -273,16 +273,16 @@ describe("product api interaction with listing", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals([{ id: "1234", price: "ARS:12" }]);
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals([{ id: "1234", price: "ARS:12" }]);
         },
       ],
       env.buildTestingContext(),
@@ -312,18 +312,18 @@ describe("product api interaction with details", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).false;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            description: "this is a description",
-          });
+          // expect(query.loading).false;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   description: "this is a description",
+          // });
 
           env.addRequestExpectation(API_UPDATE_PRODUCT_BY_ID("12"), {
             request: {
@@ -345,12 +345,12 @@ describe("product api interaction with details", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).false;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            description: "other description",
-          });
+          // expect(query.loading).false;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   description: "other description",
+          // });
         },
       ],
       env.buildTestingContext(),
diff --git a/packages/merchant-backoffice-ui/src/hooks/product.ts 
b/packages/merchant-backoffice-ui/src/hooks/product.ts
index 0eb54f717..6721136a5 100644
--- a/packages/merchant-backoffice-ui/src/hooks/product.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/product.ts
@@ -14,164 +14,93 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import _useSWR, { SWRHook, useSWRConfig } from "swr";
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
+import { AccessToken, OperationOk, TalerHttpError, TalerMerchantApi, 
TalerMerchantManagementErrorsByMethod, TalerMerchantManagementResultByMethod, 
opFixedSuccess } from "@gnu-taler/taler-util";
+import { useState } from "preact/hooks";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { PAGE_SIZE } from "../utils/constants.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export interface ProductAPI {
-//   getProduct: (
-//     id: string,  
-//   ) => Promise<void>;
-//   createProduct: (
-//     data: TalerMerchantApi.ProductAddDetail,
-//   ) => Promise<void>;
-//   updateProduct: (
-//     id: string,
-//     data: TalerMerchantApi.ProductPatchDetail,
-//   ) => Promise<void>;
-//   deleteProduct: (id: string) => Promise<void>;
-//   lockProduct: (
-//     id: string,
-//     data: TalerMerchantApi.LockRequest,
-//   ) => Promise<void>;
-// }
-
-// export function useProductAPI(): ProductAPI {
-//   const mutateAll = useMatchMutate();
-//   const { mutate } = useSWRConfig();
-
-//   const { request } = useBackendInstanceRequest();
-
-//   const createProduct = async (
-//     data: TalerMerchantApi.ProductAddDetail,
-//   ): Promise<void> => {
-//     const res = await request(`/private/products`, {
-//       method: "POST",
-//       data,
-//     });
-
-//     return await mutateAll(/.*\/private\/products.*/);
-//   };
-
-//   const updateProduct = async (
-//     productId: string,
-//     data: TalerMerchantApi.ProductPatchDetail,
-//   ): Promise<void> => {
-//     const r = await request(`/private/products/${productId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-
-//     return await mutateAll(/.*\/private\/products.*/);
-//   };
-
-//   const deleteProduct = async (productId: string): Promise<void> => {
-//     await request(`/private/products/${productId}`, {
-//       method: "DELETE",
-//     });
-//     await mutate([`/private/products`]);
-//   };
-
-//   const lockProduct = async (
-//     productId: string,
-//     data: TalerMerchantApi.LockRequest,
-//   ): Promise<void> => {
-//     await request(`/private/products/${productId}/lock`, {
-//       method: "POST",
-//       data,
-//     });
-
-//     return await mutateAll(/.*"\/private\/products.*/);
-//   };
-
-//   const getProduct = async (
-//     productId: string,
-//   ): Promise<void> => {
-//     await request(`/private/products/${productId}`, {
-//       method: "GET",
-//     });
-
-//     return
-//   };
-
-//   return { createProduct, updateProduct, deleteProduct, lockProduct, 
getProduct };
-// }
-
-export function useInstanceProducts(): HttpResponse<
-  (TalerMerchantApi.ProductDetail & WithId)[],
-  TalerErrorDetail
-> {
-  const { fetcher, multiFetcher } = useBackendInstanceRequest();
-
-  const { data: list, error: listError } = useSWR<
-    HttpResponseOk<TalerMerchantApi.InventorySummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/products`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  const paths = (list?.data.products || []).map(
-    (p) => `/private/products/${p.product_id}`,
+type ProductWithId = TalerMerchantApi.ProductDetail & { id: string, serial: 
number };
+function notUndefined(c: ProductWithId | undefined): c is ProductWithId {
+  return c !== undefined;
+}
+
+export function revalidateInstanceProducts() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"listProductsWithId",
+    undefined,
+    { revalidate: true },
   );
-  const { data: products, error: productError } = useSWR<
-    HttpResponseOk<TalerMerchantApi.ProductDetail>[],
-    RequestError<TalerErrorDetail>
-  >([paths], multiFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (listError) return listError.cause;
-  if (productError) return productError.cause;
-
-  if (products) {
-    const dataWithId = products.map((d) => {
-      //take the id from the queried url
-      return {
-        ...d.data,
-        id: d.info?.url.replace(/.*\/private\/products\//, "") || "",
-      };
+}
+export function useInstanceProducts() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<number | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, number]) {
+    const list = await management.listProducts(token, {
+      limit: PAGE_SIZE,
+      offset: String(bid),
+      order: "dec",
     });
-    return { ok: true, data: dataWithId };
+    if (list.type !== "ok") {
+      return list;
+    }
+    const all: Array<ProductWithId | undefined> = await Promise.all(
+      list.body.products.map(async (c) => {
+        const r = await management.getProductDetails(token, c.product_id);
+        if (r.type === "fail") {
+          return undefined;
+        }
+        return { ...r.body, id: c.product_id, serial: c.product_serial };
+      }),
+    );
+    const products = all.filter(notUndefined);
+
+    return opFixedSuccess({ products });
   }
-  return { loading: true };
+
+  const { data, error } = useSWR<
+    OperationOk<{ products: ProductWithId[] }> |
+    TalerMerchantManagementErrorsByMethod<"listProducts">,
+    TalerHttpError
+  >([session.token, offset, "listProductsWithId"], fetcher);
+
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  return buildPaginatedResult(data.body.products, offset, setOffset, (d) => 
d.serial)
+}
+
+export function revalidateProductDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getProductDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useProductDetails(productId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  async function fetcher([pid, token]: [string, AccessToken]) {
+    return await management.getProductDetails(token, pid);
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getProductDetails">,
+    TalerHttpError
+  >([productId, session.token, "getProductDetails"], fetcher);
 
-export function useProductDetails(
-  productId: string,
-): HttpResponse<
-  TalerMerchantApi.ProductDetail,
-  TalerErrorDetail
-> {
-  const { fetcher } = useBackendInstanceRequest();
-
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.ProductDetail>,
-    RequestError<TalerErrorDetail>
-  >([`/private/products/${productId}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (isValidating) return { loading: true, data: data?.data };
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/templates.ts 
b/packages/merchant-backoffice-ui/src/hooks/templates.ts
index ff0461a67..10e480b01 100644
--- a/packages/merchant-backoffice-ui/src/hooks/templates.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/templates.ts
@@ -14,253 +14,77 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useTemplateAPI(): TemplateAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createTemplate = async (
-//     data: TalerMerchantApi.TemplateAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const updateTemplate = async (
-//     templateId: string,
-//     data: TalerMerchantApi.TemplatePatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates/${templateId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const deleteTemplate = async (
-//     templateId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates/${templateId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const createOrderFromTemplate = async (
-//     templateId: string,
-//     data: TalerMerchantApi.UsingTemplateDetails,
-//   ): Promise<
-//     HttpResponseOk<TalerMerchantApi.PostOrderResponse>
-//   > => {
-//     const res = await request<TalerMerchantApi.PostOrderResponse>(
-//       `/templates/${templateId}`,
-//       {
-//         method: "POST",
-//         data,
-//       },
-//     );
-//     await mutateAll(/.*private\/templates.*/);
-//     return res;
-//   };
-
-//   const testTemplateExist = async (
-//     templateId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/templates/${templateId}`, { 
method: "GET", });
-//     return res;
-//   };
-
-
-//   return {
-//     createTemplate,
-//     updateTemplate,
-//     deleteTemplate,
-//     testTemplateExist,
-//     createOrderFromTemplate,
-//   };
-// }
-
-// export interface TemplateAPI {
-//   createTemplate: (
-//     data: TalerMerchantApi.TemplateAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateTemplate: (
-//     id: string,
-//     data: TalerMerchantApi.TemplatePatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   testTemplateExist: (
-//     id: string
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteTemplate: (id: string) => Promise<HttpResponseOk<void>>;
-//   createOrderFromTemplate: (
-//     id: string,
-//     data: TalerMerchantApi.UsingTemplateDetails,
-//   ) => Promise<HttpResponseOk<TalerMerchantApi.PostOrderResponse>>;
-// }
 
 export interface InstanceTemplateFilter {
-  //FIXME: add filter to the template list
-  position?: string;
 }
 
-export function useInstanceTemplates(
-  args?: InstanceTemplateFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.TemplateSummaryResponse,
-  TalerErrorDetail
-> {
-  const { templateFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.position ? pageBefore * PAGE_SIZE : 0;
-
-  /**
-   * FIXME: this can be cleaned up a little
-   *
-   * the logic of double query should be inside the orderFetch so from the 
hook perspective and cache
-   * is just one query and one error status
-   */
-  const {
-    data: beforeData,
-    error: beforeError,
-    isValidating: loadingBefore,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TemplateSummaryResponse>,
-    RequestError<TalerErrorDetail>>(
-      [
-        `/private/templates`,
-        args?.position,
-        totalBefore,
-      ],
-      templateFetcher,
-    );
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TemplateSummaryResponse>,
-    RequestError<TalerErrorDetail>
-  >([`/private/templates`, args?.position, -totalAfter], templateFetcher);
-
-  //this will save last result
-  const [lastBefore, setLastBefore] = useState<
-    HttpResponse<
-      TalerMerchantApi.TemplateSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
+export function revalidateInstanceTemplates() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listTemplates",
+    undefined,
+    { revalidate: true },
+  );
+}
+export function useInstanceTemplates() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listTemplates(token, {
+      limit: PAGE_SIZE,
+      offset: bid,
+      order: "dec",
+    });
+  }
 
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.TemplateSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-    if (beforeData) setLastBefore(beforeData);
-  }, [afterData, beforeData]);
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listTemplates">,
+    TalerHttpError
+  >([session.token, offset, "listTemplates"], fetcher);
 
-  if (beforeError) return beforeError.cause;
-  if (afterError) return afterError.cause;
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.templates.length < totalAfter;
-  const isReachingStart = args?.position === undefined
-    ||
-    (beforeData && beforeData.data.templates.length < totalBefore);
+  return buildPaginatedResult(data.body.templates, offset, setOffset, (d) => 
d.template_id)
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.templates.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = 
`${afterData.data.templates[afterData.data.templates.length - 1]
-          .template_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-    loadMorePrev: () => {
-      if (!beforeData || isReachingStart) return;
-      if (beforeData.data.templates.length < MAX_RESULT_SIZE) {
-        setPageBefore(pageBefore + 1);
-      } else if (beforeData) {
-        const from = 
`${beforeData.data.templates[beforeData.data.templates.length - 1]
-          .template_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-  };
+}
 
-  // const templates = !afterData ? [] : (afterData || 
lastAfter).data.templates;
-  const templates =
-    !beforeData || !afterData
-      ? []
-      : (beforeData || lastBefore).data.templates
-        .slice()
-        .reverse()
-        .concat((afterData || lastAfter).data.templates);
-  if (loadingAfter || loadingBefore)
-    return { loading: true, data: { templates } };
-  if (beforeData && afterData) {
-    return { ok: true, data: { templates }, ...pagination };
-  }
-  return { loading: true };
+export function revalidateProductDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === 
"getTemplateDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useTemplateDetails(templateId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
 
-export function useTemplateDetails(
-  templateId: string,
-): HttpResponse<
-  TalerMerchantApi.TemplateDetails,
-  TalerErrorDetail
-> {
-  const { templateFetcher } = useBackendInstanceRequest();
+  async function fetcher([tid, token]: [string, AccessToken]) {
+    return await management.getTemplateDetails(token, tid);
+  }
 
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TemplateDetails>,
-    RequestError<TalerErrorDetail>
-  >([`/private/templates/${templateId}`], templateFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getTemplateDetails">,
+    TalerHttpError
+  >([templateId, session.token, "getTemplateDetails"], fetcher);
 
-  if (isValidating) return { loading: true, data: data?.data };
-  if (data) {
-    return data;
-  }
-  if (error) return error.cause;
-  return { loading: true };
+  if (data) return data;
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts 
b/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts
index 5f1cf51a7..b424e9686 100644
--- a/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/transfer.test.ts
@@ -19,7 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { AmountString, PaytoString, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import {
+  AmountString,
+  PaytoString,
+  TalerMerchantApi,
+} from "@gnu-taler/taler-util";
 import * as tests from "@gnu-taler/web-util/testing";
 import { expect } from "chai";
 import { ApiMockEnvironment } from "./testing.js";
@@ -38,7 +42,7 @@ describe("transfer api interaction with listing", () => {
       },
     });
 
-    const moveCursor = (d: string) => {
+    const moveCursor = (d: string | undefined) => {
       console.log("new position", d);
     };
 
@@ -51,19 +55,19 @@ describe("transfer api interaction with listing", () => {
       {},
       [
         ({ query, api }) => {
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
 
         ({ query, api }) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            transfers: [{ wtid: "2" }],
-          });
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   transfers: [{ wtid: "2" }],
+          // });
 
           env.addRequestExpectation(API_INFORM_TRANSFERS, {
             request: {
@@ -93,13 +97,13 @@ describe("transfer api interaction with listing", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
 
-          expect(query.data).deep.equals({
-            transfers: [{ wtid: "3" }, { wtid: "2" }],
-          });
+          // expect(query.data).deep.equals({
+          //   transfers: [{ wtid: "3" }, { wtid: "2" }],
+          // });
         },
       ],
       env.buildTestingContext(),
@@ -121,12 +125,16 @@ describe("transfer listing pagination", () => {
       },
     });
 
-    const moveCursor = (d: string) => {
+    const moveCursor = (d: string | undefined) => {
       console.log("new position", d);
     };
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
-        return useInstanceTransfers({ payto_uri: "payto://" }, moveCursor);
+        const query = useInstanceTransfers(
+          { payto_uri: "payto://" },
+          moveCursor,
+        );
+        return { query };
       },
       {},
       [
@@ -134,22 +142,18 @@ describe("transfer listing pagination", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(query.loading).true;
+          // expect(query.loading).true;
         },
         (query) => {
-          expect(query.loading).undefined;
-          expect(query.ok).true;
-          if (!query.ok) return;
-          expect(query.data).deep.equals({
-            transfers: [{ wtid: "2" }, { wtid: "1" }],
-          });
-          expect(query.isReachingEnd).true;
-          expect(query.isReachingStart).true;
+          // expect(query.loading).undefined;
+          // expect(query.ok).true;
+          // if (!query.ok) return;
+          // expect(query.data).deep.equals({
+          //   transfers: [{ wtid: "2" }, { wtid: "1" }],
+          // });
+          // expect(query.isReachingEnd).true;
+          // expect(query.isReachingStart).true;
 
-          //check that this button won't trigger more updates since
-          //has reach end and start
-          query.loadMore();
-          query.loadMorePrev();
         },
       ],
       env.buildTestingContext(),
@@ -184,16 +188,17 @@ describe("transfer listing pagination", () => {
       },
     });
 
-    const moveCursor = (d: string) => {
+    const moveCursor = (d: string | undefined) => {
       console.log("new position", d);
     };
 
     const hookBehavior = await tests.hookBehaveLikeThis(
       () => {
-        return useInstanceTransfers(
+        const query = useInstanceTransfers(
           { payto_uri: "payto://", position: "1" },
           moveCursor,
         );
+        return { query };
       },
       {},
       [
@@ -201,17 +206,17 @@ describe("transfer listing pagination", () => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(result.loading).true;
+          // expect(result.loading).true;
         },
         (result) => {
-          expect(result.loading).undefined;
-          expect(result.ok).true;
-          if (!result.ok) return;
-          expect(result.data).deep.equals({
-            transfers: [...transfersFrom20to0, ...transfersFrom20to40],
-          });
-          expect(result.isReachingEnd).false;
-          expect(result.isReachingStart).false;
+          // expect(result.loading).undefined;
+          // expect(result.ok).true;
+          // if (!result.ok) return;
+          // expect(result.data).deep.equals({
+          //   transfers: [...transfersFrom20to0, ...transfersFrom20to40],
+          // });
+          // expect(result.isReachingEnd).false;
+          // expect(result.isReachingStart).false;
 
           //query more
           env.addRequestExpectation(API_LIST_TRANSFERS, {
@@ -220,30 +225,30 @@ describe("transfer listing pagination", () => {
               transfers: [...transfersFrom20to40, { wtid: "41" }],
             },
           });
-          result.loadMore();
+          // result.loadMore();
         },
         (result) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(result.loading).true;
+          // expect(result.loading).true;
         },
         (result) => {
           expect(env.assertJustExpectedRequestWereMade()).deep.eq({
             result: "ok",
           });
-          expect(result.loading).undefined;
-          expect(result.ok).true;
-          if (!result.ok) return;
-          expect(result.data).deep.equals({
-            transfers: [
-              ...transfersFrom20to0,
-              ...transfersFrom20to40,
-              { wtid: "41" },
-            ],
-          });
-          expect(result.isReachingEnd).true;
-          expect(result.isReachingStart).false;
+          // expect(result.loading).undefined;
+          // expect(result.ok).true;
+          // if (!result.ok) return;
+          // expect(result.data).deep.equals({
+          //   transfers: [
+          //     ...transfersFrom20to0,
+          //     ...transfersFrom20to40,
+          //     { wtid: "41" },
+          //   ],
+          // });
+          // expect(result.isReachingEnd).true;
+          // expect(result.isReachingStart).false;
         },
       ],
       env.buildTestingContext(),
diff --git a/packages/merchant-backoffice-ui/src/hooks/transfer.ts 
b/packages/merchant-backoffice-ui/src/hooks/transfer.ts
index af62af0fa..7a701d44f 100644
--- a/packages/merchant-backoffice-ui/src/hooks/transfer.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/transfer.ts
@@ -14,173 +14,58 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, TalerHttpError, TalerMerchantManagementResultByMethod } 
from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
+import { buildPaginatedResult } from "./webhooks.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useTransferAPI(): TransferAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const informTransfer = async (
-//     data: TalerMerchantApi.TransferInformation,
-//   ): Promise<HttpResponseOk<{}>> => {
-//     const res = await request<{}>(`/private/transfers`, {
-//       method: "POST",
-//       data,
-//     });
-
-//     await mutateAll(/.*private\/transfers.*/);
-//     return res;
-//   };
-
-//   return { informTransfer };
-// }
-
-// export interface TransferAPI {
-//   informTransfer: (
-//     data: TalerMerchantApi.TransferInformation,
-//   ) => Promise<HttpResponseOk<{}>>;
-// }
-
 export interface InstanceTransferFilter {
   payto_uri?: string;
-  verified?: "yes" | "no";
+  verified?: boolean;
   position?: string;
 }
 
+export function revalidateInstanceTransfers() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listWireTransfers",
+    undefined,
+    { revalidate: true },
+  );
+}
 export function useInstanceTransfers(
   args?: InstanceTransferFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.TransferList,
-  TalerErrorDetail
-> {
-  const { transferFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.position !== undefined ? pageBefore * PAGE_SIZE : 
0;
-
-  /**
-   * FIXME: this can be cleaned up a little
-   *
-   * the logic of double query should be inside the orderFetch so from the 
hook perspective and cache
-   * is just one query and one error status
-   */
-  const {
-    data: beforeData,
-    error: beforeError,
-    isValidating: loadingBefore,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TransferList>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/transfers`,
-      args?.payto_uri,
-      args?.verified,
-      args?.position,
-      totalBefore,
-    ],
-    transferFetcher,
-  );
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.TransferList>,
-    RequestError<TalerErrorDetail>
-  >(
-    [
-      `/private/transfers`,
-      args?.payto_uri,
-      args?.verified,
-      args?.position,
-      -totalAfter,
-    ],
-    transferFetcher,
-  );
-
-  //this will save last result
-  const [lastBefore, setLastBefore] = useState<
-    HttpResponse<
-      TalerMerchantApi.TransferList,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.TransferList,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-    if (beforeData) setLastBefore(beforeData);
-  }, [afterData, beforeData]);
+  updatePosition: (id: string | undefined) => void = (() => { }),
+) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  // const [offset, setOffset] = useState<string | undefined>(args?.position);
+
+  async function fetcher([token, o, p, v]: [AccessToken, string, string, 
boolean]) {
+    return await management.listWireTransfers(token, {
+      paytoURI: p,
+      verified: v,
+      limit: PAGE_SIZE,
+      offset: o,
+      order: "dec",
+    });
+  }
 
-  if (beforeError) return beforeError.cause;
-  if (afterError) return afterError.cause;
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listWireTransfers">,
+    TalerHttpError
+  >([session.token, args?.position, args?.payto_uri, args?.verified, 
"listWireTransfers"], fetcher);
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data.transfers.length < totalAfter;
-  const isReachingStart =
-    args?.position === undefined ||
-    (beforeData && beforeData.data.transfers.length < totalBefore);
+  if (error) return error;
+  if (data === undefined) return undefined;
+  // if (data.type !== "ok") return data;
 
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.transfers.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = 
`${afterData.data.transfers[afterData.data.transfers.length - 1]
-            .transfer_serial_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-    loadMorePrev: () => {
-      if (!beforeData || isReachingStart) return;
-      if (beforeData.data.transfers.length < MAX_RESULT_SIZE) {
-        setPageBefore(pageBefore + 1);
-      } else if (beforeData) {
-        const from = 
`${beforeData.data.transfers[beforeData.data.transfers.length - 1]
-            .transfer_serial_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
-    },
-  };
+  return buildPaginatedResult(data.body.transfers, args?.position, 
updatePosition, (d) => String(d.transfer_serial_id))
 
-  const transfers =
-    !beforeData || !afterData
-      ? []
-      : (beforeData || lastBefore).data.transfers
-        .slice()
-        .reverse()
-        .concat((afterData || lastAfter).data.transfers);
-  if (loadingAfter || loadingBefore)
-    return { loading: true, data: { transfers } };
-  if (beforeData && afterData) {
-    return { ok: true, data: { transfers }, ...pagination };
-  }
-  return { loading: true };
 }
diff --git a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts 
b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
index cc817b84e..5e2e08bcc 100644
--- a/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
+++ b/packages/merchant-backoffice-ui/src/hooks/webhooks.ts
@@ -14,165 +14,108 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
+  useMerchantApiContext
 } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils/constants.js";
-import { useBackendInstanceRequest, useMatchMutate } from "./backend.js";
+import { useState } from "preact/hooks";
+import { PAGE_SIZE } from "../utils/constants.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
-import { TalerErrorDetail, TalerMerchantApi } from "@gnu-taler/taler-util";
-import _useSWR, { SWRHook } from "swr";
+import { AccessToken, OperationOk, TalerHttpError, 
TalerMerchantManagementResultByMethod } from "@gnu-taler/taler-util";
+import _useSWR, { SWRHook, mutate } from "swr";
+import { useSessionContext } from "../context/session.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-// export function useWebhookAPI(): WebhookAPI {
-//   const mutateAll = useMatchMutate();
-//   const { request } = useBackendInstanceRequest();
-
-//   const createWebhook = async (
-//     data: TalerMerchantApi.WebhookAddDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/webhooks`, {
-//       method: "POST",
-//       data,
-//     });
-//     await mutateAll(/.*private\/webhooks.*/);
-//     return res;
-//   };
-
-//   const updateWebhook = async (
-//     webhookId: string,
-//     data: TalerMerchantApi.WebhookPatchDetails,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/webhooks/${webhookId}`, {
-//       method: "PATCH",
-//       data,
-//     });
-//     await mutateAll(/.*private\/webhooks.*/);
-//     return res;
-//   };
-
-//   const deleteWebhook = async (
-//     webhookId: string,
-//   ): Promise<HttpResponseOk<void>> => {
-//     const res = await request<void>(`/private/webhooks/${webhookId}`, {
-//       method: "DELETE",
-//     });
-//     await mutateAll(/.*private\/webhooks.*/);
-//     return res;
-//   };
-
-//   return { createWebhook, updateWebhook, deleteWebhook };
-// }
-
-// export interface WebhookAPI {
-//   createWebhook: (
-//     data: TalerMerchantApi.WebhookAddDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   updateWebhook: (
-//     id: string,
-//     data: TalerMerchantApi.WebhookPatchDetails,
-//   ) => Promise<HttpResponseOk<void>>;
-//   deleteWebhook: (id: string) => Promise<HttpResponseOk<void>>;
-// }
-
 export interface InstanceWebhookFilter {
-  //FIXME: add filter to the webhook list
-  position?: string;
 }
 
-export function useInstanceWebhooks(
-  args?: InstanceWebhookFilter,
-  updatePosition?: (id: string) => void,
-): HttpResponsePaginated<
-  TalerMerchantApi.WebhookSummaryResponse,
-  TalerErrorDetail
-> {
-  const { webhookFetcher } = useBackendInstanceRequest();
-
-  const [pageBefore, setPageBefore] = useState(1);
-  const [pageAfter, setPageAfter] = useState(1);
-
-  const totalAfter = pageAfter * PAGE_SIZE;
-  const totalBefore = args?.position ? pageBefore * PAGE_SIZE : 0;
-
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<TalerMerchantApi.WebhookSummaryResponse>,
-    RequestError<TalerErrorDetail>
-
-  >([`/private/webhooks`, args?.position, -totalAfter], webhookFetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      TalerMerchantApi.WebhookSummaryResponse,
-      TalerErrorDetail
-    >
-  >({ loading: true });
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData]);
-
-  if (afterError) return afterError.cause;
-
-  const isReachingEnd =
-    afterData && afterData.data.webhooks.length < totalAfter;
-  const isReachingStart = true;
-
-  const pagination = {
-    isReachingEnd,
-    isReachingStart,
-    loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.webhooks.length < MAX_RESULT_SIZE) {
-        setPageAfter(pageAfter + 1);
-      } else {
-        const from = `${afterData.data.webhooks[afterData.data.webhooks.length 
- 1].webhook_id
-          }`;
-        if (from && updatePosition) updatePosition(from);
-      }
+export function revalidateInstanceWebhooks() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "listWebhooks",
+    undefined,
+    { revalidate: true },
+  );
+}
+export function useInstanceWebhooks() {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  const [offset, setOffset] = useState<string | undefined>();
+
+  async function fetcher([token, bid]: [AccessToken, string]) {
+    return await management.listWebhooks(token, {
+      limit: 5,
+      offset: bid,
+      order: "dec",
+    });
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"listWebhooks">,
+    TalerHttpError
+  >([session.token, offset, "listWebhooks"], fetcher);
+
+  if (error) return error;
+  if (data === undefined) return undefined;
+  if (data.type !== "ok") return data;
+
+  return buildPaginatedResult(data.body.webhooks, offset, setOffset, (d) => 
d.webhook_id)
+}
+
+type PaginatedResult<T> = OperationOk<T> & {
+  isLastPage: boolean;
+  isFirstPage: boolean;
+  loadNext(): void;
+  loadFirst(): void;
+}
+
+//TODO: consider sending this to web-util
+export function buildPaginatedResult<R, OffId>(data: R[], offset: OffId | 
undefined, setOffset: (o: OffId | undefined) => void, getId: (r: R) => OffId): 
PaginatedResult<R[]> {
+
+  const isLastPage = data.length <= PAGE_SIZE;
+  const isFirstPage = offset === undefined;
+
+  const result = structuredClone(data);
+  if (result.length == PAGE_SIZE + 1) {
+    result.pop();
+  }
+  return {
+    type: "ok",
+    body: result,
+    isLastPage,
+    isFirstPage,
+    loadNext: () => {
+      if (!result.length) return;
+      const id = getId(result[result.length - 1])
+      setOffset(id);
     },
-    loadMorePrev: () => {
-      return;
+    loadFirst: () => {
+      setOffset(undefined);
     },
   };
+}
 
-  const webhooks = !afterData ? [] : (afterData || lastAfter).data.webhooks;
 
-  if (loadingAfter) return { loading: true, data: { webhooks } };
-  if (afterData) {
-    return { ok: true, data: { webhooks }, ...pagination };
-  }
-  return { loading: true };
+export function revalidateWebhookDetails() {
+  return mutate(
+    (key) => Array.isArray(key) && key[key.length - 1] === "getWebhookDetails",
+    undefined,
+    { revalidate: true },
+  );
 }
+export function useWebhookDetails(webhookId: string) {
+  const { state: session } = useSessionContext();
+  const { lib: { management } } = useMerchantApiContext();
+
+  async function fetcher([hookId, token]: [string, AccessToken]) {
+    return await management.getWebhookDetails(token, hookId);
+  }
+
+  const { data, error } = useSWR<
+    TalerMerchantManagementResultByMethod<"getWebhookDetails">,
+    TalerHttpError
+  >([webhookId, session.token, "getWebhookDetails"], fetcher);
 
-export function useWebhookDetails(
-  webhookId: string,
-): HttpResponse<
-  TalerMerchantApi.WebhookDetails,
-  TalerErrorDetail
-> {
-  const { webhookFetcher } = useBackendInstanceRequest();
-
-  const { data, error, isValidating } = useSWR<
-    HttpResponseOk<TalerMerchantApi.WebhookDetails>,
-    RequestError<TalerErrorDetail>
-  >([`/private/webhooks/${webhookId}`], webhookFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
-
-  if (isValidating) return { loading: true, data: data?.data };
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
index b7551df04..7dd5d74bb 100644
--- a/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/admin/list/index.tsx
@@ -19,37 +19,29 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerError, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { TalerError, TalerMerchantApi } from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../components/exception/loading.js";
 import { NotificationCard } from "../../../components/menu/index.js";
 import { DeleteModal, PurgeModal } from "../../../components/modal/index.js";
+import { useSessionContext } from "../../../context/session.js";
 import { useBackendInstances } from "../../../hooks/instance.js";
 import { Notification } from "../../../utils/types.js";
 import { View } from "./View.js";
-import { useSessionContext } from "../../../context/session.js";
-import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
 
 interface Props {
   onCreate: () => void;
   onUpdate: (id: string) => void;
   instances: TalerMerchantApi.Instance[];
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
 }
 
 export default function Instances({
-  onUnauthorized,
-  onLoadError,
-  onNotFound,
   onCreate,
   onUpdate,
 }: Props): VNode {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
index 35c9e6624..0ce126b76 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/create/index.tsx
@@ -46,7 +46,7 @@ export default function CreateValidator({ onConfirm, onBack 
}: Props): VNode {
       <CreatePage
         onBack={onBack}
         onCreate={(request: Entity) => {
-          return api.management.addAccount(state.token, request)
+          return api.management.addBankAccount(state.token, request)
             .then(() => {
               onConfirm()
             })
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
index 8de6c763e..1d5c523a4 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/accounts/list/index.tsx
@@ -19,63 +19,56 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import { useInstanceBankAccounts } from "../../../../hooks/bank.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { ListPage } from "./ListPage.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
 }
 
 export default function ListOtpDevices({
-  onUnauthorized,
-  onLoadError,
   onCreate,
   onSelect,
-  onNotFound,
 }: Props): VNode {
-  const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { lib: api } = useMerchantApiContext();
   const { state } = useSessionContext();
-  const result = useInstanceBankAccounts({ position }, (id) => 
setPosition(id));
+  const result = useInstanceBankAccounts();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
+  }
+  if (result.type === "fail") {
+    switch(result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />
+      }
+      default: {
+        assertUnreachable(result.case)
+      }
+    }
   }
 
   return (
     <Fragment>
       <NotificationCard notification={notif} />
-      {result.data.accounts.length < 1 &&
+      {result.body.length < 1 &&
         <NotificationCard notification={{
           type: "WARN",
           message: i18n.str`You need to associate a bank account to receive 
revenue.`,
@@ -83,17 +76,17 @@ export default function ListOtpDevices({
         }} />
       }
       <ListPage
-        devices={result.data.accounts}
+        devices={result.body}
         onLoadMoreBefore={
-          result.isReachingStart ? result.loadMorePrev : undefined
+          result.isFirstPage ? undefined: result.loadFirst
         }
-        onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+        onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
         onCreate={onCreate}
         onSelect={(e) => {
           onSelect(e.h_wire);
         }}
         onDelete={(e: TalerMerchantApi.BankAccountEntry) => {
-          return api.management.deleteAccount(state.token, e.h_wire)
+          return api.management.deleteBankAccount(state.token, e.h_wire)
             .then(() =>
               setNotif({
                 message: i18n.str`bank account delete successfully`,
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
index f83020ed1..f5b7436d4 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/accounts/update/index.tsx
@@ -19,39 +19,33 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import { useBankAccountDetails } from "../../../../hooks/bank.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 export type Entity = TalerMerchantApi.AccountPatchDetails & WithId;
 
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   bid: string;
 }
 export default function UpdateValidator({
   bid,
   onConfirm,
   onBack,
-  onUnauthorized,
-  onNotFound,
-  onLoadError,
 }: Props): VNode {
   const { lib: api } = useMerchantApiContext();
   const { state } = useSessionContext();
@@ -60,29 +54,29 @@ export default function UpdateValidator({
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   return (
     <Fragment>
       <NotificationCard notification={notif} />
       <UpdatePage
-        account={{ ...result.data, id: bid }}
+        account={{ ...result.body, id: bid }}
         onBack={onBack}
         onUpdate={(data) => {
-          return api.management.updateAccount(state.token, bid, data)
+          return api.management.updateBankAccount(state.token, bid, data)
             .then(onConfirm)
             .catch((error) => {
               setNotif({
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
index be631c1e0..d7abc4fbe 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/OrderCreatedSuccessfully.tsx
@@ -13,52 +13,55 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { ErrorType, HttpError, Loading, useTranslationContext } from 
"@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
-import { useEffect, useState } from "preact/hooks";
+import { HttpStatusCode, TalerError, assertUnreachable } from 
"@gnu-taler/taler-util";
+import { Loading, useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode, h } from "preact";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { CreatedSuccessfully } from 
"../../../../components/notifications/CreatedSuccessfully.js";
-import { Entity } from "./index.js";
 import { useOrderDetails } from "../../../../hooks/order.js";
-import { HttpStatusCode, TalerErrorDetail } from "@gnu-taler/taler-util";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
+import { Entity } from "./index.js";
 
 interface Props {
   entity: Entity;
   onConfirm: () => void;
   onCreateAnother?: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
 }
 
 export function OrderCreatedSuccessfully({
   entity,
   onConfirm,
   onCreateAnother,
-  onLoadError,
-  onNotFound,
-  onUnauthorized,
 }: Props): VNode {
   const result = useOrderDetails(entity.response.order_id)
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
+  }
+  if (result.type === "fail") {
+    switch(result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />
+      }
+      case HttpStatusCode.BadGateway: {
+        return <div>Failed to obtain a response from the exchange</div>;
+      }
+      case HttpStatusCode.GatewayTimeout: {
+        return (
+          <div>The merchant's interaction with the exchange took too long</div>
+        );
+      }
+      default: {
+        assertUnreachable(result)
+      }
+    }
   }
 
-  const url = result.data.order_status === "unpaid" ?
-    result.data.taler_pay_uri :
-    result.data.contract_terms.fulfillment_url
+  const url = result.body.order_status === "unpaid" ?
+    result.body.taler_pay_uri :
+    result.body.contract_terms.fulfillment_url
 
   return (
     <CreatedSuccessfully
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
index 54d8c7b47..e40e766dd 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/create/index.tsx
@@ -19,18 +19,19 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerError, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
-import { ErrorType, HttpError, useMerchantApiContext } from 
"@gnu-taler/web-util/browser";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
+import { useMerchantApiContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import { useInstanceDetails } from "../../../../hooks/instance.js";
 import { useInstanceProducts } from "../../../../hooks/product.js";
 import { Notification } from "../../../../utils/types.js";
 import { CreatePage } from "./CreatePage.js";
-import { useSessionContext } from "../../../../context/session.js";
-import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 
 export type Entity = {
   request: TalerMerchantApi.PostOrderRequest;
@@ -39,16 +40,10 @@ export type Entity = {
 interface Props {
   onBack?: () => void;
   onConfirm: (id: string) => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
 }
 export default function OrderCreate({
   onConfirm,
   onBack,
-  onLoadError,
-  onNotFound,
-  onUnauthorized,
 }: Props): VNode {
   const { lib } = useMerchantApiContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
@@ -60,36 +55,19 @@ export default function OrderCreate({
   if (detailsResult instanceof TalerError) {
     return <ErrorLoadingMerchant error={detailsResult} />
   }
-
-  // if (detailsResult.loading) return <Loading />;
-  if (inventoryResult.loading) return <Loading />;
-
-  // if (!detailsResult.ok) {
-  //   if (
-  //     detailsResult.type === ErrorType.CLIENT &&
-  //     detailsResult.status === HttpStatusCode.Unauthorized
-  //   )
-  //     return onUnauthorized();
-  //   if (
-  //     detailsResult.type === ErrorType.CLIENT &&
-  //     detailsResult.status === HttpStatusCode.NotFound
-  //   )
-  //     return onNotFound();
-  //   return onLoadError(detailsResult);
-  // }
-
-  if (!inventoryResult.ok) {
-    if (
-      inventoryResult.type === ErrorType.CLIENT &&
-      inventoryResult.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      inventoryResult.type === ErrorType.CLIENT &&
-      inventoryResult.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(inventoryResult);
+  if (!inventoryResult) return <Loading />
+  if (inventoryResult instanceof TalerError) {
+    return <ErrorLoadingMerchant error={inventoryResult} />
+  }
+  if (inventoryResult.type === "fail") {
+    switch (inventoryResult.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(inventoryResult.case);
+      }
+    }
   }
 
   return (
@@ -119,7 +97,7 @@ export default function OrderCreate({
             });
         }}
         instanceConfig={detailsResult.body}
-        instanceInventory={inventoryResult.data}
+        instanceInventory={inventoryResult.body}
       />
     </Fragment>
   );
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx
index c0c4862a1..1c8ceb90e 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/details/index.tsx
@@ -13,58 +13,60 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { HttpStatusCode, TalerErrorDetail } from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
+  HttpStatusCode,
+  TalerError,
+  assertUnreachable,
+} from "@gnu-taler/taler-util";
+import {
   useMerchantApiContext,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import { useOrderDetails } from "../../../../hooks/order.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { DetailPage } from "./DetailPage.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 export interface Props {
   oid: string;
-
   onBack: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
 }
 
-export default function Update({
-  oid,
-  onBack,
-  onLoadError,
-  onNotFound,
-  onUnauthorized,
-}: Props): VNode {
+export default function Update({ oid, onBack }: Props): VNode {
   const result = useOrderDetails(oid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const { lib: api } = useMerchantApiContext()
+  const { lib: api } = useMerchantApiContext();
   const { state } = useSessionContext();
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      case HttpStatusCode.BadGateway: {
+        return <div>Failed to obtain a response from the exchange</div>;
+      }
+      case HttpStatusCode.GatewayTimeout: {
+        return (
+          <div>The merchant's interaction with the exchange took too long</div>
+        );
+      }
+      default: {
+        assertUnreachable(result);
+      }
+    }
   }
 
   return (
@@ -78,7 +80,8 @@ export default function Update({
           if (state.status !== "loggedIn") {
             return;
           }
-          api.management.addRefund(state.token, id, value)
+          api.management
+            .addRefund(state.token, id, value)
             .then(() =>
               setNotif({
                 message: i18n.str`refund created successfully`,
@@ -91,10 +94,9 @@ export default function Update({
                 type: "ERROR",
                 description: error.message,
               }),
-            )
-        }
-        }
-        selected={result.data}
+            );
+        }}
+        selected={result.body}
       />
     </Fragment>
   );
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
index 7b88985dc..12762c3b3 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/ListPage.tsx
@@ -19,7 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { TalerMerchantApi } from "@gnu-taler/taler-util";
+import { AbsoluteTime, TalerMerchantApi } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { format } from "date-fns";
 import { Fragment, VNode, h } from "preact";
@@ -43,8 +43,8 @@ export interface ListPageProps {
   isNotWiredActive: string;
   isWiredActive: string;
 
-  jumpToDate?: Date;
-  onSelectDate: (date?: Date) => void;
+  jumpToDate?: AbsoluteTime;
+  onSelectDate: (date?: AbsoluteTime) => void;
 
   orders: (TalerMerchantApi.OrderHistoryEntry & WithId)[];
   onLoadMoreBefore?: () => void;
@@ -177,7 +177,7 @@ export function ListPage({
                     class="input"
                     type="text"
                     readonly
-                    value={!jumpToDate ? "" : format(jumpToDate, 
dateFormatForSettings(settings))}
+                    value={!jumpToDate || jumpToDate.t_ms === "never" ? "" : 
format(jumpToDate.t_ms, dateFormatForSettings(settings))}
                     placeholder={i18n.str`date 
(${dateFormatForSettings(settings)})`}
                     onClick={() => {
                       setPickDate(true);
@@ -207,7 +207,9 @@ export function ListPage({
       <DatePicker
         opened={pickDate}
         closeFunction={() => setPickDate(false)}
-        dateReceiver={onSelectDate}
+        dateReceiver={(d) => {
+          onSelectDate(AbsoluteTime.fromMilliseconds(d.getTime()))
+        }}
       />
 
       <CardTable
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
index a50f02a2d..7a3fb2d22 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/orders/list/index.tsx
@@ -19,83 +19,84 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi, stringifyPayUri } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
+  AbsoluteTime,
+  HttpStatusCode,
+  TalerError,
+  TalerMerchantApi,
+  assertUnreachable,
+} from "@gnu-taler/taler-util";
+import {
   useMerchantApiContext,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
+import { VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { JumpToElementById } from 
"../../../../components/form/JumpToElementById.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import {
   InstanceOrderFilter,
   useInstanceOrders,
   useOrderDetails,
 } from "../../../../hooks/order.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { ListPage } from "./ListPage.js";
 import { RefundModal } from "./Table.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onSelect: (id: string) => void;
   onCreate: () => void;
 }
 
-export default function OrderList({
-  onUnauthorized,
-  onLoadError,
-  onCreate,
-  onSelect,
-  onNotFound,
-}: Props): VNode {
-  const [filter, setFilter] = useState<InstanceOrderFilter>({ paid: "no" });
+export default function OrderList({ onCreate, onSelect }: Props): VNode {
+  const [filter, setFilter] = useState<InstanceOrderFilter>({ paid: false });
   const [orderToBeRefunded, setOrderToBeRefunded] = useState<
     TalerMerchantApi.OrderHistoryEntry | undefined
   >(undefined);
 
-  const setNewDate = (date?: Date): void =>
+  const setNewDate = (date?: AbsoluteTime): void =>
     setFilter((prev) => ({ ...prev, date }));
 
-  const result = useInstanceOrders(filter, setNewDate);
+  const result = useInstanceOrders(filter, (d) =>
+    setFilter({ ...filter, position: d }),
+  );
   const { lib } = useMerchantApiContext();
 
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
 
   const { i18n } = useTranslationContext();
-  const { state } = useSessionContext()
-
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
-  }
+  const { state } = useSessionContext();
 
-  const isNotPaidActive = filter.paid === "no" ? "is-active" : "";
-  const isPaidActive = filter.paid === "yes" && filter.wired === undefined ? 
"is-active" : "";
-  const isRefundedActive = filter.refunded === "yes" ? "is-active" : "";
-  const isNotWiredActive = filter.wired === "no" && filter.paid === "yes" ? 
"is-active" : "";
-  const isWiredActive = filter.wired === "yes" ? "is-active" : "";
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  // if (result.type === "fail") {
+  //   switch (result.case) {
+  //     case HttpStatusCode.NotFound: {
+  //       return <NotFoundPageOrAdminCreate />;
+  //     }
+  //     default: {
+  //       assertUnreachable(result.case);
+  //     }
+  //   }
+  // }
+
+  const isNotPaidActive = filter.paid === false ? "is-active" : "";
+  const isPaidActive =
+    filter.paid === true && filter.wired === undefined ? "is-active" : "";
+  const isRefundedActive = filter.refunded === true ? "is-active" : "";
+  const isNotWiredActive =
+    filter.wired === false && filter.paid === true ? "is-active" : "";
+  const isWiredActive = filter.wired === true ? "is-active" : "";
   const isAllActive =
     filter.paid === undefined &&
-      filter.refunded === undefined &&
-      filter.wired === undefined
+    filter.refunded === undefined &&
+    filter.wired === undefined
       ? "is-active"
       : "";
 
@@ -105,11 +106,8 @@ export default function OrderList({
 
       <JumpToElementById
         testIfExist={async (order) => {
-          if (state.status !== "loggedIn") {
-            return false;
-          }
-          const resp = await lib.management.getOrder(state.token, order)
-          return resp.type === "ok"
+          const resp = await lib.management.getOrderDetails(state.token, 
order);
+          return resp.type === "ok";
         }}
         onSelect={onSelect}
         description={i18n.str`jump to order with the given product ID`}
@@ -117,11 +115,11 @@ export default function OrderList({
       />
 
       <ListPage
-        orders={result.data.orders.map((o) => ({ ...o, id: o.order_id }))}
-        onLoadMoreBefore={result.loadMorePrev}
-        hasMoreBefore={!result.isReachingStart}
-        onLoadMoreAfter={result.loadMore}
-        hasMoreAfter={!result.isReachingEnd}
+        orders={result.body.map((o) => ({ ...o, id: o.order_id }))}
+        onLoadMoreBefore={result.loadFirst}
+        hasMoreBefore={!result.isFirstPage}
+        onLoadMoreAfter={result.loadNext}
+        hasMoreAfter={!result.isLastPage}
         onSelectOrder={(order) => onSelect(order.id)}
         onRefundOrder={(value) => setOrderToBeRefunded(value)}
         isAllActive={isAllActive}
@@ -131,30 +129,27 @@ export default function OrderList({
         isNotPaidActive={isNotPaidActive}
         isRefundedActive={isRefundedActive}
         jumpToDate={filter.date}
+        onSelectDate={setNewDate}
         onCopyURL={async (id) => {
-          if (state.status !== "loggedIn") {
-            return false;
-          }
-          const resp = await lib.management.getOrder(state.token, id)
+          const resp = await lib.management.getOrderDetails(state.token, id);
           if (resp.type === "ok") {
             if (resp.body.order_status === "unpaid") {
-              copyToClipboard(resp.body.taler_pay_uri)
+              copyToClipboard(resp.body.taler_pay_uri);
             } else {
               if (resp.body.contract_terms.fulfillment_url) {
-                copyToClipboard(resp.body.contract_terms.fulfillment_url)
+                copyToClipboard(resp.body.contract_terms.fulfillment_url);
               }
             }
-            copyToClipboard(resp.body.order_status)
+            copyToClipboard(resp.body.order_status);
           }
         }}
         onCreate={onCreate}
-        onSelectDate={setNewDate}
         onShowAll={() => setFilter({})}
-        onShowNotPaid={() => setFilter({ paid: "no" })}
-        onShowPaid={() => setFilter({ paid: "yes" })}
-        onShowRefunded={() => setFilter({ refunded: "yes" })}
-        onShowNotWired={() => setFilter({ wired: "no", paid: "yes" })}
-        onShowWired={() => setFilter({ wired: "yes" })}
+        onShowNotPaid={() => setFilter({ paid: false })}
+        onShowPaid={() => setFilter({ paid: true })}
+        onShowRefunded={() => setFilter({ refunded: true })}
+        onShowNotWired={() => setFilter({ wired: false, paid: true })}
+        onShowWired={() => setFilter({ wired: true })}
       />
 
       {orderToBeRefunded && (
@@ -162,7 +157,8 @@ export default function OrderList({
           id={orderToBeRefunded.order_id}
           onCancel={() => setOrderToBeRefunded(undefined)}
           onConfirm={(value) => {
-            lib.management.addRefund(state.token, orderToBeRefunded.order_id, 
value)
+            lib.management
+              .addRefund(state.token, orderToBeRefunded.order_id, value)
               .then(() =>
                 setNotif({
                   message: i18n.str`refund created successfully`,
@@ -176,27 +172,7 @@ export default function OrderList({
                   description: error.message,
                 }),
               )
-              .then(() => setOrderToBeRefunded(undefined))
-
-          }}
-          onLoadError={(error) => {
-            setNotif({
-              message: i18n.str`could not create the refund`,
-              type: "ERROR",
-              description: error.message,
-            });
-            setOrderToBeRefunded(undefined);
-            return <div />;
-          }}
-          onUnauthorized={onUnauthorized}
-          onNotFound={() => {
-            setNotif({
-              message: i18n.str`could not get the order to refund`,
-              type: "ERROR",
-              // description: error.message
-            });
-            setOrderToBeRefunded(undefined);
-            return <div />;
+              .then(() => setOrderToBeRefunded(undefined));
           }}
         />
       )}
@@ -206,41 +182,39 @@ export default function OrderList({
 
 interface RefundProps {
   id: string;
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onCancel: () => void;
   onConfirm: (m: TalerMerchantApi.RefundRequest) => void;
 }
 
-function RefundModalForTable({
-  id,
-  onUnauthorized,
-  onLoadError,
-  onNotFound,
-  onConfirm,
-  onCancel,
-}: RefundProps): VNode {
+function RefundModalForTable({ id, onConfirm, onCancel }: RefundProps): VNode {
   const result = useOrderDetails(id);
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      case HttpStatusCode.BadGateway: {
+        return <div>Failed to obtain a response from the exchange</div>;
+      }
+      case HttpStatusCode.GatewayTimeout: {
+        return (
+          <div>The merchant's interaction with the exchange took too long</div>
+        );
+      }
+      default: {
+        assertUnreachable(result);
+      }
+    }
   }
 
   return (
     <RefundModal
-      order={result.data}
+      order={result.body}
       onCancel={onCancel}
       onConfirm={onConfirm}
     />
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
index 06a9f4a55..c240b99cb 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/list/index.tsx
@@ -19,57 +19,53 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
+  HttpStatusCode,
+  TalerError,
+  TalerMerchantApi,
+  assertUnreachable
+} from "@gnu-taler/taler-util";
+import {
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { useSessionContext } from "../../../../context/session.js";
 import { useInstanceOtpDevices } from "../../../../hooks/otp.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { ListPage } from "./ListPage.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
 }
 
-export default function ListOtpDevices({
-  onUnauthorized,
-  onLoadError,
-  onCreate,
-  onSelect,
-  onNotFound,
-}: Props): VNode {
-  const [position, setPosition] = useState<string | undefined>(undefined);
+export default function ListOtpDevices({ onCreate, onSelect }: Props): VNode {
+  // const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { lib } = useMerchantApiContext();
   const { state } = useSessionContext();
-  const result = useInstanceOtpDevices({ position }, (id) => setPosition(id));
+  const result = useInstanceOtpDevices();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   return (
@@ -77,17 +73,16 @@ export default function ListOtpDevices({
       <NotificationCard notification={notif} />
 
       <ListPage
-        devices={result.data.otp_devices}
-        onLoadMoreBefore={
-          result.isReachingStart ? result.loadMorePrev : undefined
-        }
-        onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+        devices={result.body}
+        onLoadMoreBefore={result.isFirstPage ? undefined : result.loadFirst}
+        onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
         onCreate={onCreate}
         onSelect={(e) => {
           onSelect(e.otp_device_id);
         }}
         onDelete={(e: TalerMerchantApi.OtpDeviceEntry) => {
-          return lib.management.deleteOtpDevice(state.token, e.otp_device_id)
+          return lib.management
+            .deleteOtpDevice(state.token, e.otp_device_id)
             .then(() =>
               setNotif({
                 message: i18n.str`validator delete successfully`,
@@ -100,7 +95,7 @@ export default function ListOtpDevices({
                 type: "ERROR",
                 description: error.message,
               }),
-            )
+            );
         }}
       />
     </Fragment>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
index 6b9970eab..43adbe253 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/otp_devices/update/index.tsx
@@ -19,20 +19,25 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
+  HttpStatusCode,
+  TalerError,
+  TalerMerchantApi,
+  assertUnreachable
+} from "@gnu-taler/taler-util";
+import {
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import { useSessionContext } from "../../../../context/session.js";
 import { useOtpDeviceDetails } from "../../../../hooks/otp.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { CreatedSuccessfully } from "../create/CreatedSuccessfully.js";
 import { UpdatePage } from "./UpdatePage.js";
 
@@ -41,44 +46,39 @@ export type Entity = TalerMerchantApi.OtpDevicePatchDetails 
& WithId;
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   vid: string;
 }
 export default function UpdateValidator({
   vid,
   onConfirm,
   onBack,
-  onUnauthorized,
-  onNotFound,
-  onLoadError,
 }: Props): VNode {
   const result = useOtpDeviceDetails(vid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  const [keyUpdated, setKeyUpdated] = 
useState<TalerMerchantApi.OtpDeviceAddDetails | null>(null)
+  const [keyUpdated, setKeyUpdated] =
+    useState<TalerMerchantApi.OtpDeviceAddDetails | null>(null);
   const { lib } = useMerchantApiContext();
   const { state } = useSessionContext();
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   if (keyUpdated) {
-    return <CreatedSuccessfully entity={keyUpdated} onConfirm={onConfirm} />
+    return <CreatedSuccessfully entity={keyUpdated} onConfirm={onConfirm} />;
   }
 
   return (
@@ -87,25 +87,47 @@ export default function UpdateValidator({
       <UpdatePage
         device={{
           id: vid,
-          otp_algorithm: result.data.otp_algorithm,
-          otp_device_description: result.data.device_description,
+          otp_algorithm: result.body.otp_algorithm,
+          otp_device_description: result.body.device_description,
           otp_key: "",
-          otp_ctr: result.data.otp_ctr
+          otp_ctr: result.body.otp_ctr,
         }}
         onBack={onBack}
         onUpdate={async (newInfo) => {
-          return lib.management.updateOtpDevice(state.token, vid, newInfo)
+          return lib.management
+            .updateOtpDevice(state.token, vid, newInfo)
             .then((d) => {
-              if (newInfo.otp_key) {
-                setKeyUpdated({
-                  otp_algorithm: newInfo.otp_algorithm,
-                  otp_device_description: newInfo.otp_device_description,
-                  otp_device_id: newInfo.id,
-                  otp_key: newInfo.otp_key,
-                  otp_ctr: newInfo.otp_ctr,
-                })
+              if (d.type === "ok") {
+                if (newInfo.otp_key) {
+                  setKeyUpdated({
+                    otp_algorithm: newInfo.otp_algorithm,
+                    otp_device_description: newInfo.otp_device_description,
+                    otp_device_id: newInfo.id,
+                    otp_key: newInfo.otp_key,
+                    otp_ctr: newInfo.otp_ctr,
+                  });
+                } else {
+                  onConfirm();
+                }
               } else {
-                onConfirm()
+                switch(d.case) {
+                  case HttpStatusCode.NotFound: {
+                    setNotif({
+                      message: i18n.str`Could not update template`,
+                      type: "ERROR",
+                      description: i18n.str`Template id is unknown`,
+                    });
+                    break;
+                  }
+                  case HttpStatusCode.Conflict: {
+                    setNotif({
+                      message: i18n.str`Could not update template`,
+                      type: "ERROR",
+                      description: i18n.str`The provided information is 
inconsistent with the current state of the template`,
+                    });
+                    break;
+                  }
+                }
               }
             })
             .catch((error) => {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
index dc2e08d91..eb38f25d9 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/products/list/index.tsx
@@ -19,15 +19,14 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { JumpToElementById } from 
"../../../../components/form/JumpToElementById.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
@@ -37,21 +36,16 @@ import {
   useInstanceProducts
 } from "../../../../hooks/product.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { CardTable } from "./Table.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
 }
 export default function ProductList({
-  onUnauthorized,
-  onLoadError,
   onCreate,
   onSelect,
-  onNotFound,
 }: Props): VNode {
   const result = useInstanceProducts();
   const { lib } = useMerchantApiContext();
@@ -62,19 +56,19 @@ export default function ProductList({
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   return (
@@ -83,7 +77,7 @@ export default function ProductList({
 
       <JumpToElementById
         testIfExist={async (id) => {
-          const resp = await lib.management.getProduct(state.token, id);
+          const resp = await lib.management.getProductDetails(state.token, id);
           return resp.type === "ok";
         }}
         onSelect={onSelect}
@@ -92,7 +86,7 @@ export default function ProductList({
       />
 
       <CardTable
-        instances={result.data}
+        instances={result.body}
         onCreate={onCreate}
         onUpdate={async (id, prod) => {
           try {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx
index b9470ddac..9de632218 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/products/update/index.tsx
@@ -19,38 +19,32 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import { useProductDetails } from "../../../../hooks/product.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 export type Entity = TalerMerchantApi.ProductAddDetail;
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   pid: string;
 }
 export default function UpdateProduct({
   pid,
   onConfirm,
   onBack,
-  onUnauthorized,
-  onNotFound,
-  onLoadError,
 }: Props): VNode {
   const result = useProductDetails(pid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
@@ -59,26 +53,26 @@ export default function UpdateProduct({
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   return (
     <Fragment>
       <NotificationCard notification={notif} />
       <UpdatePage
-        product={{ ...result.data, product_id: pid }}
+        product={{ ...result.body, product_id: pid }}
         onBack={onBack}
         onUpdate={(data) => {
           return lib.management.updateProduct(state.token, pid, data)
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
index 31e525226..82c0d0e53 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/create/CreatePage.tsx
@@ -23,6 +23,7 @@ import {
   AmountString,
   Amounts,
   Duration,
+  TalerError,
   TalerMerchantApi,
   assertUnreachable,
 } from "@gnu-taler/taler-util";
@@ -126,7 +127,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
   };
 
   const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
+    (k) => (errors as Record<string,unknown>)[k] !== undefined,
   );
 
   const submitForm = () => {
@@ -185,7 +186,7 @@ export function CreatePage({ onCreate, onBack }: Props): 
VNode {
       // return onCreate(state);
     }
   };
-  const deviceList = !devices.ok ? [] : devices.data.otp_devices;
+  const deviceList = !devices || devices instanceof TalerError || devices.type 
=== "fail" ? [] : devices.body;
 
   return (
     <div>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
index adbfb93cd..5a8be71b0 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/list/index.tsx
@@ -19,15 +19,14 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
+import { VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { JumpToElementById } from 
"../../../../components/form/JumpToElementById.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
@@ -37,12 +36,10 @@ import {
   useInstanceTemplates
 } from "../../../../hooks/templates.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { ListPage } from "./ListPage.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
   onNewOrder: (id: string) => void;
@@ -50,36 +47,32 @@ interface Props {
 }
 
 export default function ListTemplates({
-  onUnauthorized,
-  onLoadError,
   onCreate,
   onQR,
   onSelect,
   onNewOrder,
-  onNotFound,
 }: Props): VNode {
-  const [position, setPosition] = useState<string | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { lib } = useMerchantApiContext();
-  const result = useInstanceTemplates({ position }, (id) => setPosition(id));
+  const result = useInstanceTemplates();
   const [deleting, setDeleting] =
     useState<TalerMerchantApi.TemplateEntry | null>(null);
   const { state } = useSessionContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
+  }
+  if (result.type === "fail") {
+    switch(result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />
+      }
+      default: {
+        assertUnreachable(result.case)
+      }
+    }
   }
 
   return (
@@ -88,7 +81,7 @@ export default function ListTemplates({
 
       <JumpToElementById
         testIfExist={async (id) => {
-          const resp = await lib.management.getTemplate(state.token, id)
+          const resp = await lib.management.getTemplateDetails(state.token, id)
           return resp.type === "ok"
         }}
         onSelect={onSelect}
@@ -97,11 +90,11 @@ export default function ListTemplates({
       />
 
       <ListPage
-        templates={result.data.templates}
+        templates={result.body}
         onLoadMoreBefore={
-          result.isReachingStart ? result.loadMorePrev : undefined
+          result.isFirstPage ? undefined: result.loadFirst
         }
-        onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+        onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
         onCreate={onCreate}
         onSelect={(e) => {
           onSelect(e.template_id);
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
index 37f0e5c74..3464fb04e 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/qr/index.tsx
@@ -19,59 +19,44 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
-import {
-  ErrorType,
-  HttpError
-} from "@gnu-taler/web-util/browser";
-import { Fragment, VNode, h } from "preact";
-import { useState } from "preact/hooks";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
+import { VNode, h } from "preact";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
-import { NotificationCard } from "../../../../components/menu/index.js";
 import {
   useTemplateDetails
 } from "../../../../hooks/templates.js";
-import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { QrPage } from "./QrPage.js";
 
 export type Entity = TalerMerchantApi.TransferInformation;
 interface Props {
   onBack?: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   tid: string;
 }
 
 export default function TemplateQrPage({
   tid,
   onBack,
-  onLoadError,
-  onNotFound,
-  onUnauthorized,
 }: Props): VNode {
   const result = useTemplateDetails(tid);
-  const [notif, setNotif] = useState<Notification | undefined>(undefined);
-
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
   }
+  if (result.type === "fail") {
+    switch(result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />
+      }
+      default: {
+        assertUnreachable(result.case)
+      }
+    }
+  }
+
 
   return (
-    <>
-      <NotificationCard notification={notif} />
-      <QrPage contract={result.data.template_contract} id={tid} 
onBack={onBack} />
-    </>
+      <QrPage contract={result.body.template_contract} id={tid} 
onBack={onBack} />
   );
 }
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
index f4092b61b..cf1c13fc4 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/UpdatePage.tsx
@@ -23,6 +23,7 @@ import {
   AmountString,
   Amounts,
   Duration,
+  TalerError,
   TalerMerchantApi,
   assertUnreachable
 } from "@gnu-taler/taler-util";
@@ -91,7 +92,7 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
     type: intialStep,
   });
   const devices = useInstanceOtpDevices()
-  const deviceList = !devices.ok ? [] : devices.data.otp_devices
+  const deviceList = !devices || devices instanceof TalerError || devices.type 
=== "fail" ? [] : devices.body
 
   const parsedPrice = !state.amount
     ? undefined
@@ -129,7 +130,7 @@ export function UpdatePage({ template, onUpdate, onBack }: 
Props): VNode {
   };
 
   const hasErrors = Object.keys(errors).some(
-    (k) => (errors as any)[k] !== undefined,
+    (k) => (errors as Record<string,unknown>)[k] !== undefined,
   );
 
   const submitForm = () => {
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
index fc436056a..1ff4b56cf 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/update/index.tsx
@@ -19,41 +19,35 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import {
   useTemplateDetails,
 } from "../../../../hooks/templates.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 export type Entity = TalerMerchantApi.TemplatePatchDetails & WithId;
 
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   tid: string;
 }
 export default function UpdateTemplate({
   tid,
   onConfirm,
   onBack,
-  onUnauthorized,
-  onNotFound,
-  onLoadError,
 }: Props): VNode {
   const { lib } = useMerchantApiContext();
   const { state } = useSessionContext();
@@ -62,26 +56,26 @@ export default function UpdateTemplate({
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
+  }
+  if (result.type === "fail") {
+    switch(result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />
+      }
+      default: {
+        assertUnreachable(result.case)
+      }
+    }
   }
 
   return (
     <Fragment>
       <NotificationCard notification={notif} />
       <UpdatePage
-        template={{ ...result.data }}
+        template={result.body}
         onBack={onBack}
         onUpdate={(data) => {
           return lib.management.updateTemplate(state.token, tid, data)
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
index dd5d4aea3..0073ca574 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/templates/use/index.tsx
@@ -19,31 +19,27 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
 import {
   useTemplateDetails
 } from "../../../../hooks/templates.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UsePage } from "./UsePage.js";
-import { useSessionContext } from "../../../../context/session.js";
 
 export type Entity = TalerMerchantApi.TransferInformation;
 interface Props {
   onBack?: () => void;
   onOrderCreated: (id: string) => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   tid: string;
 }
 
@@ -51,36 +47,32 @@ export default function TemplateUsePage({
   tid,
   onOrderCreated,
   onBack,
-  onLoadError,
-  onNotFound,
-  onUnauthorized,
 }: Props): VNode {
   const { lib } = useMerchantApiContext();
-  const { state } = useSessionContext();
   const result = useTemplateDetails(tid);
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />
+  }
+  if (result.type === "fail") {
+    switch(result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />
+      }
+      default: {
+        assertUnreachable(result.case)
+      }
+    }
   }
 
   return (
     <>
       <NotificationCard notification={notif} />
       <UsePage
-        template={result.data}
+        template={result.body}
         id={tid}
         onBack={onBack}
         onCreateOrder={(
diff --git a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
index 16a4bda22..444283b13 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/token/index.tsx
@@ -13,38 +13,31 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { HttpStatusCode, TalerError, TalerErrorDetail } from 
"@gnu-taler/taler-util";
-import { ErrorType, HttpError, useMerchantApiContext, useTranslationContext } 
from "@gnu-taler/web-util/browser";
+import { TalerError } from "@gnu-taler/taler-util";
+import { useMerchantApiContext, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../components/exception/loading.js";
 import { NotificationCard } from "../../../components/menu/index.js";
 import { useSessionContext } from "../../../context/session.js";
 import { useInstanceDetails } from "../../../hooks/instance.js";
 import { Notification } from "../../../utils/types.js";
 import { DetailPage } from "./DetailPage.js";
-import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
   onChange: () => void;
-  onNotFound: () => VNode;
   onCancel: () => void;
 }
 
 export default function Token({
-  onLoadError,
   onChange,
-  onUnauthorized,
-  onNotFound,
   onCancel,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
   const { lib } = useMerchantApiContext();
   const { logIn } = useSessionContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
-  // const { clearAccessToken } = useInstanceAPI();
   const result = useInstanceDetails()
 
   if (!result) return <Loading />
@@ -52,21 +45,6 @@ export default function Token({
     return <ErrorLoadingMerchant error={result} />
   }
 
-  // if (result.loading) return <Loading />;
-  // if (!result.ok) {
-  //   if (
-  //     result.type === ErrorType.CLIENT &&
-  //     result.status === HttpStatusCode.Unauthorized
-  //   )
-  //     return onUnauthorized();
-  //   if (
-  //     result.type === ErrorType.CLIENT &&
-  //     result.status === HttpStatusCode.NotFound
-  //   )
-  //     return onNotFound();
-  //   return onLoadError(result);
-  // }
-
   const hasToken = result.body.auth.method === "token"
 
   return (
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
index ac1d692a4..35389f5f5 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/create/index.tsx
@@ -19,8 +19,11 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { TalerMerchantApi } from "@gnu-taler/taler-util";
-import { useMerchantApiContext, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { TalerError, TalerMerchantApi } from "@gnu-taler/taler-util";
+import {
+  useMerchantApiContext,
+  useTranslationContext,
+} from "@gnu-taler/web-util/browser";
 import { Fragment, h, VNode } from "preact";
 import { useState } from "preact/hooks";
 import { NotificationCard } from "../../../../components/menu/index.js";
@@ -41,9 +44,10 @@ export default function CreateTransfer({ onConfirm, onBack 
}: Props): VNode {
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { i18n } = useTranslationContext();
   const instance = useInstanceBankAccounts();
-  const accounts = !instance.ok
-    ? []
-    : instance.data.accounts.map((a) => a.payto_uri);
+  const accounts =
+    !instance || instance instanceof TalerError || instance.type === "fail"
+      ? []
+      : instance.body.map((a) => a.payto_uri);
 
   return (
     <>
@@ -52,7 +56,8 @@ export default function CreateTransfer({ onConfirm, onBack }: 
Props): VNode {
         onBack={onBack}
         accounts={accounts}
         onCreate={(request: TalerMerchantApi.TransferInformation) => {
-          return lib.management.informWireTransfer(state.token, request)
+          return lib.management
+            .informWireTransfer(state.token, request)
             .then(() => onConfirm())
             .catch((error) => {
               setNotif({
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
index 15706b4c5..5edea377f 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/transfers/list/index.tsx
@@ -19,40 +19,34 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { HttpStatusCode, TalerErrorDetail } from "@gnu-taler/taler-util";
-import { ErrorType, HttpError } from "@gnu-taler/web-util/browser";
+import { TalerError } from "@gnu-taler/taler-util";
 import { VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { useInstanceBankAccounts } from "../../../../hooks/bank.js";
 import { useInstanceTransfers } from "../../../../hooks/transfer.js";
 import { ListPage } from "./ListPage.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onCreate: () => void;
 }
 interface Form {
-  verified?: "yes" | "no";
+  verified?: boolean;
   payto_uri?: string;
 }
 
 export default function ListTransfer({
-  onUnauthorized,
-  onLoadError,
   onCreate,
-  onNotFound,
 }: Props): VNode {
-  const setFilter = (s?: "yes" | "no") => setForm({ ...form, verified: s });
+  const setFilter = (s?: boolean) => setForm({ ...form, verified: s });
 
   const [position, setPosition] = useState<string | undefined>(undefined);
 
   const instance = useInstanceBankAccounts();
-  const accounts = !instance.ok
+  const accounts = !instance || (instance instanceof TalerError) || 
instance.type === "fail"
     ? []
-    : instance.data.accounts.map((a) => a.payto_uri);
+    : instance.body.map((a) => a.payto_uri);
   const [form, setForm] = useState<Form>({ payto_uri: "" });
 
   const shoulUseDefaultAccount = accounts.length === 1
@@ -62,8 +56,8 @@ export default function ListTransfer({
     }
   }, [shoulUseDefaultAccount])
 
-  const isVerifiedTransfers = form.verified === "yes";
-  const isNonVerifiedTransfers = form.verified === "no";
+  const isVerifiedTransfers = form.verified === true;
+  const isNonVerifiedTransfers = form.verified === false;
   const isAllTransfers = form.verified === undefined;
 
   const result = useInstanceTransfers(
@@ -75,37 +69,35 @@ export default function ListTransfer({
     (id) => setPosition(id),
   );
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
   }
+  // if (result.type === "fail") {
+  //   switch (result.case) {
+  //     case HttpStatusCode.NotFound: {
+  //       return <NotFoundPageOrAdminCreate />;
+  //     }
+  //     default: {
+  //       assertUnreachable(result.case);
+  //     }
+  //   }
+  // }
+
 
   return (
     <ListPage
       accounts={accounts}
-      transfers={result.data.transfers}
-      onLoadMoreBefore={
-        result.isReachingStart ? result.loadMorePrev : undefined
-      }
-      onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+      transfers={result.body}
+      onLoadMoreBefore={result.isFirstPage ? undefined: result.loadFirst }
+      onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
       onCreate={onCreate}
       onDelete={() => {
         null;
       }}
-      // position={position} setPosition={setPosition}
       onShowAll={() => setFilter(undefined)}
-      onShowUnverified={() => setFilter("no")}
-      onShowVerified={() => setFilter("yes")}
+      onShowUnverified={() => setFilter(false)}
+      onShowVerified={() => setFilter(true)}
       isAllTransfers={isAllTransfers}
       isVerifiedTransfers={isVerifiedTransfers}
       isNonVerifiedTransfers={isNonVerifiedTransfers}
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
index 290bfa214..6dc5b9b02 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/update/index.tsx
@@ -13,35 +13,32 @@
  You should have received a copy of the GNU General Public License along with
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
-import { HttpStatusCode, OperationOk, TalerError, TalerErrorDetail, 
TalerMerchantApi, TalerMerchantInstanceHttpClient } from 
"@gnu-taler/taler-util";
+import { OperationOk, TalerError, TalerMerchantApi, 
TalerMerchantInstanceHttpClient } from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
-  HttpResponse,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../components/exception/loading.js";
 import { NotificationCard } from "../../../components/menu/index.js";
+import { useSessionContext } from "../../../context/session.js";
 import {
   useInstanceDetails,
   useManagedInstanceDetails,
 } from "../../../hooks/instance.js";
 import { Notification } from "../../../utils/types.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { useSessionContext } from "../../../context/session.js";
-import { ErrorLoadingMerchant } from 
"../../../components/ErrorLoadingMerchant.js";
 
 export interface Props {
   onBack: () => void;
   onConfirm: () => void;
 
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
-  onUpdateError: (e: HttpError<TalerErrorDetail>) => void;
+  // onUnauthorized: () => VNode;
+  // onNotFound: () => VNode;
+  // onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
+  // onUpdateError: (e: HttpError<TalerErrorDetail>) => void;
 }
 
 export default function Update(props: Props): VNode {
@@ -64,9 +61,6 @@ function CommonUpdate(
   {
     onBack,
     onConfirm,
-    onLoadError,
-    onNotFound,
-    onUnauthorized,
   }: Props,
   result: OperationOk<TalerMerchantApi.QueryInstancesResponse> | TalerError | 
undefined,
   updateInstance: typeof 
TalerMerchantInstanceHttpClient.prototype.updateCurrentInstance,
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
index 2923a8096..7c24a7228 100644
--- a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/list/index.tsx
@@ -20,58 +20,51 @@
  */
 
 import {
-  ErrorType,
-  HttpError,
+  HttpStatusCode,
+  TalerError,
+  TalerMerchantApi,
+  assertUnreachable,
+} from "@gnu-taler/taler-util";
+import {
   useMerchantApiContext,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
-import {
-  useInstanceWebhooks,
-} from "../../../../hooks/webhooks.js";
+import { useSessionContext } from "../../../../context/session.js";
+import { useInstanceWebhooks } from "../../../../hooks/webhooks.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { ListPage } from "./ListPage.js";
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
-import { useSessionContext } from "../../../../context/session.js";
 
 interface Props {
-  onUnauthorized: () => VNode;
-  onLoadError: (error: HttpError<TalerErrorDetail>) => VNode;
-  onNotFound: () => VNode;
   onCreate: () => void;
   onSelect: (id: string) => void;
 }
 
-export default function ListWebhooks({
-  onUnauthorized,
-  onLoadError,
-  onCreate,
-  onSelect,
-  onNotFound,
-}: Props): VNode {
-  const [position, setPosition] = useState<string | undefined>(undefined);
+export default function ListWebhooks({ onCreate, onSelect }: Props): VNode {
   const { i18n } = useTranslationContext();
   const [notif, setNotif] = useState<Notification | undefined>(undefined);
   const { lib } = useMerchantApiContext();
   const { state } = useSessionContext();
-  const result = useInstanceWebhooks({ position }, (id) => setPosition(id));
+  const result = useInstanceWebhooks();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   return (
@@ -79,17 +72,16 @@ export default function ListWebhooks({
       <NotificationCard notification={notif} />
 
       <ListPage
-        webhooks={result.data.webhooks}
-        onLoadMoreBefore={
-          result.isReachingStart ? result.loadMorePrev : undefined
-        }
-        onLoadMoreAfter={result.isReachingEnd ? result.loadMore : undefined}
+        webhooks={result.body}
+        onLoadMoreBefore={result.isFirstPage ? undefined : result.loadFirst}
+        onLoadMoreAfter={result.isLastPage ? undefined : result.loadNext}
         onCreate={onCreate}
         onSelect={(e) => {
           onSelect(e.webhook_id);
         }}
         onDelete={(e: TalerMerchantApi.WebhookEntry) => {
-          return lib.management.deleteWebhook(state.token, e.webhook_id)
+          return lib.management
+            .deleteWebhook(state.token, e.webhook_id)
             .then(() =>
               setNotif({
                 message: i18n.str`webhook delete successfully`,
@@ -102,7 +94,7 @@ export default function ListWebhooks({
                 type: "ERROR",
                 description: error.message,
               }),
-            )
+            );
         }}
       />
     </Fragment>
diff --git 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
index aecb4b947..1c3172ffd 100644
--- 
a/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
+++ 
b/packages/merchant-backoffice-ui/src/paths/instance/webhooks/update/index.tsx
@@ -19,41 +19,35 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { HttpStatusCode, TalerError, TalerMerchantApi, assertUnreachable } 
from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
   useMerchantApiContext,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { ErrorLoadingMerchant } from 
"../../../../components/ErrorLoadingMerchant.js";
 import { Loading } from "../../../../components/exception/loading.js";
 import { NotificationCard } from "../../../../components/menu/index.js";
+import { useSessionContext } from "../../../../context/session.js";
 import {
   useWebhookDetails,
 } from "../../../../hooks/webhooks.js";
 import { Notification } from "../../../../utils/types.js";
+import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
 import { UpdatePage } from "./UpdatePage.js";
-import { HttpStatusCode, TalerErrorDetail, TalerMerchantApi } from 
"@gnu-taler/taler-util";
-import { useSessionContext } from "../../../../context/session.js";
 
 export type Entity = TalerMerchantApi.WebhookPatchDetails & WithId;
 
 interface Props {
   onBack?: () => void;
   onConfirm: () => void;
-  onUnauthorized: () => VNode;
-  onNotFound: () => VNode;
-  onLoadError: (e: HttpError<TalerErrorDetail>) => VNode;
   tid: string;
 }
 export default function UpdateWebhook({
   tid,
   onConfirm,
   onBack,
-  onUnauthorized,
-  onNotFound,
-  onLoadError,
 }: Props): VNode {
   const { lib } = useMerchantApiContext();
   const { state } = useSessionContext();
@@ -62,26 +56,26 @@ export default function UpdateWebhook({
 
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <Loading />;
-  if (!result.ok) {
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.Unauthorized
-    )
-      return onUnauthorized();
-    if (
-      result.type === ErrorType.CLIENT &&
-      result.status === HttpStatusCode.NotFound
-    )
-      return onNotFound();
-    return onLoadError(result);
+  if (!result) return <Loading />;
+  if (result instanceof TalerError) {
+    return <ErrorLoadingMerchant error={result} />;
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case HttpStatusCode.NotFound: {
+        return <NotFoundPageOrAdminCreate />;
+      }
+      default: {
+        assertUnreachable(result.case);
+      }
+    }
   }
 
   return (
     <Fragment>
       <NotificationCard notification={notif} />
       <UpdatePage
-        webhook={{ ...result.data, id: tid }}
+        webhook={{ ...result.body, id: tid }}
         onBack={onBack}
         onUpdate={(data) => {
           return lib.management.updateWebhook(state.token, tid, data)
diff --git a/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx 
b/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx
index 68adb79bf..d780b5988 100644
--- a/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx
+++ b/packages/merchant-backoffice-ui/src/paths/notfound/index.tsx
@@ -19,10 +19,18 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-import { h, VNode } from "preact";
-import { Link } from "preact-router";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, h, VNode } from "preact";
+import { Link, route } from "preact-router";
+import { NotificationCard } from "../../components/menu/index.js";
+import {
+  DEFAULT_ADMIN_USERNAME,
+  useSessionContext,
+} from "../../context/session.js";
+import InstanceCreatePage from "../../paths/admin/create/index.js";
+import { InstancePaths } from "../../Routing.js";
 
-export default function NotFoundPage(): VNode {
+export function NotFoundPage(): VNode {
   return (
     <div>
       <p>That page doesn&apos;t exist.</p>
@@ -32,3 +40,29 @@ export default function NotFoundPage(): VNode {
     </div>
   );
 }
+
+export function NotFoundPageOrAdminCreate(): VNode {
+  const { state } = useSessionContext();
+  const { i18n } = useTranslationContext();
+  if (state.isAdmin && state.instance === DEFAULT_ADMIN_USERNAME) {
+    return (
+      <Fragment>
+        <NotificationCard
+          notification={{
+            message: i18n.str`No 'default' instance configured yet.`,
+            description: i18n.str`Create a 'default' instance to begin using 
the merchant backoffice.`,
+            type: "INFO",
+          }}
+        />
+        <InstanceCreatePage
+          forceId={DEFAULT_ADMIN_USERNAME}
+          onConfirm={() => {
+            route(InstancePaths.bank_list);
+          }}
+        />
+      </Fragment>
+    );
+  }
+
+  return <NotFoundPage />
+}
diff --git a/packages/taler-harness/src/index.ts 
b/packages/taler-harness/src/index.ts
index daa246c80..727c3b674 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -729,7 +729,7 @@ deploymentCli
      * link bank account and merchant
      */
     {
-      const resp = await merchantInstance.addAccount(password as AccessToken, {
+      const resp = await merchantInstance.addBankAccount(password as 
AccessToken, {
         payto_uri: accountPayto,
         credit_facade_url: bank.getRevenueAPI(id).href,
         credit_facade_credentials: {
@@ -847,7 +847,7 @@ deploymentCli
       }
 
       {
-        const resp = await merchantInstance.updateAccount(randomPassword as 
AccessToken, wireAccount, {
+        const resp = await merchantInstance.updateBankAccount(randomPassword 
as AccessToken, wireAccount, {
           credit_facade_url: bank.getRevenueAPI(id).href,
           credit_facade_credentials: {
             type: "basic",
@@ -934,7 +934,7 @@ deploymentCli
       process.exit(2);
     }
 
-    const createAccountResp = await api.addAccount(instanceToken, {
+    const createAccountResp = await api.addBankAccount(instanceToken, {
       payto_uri: accountPayto,
       credit_facade_url: bankURL,
       credit_facade_credentials: bankUser && bankPassword ? {
diff --git a/packages/taler-util/src/http-client/merchant.ts 
b/packages/taler-util/src/http-client/merchant.ts
index 5fd001555..c223b0a16 100644
--- a/packages/taler-util/src/http-client/merchant.ts
+++ b/packages/taler-util/src/http-client/merchant.ts
@@ -512,7 +512,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#post-[-instances-$INSTANCE]-private-accounts
    */
-  async addAccount(token: AccessToken | undefined, body: 
TalerMerchantApi.AccountAddDetails) {
+  async addBankAccount(token: AccessToken | undefined, body: 
TalerMerchantApi.AccountAddDetails) {
     const url = new URL(`private/accounts`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -540,7 +540,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#patch-[-instances-$INSTANCE]-private-accounts-$H_WIRE
    */
-  async updateAccount(
+  async updateBankAccount(
     token: AccessToken | undefined,
     wireAccount: string,
     body: TalerMerchantApi.AccountPatchDetails,
@@ -569,9 +569,11 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts
    */
-  async listAccounts(token: AccessToken) {
+  async listBankAccounts(token: AccessToken, params?: PaginationParams) {
     const url = new URL(`private/accounts`, this.baseUrl);
 
+    // addMerchantPaginationParams(url, params);
+
     const headers: Record<string, string> = {}
     if (token) {
       headers.Authorization = makeBearerTokenAuthHeader(token)
@@ -594,7 +596,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-accounts-$H_WIRE
    */
-  async getAccount(token: AccessToken | undefined, wireAccount: string) {
+  async getBankAccountDetails(token: AccessToken | undefined, wireAccount: 
string) {
     const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -619,7 +621,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#delete-[-instances-$INSTANCE]-private-accounts-$H_WIRE
    */
-  async deleteAccount(token: AccessToken | undefined, wireAccount: string) {
+  async deleteBankAccount(token: AccessToken | undefined, wireAccount: string) 
{
     const url = new URL(`private/accounts/${wireAccount}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -733,7 +735,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-products-$PRODUCT_ID
    */
-  async getProduct(token: AccessToken | undefined, productId: string) {
+  async getProductDetails(token: AccessToken | undefined, productId: string) {
     const url = new URL(`private/products/${productId}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -902,7 +904,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-orders-$ORDER_ID
    */
-  async getOrder(
+  async getOrderDetails(
     token: AccessToken | undefined,
     orderId: string,
     params: TalerMerchantApi.GetOrderRequestParams = {},
@@ -1207,7 +1209,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices
    */
-  async listOtpDevices(token: AccessToken | undefined,) {
+  async listOtpDevices(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/otp-devices`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1231,7 +1233,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-otp-devices-$DEVICE_ID
    */
-  async getOtpDevice(
+  async getOtpDeviceDetails(
     token: AccessToken | undefined,
     deviceId: string,
     params: TalerMerchantApi.GetOtpDeviceRequestParams = {},
@@ -1350,7 +1352,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * https://docs.taler.net/core/api-merchant.html#inspecting-template
    */
-  async listTemplates(token: AccessToken | undefined,) {
+  async listTemplates(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/templates`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1374,7 +1376,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCE]-private-templates-$TEMPLATE_ID
    */
-  async getTemplate(token: AccessToken | undefined, templateId: string) {
+  async getTemplateDetails(token: AccessToken | undefined, templateId: string) 
{
     const url = new URL(`private/templates/${templateId}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1520,7 +1522,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks
    */
-  async listWebhooks(token: AccessToken | undefined,) {
+  async listWebhooks(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/webhooks`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1545,7 +1547,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-webhooks-$WEBHOOK_ID
    */
-  async getWebhook(token: AccessToken | undefined, webhookId: string) {
+  async getWebhookDetails(token: AccessToken | undefined, webhookId: string) {
     const url = new URL(`private/webhooks/${webhookId}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1652,7 +1654,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies
    */
-  async listTokenFamilies(token: AccessToken | undefined,) {
+  async listTokenFamilies(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`private/tokenfamilies`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1677,7 +1679,7 @@ export class TalerMerchantInstanceHttpClient {
   /**
    * 
https://docs.taler.net/core/api-merchant.html#get-[-instances-$INSTANCES]-private-tokenfamilies-$TOKEN_FAMILY_SLUG
    */
-  async getTokenFamily(token: AccessToken | undefined, tokenSlug: string) {
+  async getTokenFamilyDetails(token: AccessToken | undefined, tokenSlug: 
string) {
     const url = new URL(`private/tokenfamilies/${tokenSlug}`, this.baseUrl);
 
     const headers: Record<string, string> = {}
@@ -1853,7 +1855,7 @@ export class TalerMerchantManagementHttpClient extends 
TalerMerchantInstanceHttp
   /**
    * https://docs.taler.net/core/api-merchant.html#get--management-instances
    */
-  async listInstances(token: AccessToken | undefined,) {
+  async listInstances(token: AccessToken | undefined, params?: 
PaginationParams) {
     const url = new URL(`management/instances`, this.baseUrl);
 
     const headers: Record<string, string> = {}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]