gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated (39ba26c76 -> 366cccb8f)


From: gnunet
Subject: [taler-wallet-core] branch master updated (39ba26c76 -> 366cccb8f)
Date: Thu, 19 Oct 2023 07:56:22 +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 39ba26c76 wallet-core: implement listExchangesForScopedCurrency
     new c6968c3c2 talerhttperror definition, for error in http request
     new f89e27a4e update api
     new c0dd59db4 return info on timeout
     new a67518ab1 update testing hook
     new 366cccb8f integrate bank into the new taler-util API

The 5 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:
 .../demobank-ui/src/components/Cashouts/index.ts   |   6 +-
 .../demobank-ui/src/components/Cashouts/state.ts   |   7 +-
 .../demobank-ui/src/components/Cashouts/views.tsx  |   4 +-
 .../demobank-ui/src/components/ErrorLoading.tsx    | 107 +++-
 packages/demobank-ui/src/components/Routing.tsx    |   7 +-
 .../src/components/Transactions/index.ts           |   4 +-
 .../src/components/Transactions/state.ts           |  18 +-
 .../src/components/Transactions/test.ts            |  94 ++--
 packages/demobank-ui/src/components/app.tsx        |  90 ++--
 packages/demobank-ui/src/context/config.ts         |  65 ++-
 packages/demobank-ui/src/declaration.d.ts          | 520 -------------------
 packages/demobank-ui/src/hooks/access.ts           | 401 ++++-----------
 packages/demobank-ui/src/hooks/backend.ts          | 265 +---------
 packages/demobank-ui/src/hooks/circuit.ts          | 548 +++++----------------
 packages/demobank-ui/src/hooks/config.ts           |  59 ---
 .../demobank-ui/src/hooks/useCredentialsChecker.ts | 135 -----
 .../demobank-ui/src/pages/AccountPage/index.ts     |  25 +-
 .../demobank-ui/src/pages/AccountPage/state.ts     |  51 +-
 .../demobank-ui/src/pages/AccountPage/views.tsx    |  34 +-
 packages/demobank-ui/src/pages/BankFrame.tsx       |  36 +-
 packages/demobank-ui/src/pages/HomePage.tsx        |  78 +--
 packages/demobank-ui/src/pages/LoginForm.tsx       | 104 ++--
 .../demobank-ui/src/pages/OperationState/index.ts  |  10 +-
 .../demobank-ui/src/pages/OperationState/state.ts  | 162 +++---
 .../src/pages/PaytoWireTransferForm.tsx            |  77 ++-
 .../demobank-ui/src/pages/PublicHistoriesPage.tsx  |  25 +-
 packages/demobank-ui/src/pages/QrCodeSection.tsx   |  50 +-
 .../demobank-ui/src/pages/RegistrationPage.tsx     | 110 +++--
 .../demobank-ui/src/pages/ShowAccountDetails.tsx   | 123 +++--
 .../src/pages/UpdateAccountPassword.tsx            |  60 ++-
 .../demobank-ui/src/pages/WalletWithdrawForm.tsx   |  60 ++-
 .../src/pages/WithdrawalConfirmationQuestion.tsx   |  77 +--
 .../demobank-ui/src/pages/WithdrawalQRCode.tsx     |  98 ++--
 packages/demobank-ui/src/pages/admin/Account.tsx   |  37 +-
 .../demobank-ui/src/pages/admin/AccountForm.tsx    |  72 +--
 .../demobank-ui/src/pages/admin/AccountList.tsx    |  34 +-
 .../src/pages/admin/CreateNewAccount.tsx           |  80 ++-
 packages/demobank-ui/src/pages/admin/Home.tsx      |  13 +-
 .../demobank-ui/src/pages/admin/RemoveAccount.tsx  | 101 ++--
 packages/demobank-ui/src/pages/business/Home.tsx   | 377 +++++++-------
 packages/demobank-ui/src/stories.test.ts           |   3 +-
 packages/demobank-ui/src/utils.ts                  |  70 +--
 .../merchant-backoffice-ui/src/hooks/testing.tsx   |  61 ++-
 packages/taler-util/src/errors.ts                  |  33 +-
 .../taler-util/src/http-client/authentication.ts   |  83 ++++
 packages/taler-util/src/http-client/bank-core.ts   | 284 +++++------
 .../taler-util/src/http-client/bank-integration.ts |   2 +-
 .../taler-util/src/http-client/bank-revenue.ts     |   5 +-
 packages/taler-util/src/http-client/bank-wire.ts   |   8 +-
 packages/taler-util/src/http-client/types.ts       |  56 ++-
 packages/taler-util/src/http-client/utils.ts       | 110 +----
 packages/taler-util/src/http-common.ts             |  31 --
 packages/web-util/src/context/api.ts               |   9 +-
 packages/web-util/src/hooks/useNotifications.ts    |   2 +-
 packages/web-util/src/utils/http-impl.browser.ts   |   6 +-
 packages/web-util/src/utils/http-impl.sw.ts        |   6 +-
 56 files changed, 1969 insertions(+), 3024 deletions(-)
 delete mode 100644 packages/demobank-ui/src/hooks/config.ts
 delete mode 100644 packages/demobank-ui/src/hooks/useCredentialsChecker.ts
 create mode 100644 packages/taler-util/src/http-client/authentication.ts

diff --git a/packages/demobank-ui/src/components/Cashouts/index.ts 
b/packages/demobank-ui/src/components/Cashouts/index.ts
index 05ef1f3b4..ae020cef6 100644
--- a/packages/demobank-ui/src/components/Cashouts/index.ts
+++ b/packages/demobank-ui/src/components/Cashouts/index.ts
@@ -18,7 +18,7 @@ import { HttpError, utils } from 
"@gnu-taler/web-util/browser";
 import { Loading } from "../Loading.js";
 // import { compose, StateViewMap } from "../../utils/index.js";
 // import { wxApi } from "../../wxApi.js";
-import { AbsoluteTime, AmountJson } from "@gnu-taler/taler-util";
+import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError } from 
"@gnu-taler/taler-util";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
@@ -37,7 +37,7 @@ export namespace State {
 
   export interface LoadingUriError {
     status: "loading-error";
-    error: HttpError<SandboxBackend.SandboxError>;
+    error: TalerError;
   }
 
   export interface BaseInfo {
@@ -46,7 +46,7 @@ export namespace State {
   export interface Ready extends BaseInfo {
     status: "ready";
     error: undefined;
-    cashouts: SandboxBackend.Circuit.CashoutStatusResponseWithId[];
+    cashouts: (TalerCorebankApi.CashoutStatusResponse & { id: string })[];
     onSelected: (id: string) => void;
   }
 }
diff --git a/packages/demobank-ui/src/components/Cashouts/state.ts 
b/packages/demobank-ui/src/components/Cashouts/state.ts
index 124f9bf9c..47ad0a297 100644
--- a/packages/demobank-ui/src/components/Cashouts/state.ts
+++ b/packages/demobank-ui/src/components/Cashouts/state.ts
@@ -14,18 +14,19 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { TalerError } from "@gnu-taler/taler-util";
 import { useCashouts } from "../../hooks/circuit.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState({ account, onSelected }: Props): State {
   const result = useCashouts(account);
-  if (result.loading) {
+  if (!result) {
     return {
       status: "loading",
       error: undefined,
     };
   }
-  if (!result.ok) {
+  if (result instanceof TalerError) {
     return {
       status: "loading-error",
       error: result,
@@ -35,7 +36,7 @@ export function useComponentState({ account, onSelected }: 
Props): State {
   return {
     status: "ready",
     error: undefined,
-    cashouts: result.data,
+    cashouts: result.body.cashouts,
     onSelected,
   };
 }
diff --git a/packages/demobank-ui/src/components/Cashouts/views.tsx 
b/packages/demobank-ui/src/components/Cashouts/views.tsx
index a32deb266..0602f507e 100644
--- a/packages/demobank-ui/src/components/Cashouts/views.tsx
+++ b/packages/demobank-ui/src/components/Cashouts/views.tsx
@@ -57,10 +57,10 @@ export function ReadyView({ cashouts, onSelected }: 
State.Ready): VNode {
           {cashouts.map((item, idx) => {
             return (
               <tr key={idx}>
-                <td>{format(item.creation_time, "dd/MM/yyyy HH:mm:ss")}</td>
+                <td>{item.creation_time.t_s === "never" ? i18n.str`never` : 
format(item.creation_time.t_s, "dd/MM/yyyy HH:mm:ss")}</td>
                 <td>
                   {item.confirmation_time
-                    ? format(item.confirmation_time, "dd/MM/yyyy HH:mm:ss")
+                    ? item.confirmation_time.t_s === "never" ? i18n.str`never` 
: format(item.confirmation_time.t_s, "dd/MM/yyyy HH:mm:ss")
                     : "-"}
                 </td>
                 <td><RenderAmount 
value={Amounts.parseOrThrow(item.amount_debit)} /></td>
diff --git a/packages/demobank-ui/src/components/ErrorLoading.tsx 
b/packages/demobank-ui/src/components/ErrorLoading.tsx
index ee62671ce..84e72c5a1 100644
--- a/packages/demobank-ui/src/components/ErrorLoading.tsx
+++ b/packages/demobank-ui/src/components/ErrorLoading.tsx
@@ -15,15 +15,106 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpError, useTranslationContext } from "@gnu-taler/web-util/browser";
-import { h, VNode } from "preact";
+import { TalerError, TalerErrorCode } from "@gnu-taler/taler-util";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
 import { Attention } from "./Attention.js";
-import { TranslatedString } from "@gnu-taler/taler-util";
+import { assertUnreachable } from "./Routing.js";
 
-export function ErrorLoading({ error }: { error: 
HttpError<SandboxBackend.SandboxError> }): VNode {
+export function ErrorLoading({ error, showDetail }: { error: TalerError, 
showDetail?: boolean }): VNode {
   const { i18n } = useTranslationContext()
-  return (<Attention type="danger" title={error.message as TranslatedString}>
-    <p class="text-sm font-medium text-red-800">Got status 
"{error.info.status}" on {error.info.url}</p>
-  </Attention>
-  );
+  switch (error.errorDetail.code) {
+    //////////////////
+    // Every error that can be produce in a Http Request
+    //////////////////
+    case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: {
+      if 
(error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT)) {
+        const { requestMethod, requestUrl, timeoutMs } = error.errorDetail
+        return <Attention type="danger" title={i18n.str`The request reached a 
timeout, check your connection.`}>
+          {error.message}
+          {showDetail &&
+            <pre class="whitespace-break-spaces ">
+              {JSON.stringify({ requestMethod, requestUrl, timeoutMs }, 
undefined, 2)}
+            </pre>
+          }
+        </Attention>
+      }
+      assertUnreachable(1 as never)
+    }
+    case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: {
+      if (error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) {
+        const { requestMethod, requestUrl, throttleStats } = error.errorDetail
+        return <Attention type="danger" title={i18n.str`A lot of request were 
made to the same server and this action was throttled`}>
+          {error.message}
+          {showDetail &&
+            <pre class="whitespace-break-spaces ">
+              {JSON.stringify({ requestMethod, requestUrl, throttleStats }, 
undefined, 2)}
+            </pre>
+          }
+        </Attention>
+      }
+      assertUnreachable(1 as never)
+    }
+    case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: {
+      if 
(error.hasErrorCode(TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE)) {
+        const { requestMethod, requestUrl, httpStatusCode, validationError } = 
error.errorDetail
+        return <Attention type="danger" title={i18n.str`The response of the 
request is malformed.`}>
+          {error.message}
+          {showDetail &&
+            <pre class="whitespace-break-spaces ">
+              {JSON.stringify({ requestMethod, requestUrl, httpStatusCode, 
validationError }, undefined, 2)}
+            </pre>
+          }
+        </Attention>
+      }
+      assertUnreachable(1 as never)
+    }
+    case TalerErrorCode.WALLET_NETWORK_ERROR: {
+      if (error.hasErrorCode(TalerErrorCode.WALLET_NETWORK_ERROR)) {
+        const { requestMethod, requestUrl } = error.errorDetail
+        return <Attention type="danger" title={i18n.str`Could not complete the 
request due to a network problem.`}>
+          {error.message}
+          {showDetail &&
+            <pre class="whitespace-break-spaces ">
+              {JSON.stringify({ requestMethod, requestUrl }, undefined, 2)}
+            </pre>
+          }
+        </Attention>
+      }
+      assertUnreachable(1 as never)
+    }
+    case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: {
+      if (error.hasErrorCode(TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR)) {
+        const { requestMethod, requestUrl, httpStatusCode, errorResponse } = 
error.errorDetail
+        return <Attention type="danger" title={i18n.str`Unexpected request 
error`}>
+          {error.message}
+          {showDetail &&
+            <pre class="whitespace-break-spaces ">
+              {JSON.stringify({ requestMethod, requestUrl, httpStatusCode, 
errorResponse }, undefined, 2)}
+            </pre>
+          }
+        </Attention>
+      }
+      assertUnreachable(1 as never)
+    }
+    //////////////////
+    // Every other error 
+    //////////////////
+    // case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: {
+    //   return <Attention type="danger" title={i18n.str``}>
+    //   </Attention>
+    // }
+    //////////////////
+    // Default message for unhandled case
+    //////////////////
+    default: return <Attention type="danger" title={i18n.str`Unexpected 
error`}>
+      {error.message}
+      {showDetail &&
+        <pre class="whitespace-break-spaces ">
+          {JSON.stringify(error.errorDetail, undefined, 2)}
+        </pre>
+      }
+    </Attention>
+  }
 }
+
diff --git a/packages/demobank-ui/src/components/Routing.tsx 
b/packages/demobank-ui/src/components/Routing.tsx
index aafc95687..04cf96190 100644
--- a/packages/demobank-ui/src/components/Routing.tsx
+++ b/packages/demobank-ui/src/components/Routing.tsx
@@ -32,8 +32,8 @@ import { bankUiSettings } from "../settings.js";
 export function Routing(): VNode {
   const history = createHashHistory();
   const backend = useBackendContext();
-  const {i18n} = useTranslationContext();
-  
+  const { i18n } = useTranslationContext();
+
   if (backend.state.status === "loggedOut") {
     return <BankFrame >
       <Router history={history}>
@@ -143,9 +143,6 @@ export function Routing(): VNode {
               onRegister={() => {
                 route("/register");
               }}
-              onLoadNotOk={() => {
-                route("/account");
-              }}
             />
           )}
         />
diff --git a/packages/demobank-ui/src/components/Transactions/index.ts 
b/packages/demobank-ui/src/components/Transactions/index.ts
index 9df1a70e5..3c4fb5ce9 100644
--- a/packages/demobank-ui/src/components/Transactions/index.ts
+++ b/packages/demobank-ui/src/components/Transactions/index.ts
@@ -18,7 +18,7 @@ import { HttpError, utils } from 
"@gnu-taler/web-util/browser";
 import { Loading } from "../Loading.js";
 // import { compose, StateViewMap } from "../../utils/index.js";
 // import { wxApi } from "../../wxApi.js";
-import { AbsoluteTime, AmountJson } from "@gnu-taler/taler-util";
+import { AbsoluteTime, AmountJson, TalerError } from "@gnu-taler/taler-util";
 import { useComponentState } from "./state.js";
 import { LoadingUriView, ReadyView } from "./views.js";
 
@@ -36,7 +36,7 @@ export namespace State {
 
   export interface LoadingUriError {
     status: "loading-error";
-    error: HttpError<SandboxBackend.SandboxError>;
+    error: TalerError;
   }
 
   export interface BaseInfo {
diff --git a/packages/demobank-ui/src/components/Transactions/state.ts 
b/packages/demobank-ui/src/components/Transactions/state.ts
index 4b62b005e..c85fba85b 100644
--- a/packages/demobank-ui/src/components/Transactions/state.ts
+++ b/packages/demobank-ui/src/components/Transactions/state.ts
@@ -14,34 +14,34 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, Amounts, parsePaytoUri } from "@gnu-taler/taler-util";
+import { AbsoluteTime, Amounts, TalerError, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { useTransactions } from "../../hooks/access.js";
 import { Props, State, Transaction } from "./index.js";
 
 export function useComponentState({ account }: Props): State {
   const result = useTransactions(account);
-  if (result.loading) {
+  if (!result) {
     return {
       status: "loading",
       error: undefined,
     };
   }
-  if (!result.ok) {
+  if (result instanceof TalerError) {
     return {
       status: "loading-error",
       error: result,
     };
   }
 
-  const transactions = result.data.transactions
+  const transactions = result.data.type === "fail" ? [] : 
result.data.body.transactions
     .map((tx) => {
 
       const negative = tx.direction === "debit";
       const cp = parsePaytoUri(negative ? tx.creditor_payto_uri : 
tx.debtor_payto_uri);
       const counterpart = (cp === undefined || !cp.isKnown ? undefined :
-        cp.targetType === "iban" ? cp.iban : 
-        cp.targetType === "x-taler-bank" ? cp.account :
-        cp.targetType === "bitcoin" ? `${cp.targetPath.substring(0, 6)}...` : 
undefined) ??
+        cp.targetType === "iban" ? cp.iban :
+          cp.targetType === "x-taler-bank" ? cp.account :
+            cp.targetType === "bitcoin" ? `${cp.targetPath.substring(0, 
6)}...` : undefined) ??
         "unkown";
 
       const when = AbsoluteTime.fromProtocolTimestamp(tx.date);
@@ -61,7 +61,7 @@ export function useComponentState({ account }: Props): State {
     status: "ready",
     error: undefined,
     transactions,
-    onNext: result.isReachingEnd ? undefined : result.loadMore,
-    onPrev: result.isReachingStart ? undefined : result.loadMorePrev,
+    onNext: result.isLastPage ? undefined : result.loadMore,
+    onPrev: result.isFirstPage ? undefined : result.loadMorePrev,
   };
 }
diff --git a/packages/demobank-ui/src/components/Transactions/test.ts 
b/packages/demobank-ui/src/components/Transactions/test.ts
index 9b713bbc5..a206d9f52 100644
--- a/packages/demobank-ui/src/components/Transactions/test.ts
+++ b/packages/demobank-ui/src/components/Transactions/test.ts
@@ -26,7 +26,7 @@ import { expect } from "chai";
 import { TRANSACTION_API_EXAMPLE } from "../../endpoints.js";
 import { Props } from "./index.js";
 import { useComponentState } from "./state.js";
-import { HttpStatusCode } from "@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerErrorCode } from 
"@gnu-taler/taler-util";
 
 describe("Transaction states", () => {
   it("should query backend and render transactions", async () => {
@@ -116,47 +116,47 @@ describe("Transaction states", () => {
     expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
   });
 
-  it("should show error message on not found", async () => {
-    const env = new SwrMockEnvironment();
-
-    const props: Props = {
-      account: "myAccount",
-    };
-
-    env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_NOT_FOUND, {
-      response: {
-        error: {
-          description: "Transaction page 0 could not be retrieved.",
-        },
-      },
-    });
-
-    const hookBehavior = await tests.hookBehaveLikeThis(
-      useComponentState,
-      props,
-      [
-        ({ status, error }) => {
-          expect(status).equals("loading");
-          expect(error).undefined;
-        },
-        ({ status, error }) => {
-          expect(status).equals("loading-error");
-          if (error === undefined || error.type !== ErrorType.CLIENT) {
-            throw Error("not the expected error");
-          }
-          expect(error.payload).deep.equal({
-            error: {
-              description: "Transaction page 0 could not be retrieved.",
-            },
-          });
-        },
-      ],
-      env.buildTestingContext(),
-    );
-
-    expect(hookBehavior).deep.eq({ result: "ok" });
-    expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" });
-  });
+  // it("should show error message on not found", async () => {
+  //   const env = new SwrMockEnvironment();
+
+  //   const props: Props = {
+  //     account: "myAccount",
+  //   };
+
+  //   env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_NOT_FOUND, {
+  //     response: {
+  //       error: {
+  //         description: "Transaction page 0 could not be retrieved.",
+  //       },
+  //     },
+  //   });
+
+  //   const hookBehavior = await tests.hookBehaveLikeThis(
+  //     useComponentState,
+  //     props,
+  //     [
+  //       ({ status, error }) => {
+  //         expect(status).equals("loading");
+  //         expect(error).undefined;
+  //       },
+  //       ({ status, error }) => {
+  //         expect(status).equals("loading-error");
+  //         if (error === undefined || error.type !== ErrorType.CLIENT) {
+  //           throw Error("not the expected error");
+  //         }
+  //         expect(error.payload).deep.equal({
+  //           error: {
+  //             description: "Transaction page 0 could not be retrieved.",
+  //           },
+  //         });
+  //       },
+  //     ],
+  //     env.buildTestingContext(),
+  //   );
+
+  //   expect(hookBehavior).deep.eq({ result: "ok" });
+  //   expect(env.assertJustExpectedRequestWereMade()).deep.eq({ result: "ok" 
});
+  // });
 
   it("should show error message on server error", async () => {
     const env = new SwrMockEnvironment();
@@ -168,7 +168,7 @@ describe("Transaction states", () => {
     env.addRequestExpectation(TRANSACTION_API_EXAMPLE.LIST_ERROR, {
       response: {
         error: {
-          description: "Transaction page 0 could not be retrieved.",
+          code: TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED,
         },
       },
     });
@@ -183,14 +183,10 @@ describe("Transaction states", () => {
         },
         ({ status, error }) => {
           expect(status).equals("loading-error");
-          if (error === undefined || error.type !== ErrorType.SERVER) {
+          if (error === undefined || 
!error.hasErrorCode(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED)) {
             throw Error("not the expected error");
           }
-          expect(error.payload).deep.equal({
-            error: {
-              description: "Transaction page 0 could not be retrieved.",
-            },
-          });
+          
expect(error.errorDetail.code).deep.equal(TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED);
         },
       ],
       env.buildTestingContext(),
diff --git a/packages/demobank-ui/src/components/app.tsx 
b/packages/demobank-ui/src/components/app.tsx
index 7cf658681..beb24da57 100644
--- a/packages/demobank-ui/src/components/app.tsx
+++ b/packages/demobank-ui/src/components/app.tsx
@@ -15,47 +15,26 @@
  */
 
 import {
-  LibtoolVersion,
+  canonicalizeBaseUrl,
   getGlobalLogLevel,
-  setGlobalLogLevelFromString,
+  setGlobalLogLevelFromString
 } from "@gnu-taler/taler-util";
-import { TranslationProvider, useApiContext } from 
"@gnu-taler/web-util/browser";
-import { ComponentChildren, Fragment, FunctionalComponent, VNode, h } from 
"preact";
+import { TranslationProvider } from "@gnu-taler/web-util/browser";
+import { Fragment, FunctionalComponent, h } from "preact";
 import { SWRConfig } from "swr";
-import { BackendStateProvider, useBackendContext } from 
"../context/backend.js";
+import { BackendStateProvider } from "../context/backend.js";
+import { BankCoreApiProvider } from "../context/config.js";
 import { strings } from "../i18n/strings.js";
+import { bankUiSettings } from "../settings.js";
 import { Routing } from "./Routing.js";
-import { useEffect, useState } from "preact/hooks";
-import { Loading } from "./Loading.js";
-import { getInitialBackendBaseURL } from "../hooks/backend.js";
-import { BANK_INTEGRATION_PROTOCOL_VERSION, useConfigState } from 
"../hooks/config.js";
-import { ErrorLoading } from "./ErrorLoading.js";
-import { BankFrame } from "../pages/BankFrame.js";
-import { ConfigStateProvider } from "../context/config.js";
 const WITH_LOCAL_STORAGE_CACHE = false;
 
-/**
- * FIXME:
- *
- * - INPUT elements have their 'required' attribute ignored.
- *
- * - the page needs a "home" button that either redirects to
- *   the profile page (when the user is logged in), or to
- *   the very initial home page.
- *
- * - histories 'pages' are grouped in UL elements that cause
- *   the rendering to visually separate each UL.  History elements
- *   should instead line up without any separation caused by
- *   a implementation detail.
- *
- * - Many strings need to be i18n-wrapped.
- */
-
 const App: FunctionalComponent = () => {
+  const baseUrl = getInitialBackendBaseURL();
   return (
     <TranslationProvider source={strings}>
       <BackendStateProvider>
-        <VersionCheck>
+        <BankCoreApiProvider baseUrl={baseUrl}>
           <SWRConfig
             value={{
               provider: WITH_LOCAL_STORAGE_CACHE
@@ -65,34 +44,15 @@ const App: FunctionalComponent = () => {
           >
             <Routing />
           </SWRConfig>
-        </VersionCheck>
+        </BankCoreApiProvider>
       </BackendStateProvider>
     </TranslationProvider >
   );
 };
+
 (window as any).setGlobalLogLevelFromString = setGlobalLogLevelFromString;
 (window as any).getGlobalLevel = getGlobalLogLevel;
 
-function VersionCheck({ children }: { children: ComponentChildren }): VNode {
-  const checked = useConfigState()
-
-  if (checked === undefined) {
-    return <Loading />
-  }
-  if (checked.type === "wrong") {
-    return <BankFrame>
-      the bank backend is not supported. supported version 
"{BANK_INTEGRATION_PROTOCOL_VERSION}", server version "{checked}"
-    </BankFrame>
-  }
-  if (checked.type === "ok") {
-    return <ConfigStateProvider 
value={checked.result}>{children}</ConfigStateProvider>
-  }
-
-  return <BankFrame>
-    <ErrorLoading error={checked.result} />
-  </BankFrame>
-}
-
 function localStorageProvider(): Map<unknown, unknown> {
   const map = new Map(JSON.parse(localStorage.getItem("app-cache") || "[]"));
 
@@ -104,3 +64,31 @@ function localStorageProvider(): Map<unknown, unknown> {
 }
 
 export default App;
+
+function getInitialBackendBaseURL(): string {
+  const overrideUrl =
+    typeof localStorage !== "undefined"
+      ? localStorage.getItem("bank-base-url")
+      : undefined;
+  let result: string;
+  if (!overrideUrl) {
+    //normal path
+    if (!bankUiSettings.backendBaseURL) {
+      console.error(
+        "ERROR: backendBaseURL was overridden by a setting file and missing. 
Setting value to 'window.origin'",
+      );
+      result = window.origin
+    } else {
+      result = bankUiSettings.backendBaseURL;
+    }
+  } else {
+    // testing/development path
+    result = overrideUrl
+  }
+  try {
+    return canonicalizeBaseUrl(result)
+  } catch (e) {
+    //fall back
+    return canonicalizeBaseUrl(window.origin)
+  }
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/context/config.ts 
b/packages/demobank-ui/src/context/config.ts
index a2cde18eb..013d8922e 100644
--- a/packages/demobank-ui/src/context/config.ts
+++ b/packages/demobank-ui/src/context/config.ts
@@ -14,36 +14,71 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
+import { TalerCorebankApi, TalerCoreBankHttpClient, TalerError } from 
"@gnu-taler/taler-util";
+import { BrowserHttpLib, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { ComponentChildren, createContext, h, VNode } from "preact";
-import { useContext } from "preact/hooks";
+import { useContext, useEffect, useState } from "preact/hooks";
+import { ErrorLoading } from "../components/ErrorLoading.js";
 
 /**
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
-export type Type = Required<SandboxBackend.Config>;
-
-const initial: Type = {
-  name: "",
-  version: "0:0:0",
-  currency_fraction_digits: 2,
-  currency_fraction_limit: 2,
-  fiat_currency: "",
-  have_cashout: false,
+export type Type = {
+  url: URL,
+  config: TalerCorebankApi.Config,
+  api: TalerCoreBankHttpClient,
 };
-const Context = createContext<Type>(initial);
 
-export const useConfigContext = (): Type => useContext(Context);
+const Context = createContext<Type>(undefined as any);
+
+export const useBankCoreApiContext = (): Type => useContext(Context);
+
+export type ConfigResult = undefined
+  | { type: "ok", config: TalerCorebankApi.Config }
+  | { type: "incompatible", result: TalerCorebankApi.Config, supported: string 
}
+  | { type: "error", error: TalerError }
 
-export const ConfigStateProvider = ({
-  value,
+export const BankCoreApiProvider = ({
+  baseUrl,
   children,
 }: {
-  value: Type,
+  baseUrl: string,
   children: ComponentChildren;
 }): VNode => {
+  const [checked, setChecked] = useState<ConfigResult>()
+  const { i18n } = useTranslationContext();
+  const url = new URL(baseUrl)
+  const api = new TalerCoreBankHttpClient(url.href, new BrowserHttpLib())
+  useEffect(() => {
+    api.getConfig()
+      .then((resp) => {
+        if (api.isCompatible(resp.body.version)) {
+          setChecked({ type: "ok", config: resp.body });
+        } else {
+          setChecked({ type: "incompatible", result: resp.body, supported: 
api.PROTOCOL_VERSION })
+        }
+      })
+      .catch((error: unknown) => {
+        if (error instanceof TalerError) {
+          setChecked({ type: "error", error });
+        }
+      });
+  }, []);
 
+  if (checked === undefined) {
+    return h("div", {}, "loading...")
+  }
+  if (checked.type === "error") {
+    return h(ErrorLoading, { error: checked.error, showDetail: true })
+  }
+  if (checked.type === "incompatible") {
+    return h("div", {}, i18n.str`the bank backend is not supported. supported 
version "${checked.supported}", server version "${checked.result.version}"`)
+  }
+  const value: Type = {
+    url, config: checked.config, api
+  }
   return h(Context.Provider, {
     value,
     children,
diff --git a/packages/demobank-ui/src/declaration.d.ts 
b/packages/demobank-ui/src/declaration.d.ts
index 5c55cfade..c8ba3d576 100644
--- a/packages/demobank-ui/src/declaration.d.ts
+++ b/packages/demobank-ui/src/declaration.d.ts
@@ -31,525 +31,5 @@ declare module "*.png" {
   export default content;
 }
 
-/**********************************************
- * Type definitions for states and API calls. *
- *********************************************/
-
-/**
- * Request body of POST /transactions.
- *
- * If the amount appears twice: both as a Payto parameter and
- * in the JSON dedicate field, the one on the Payto URI takes
- * precedence.
- */
-interface TransactionRequestType {
-  paytoUri: string;
-  amount?: string; // with currency.
-}
-
-/**
- * Request body of /register.
- */
-interface CredentialsRequestType {
-  username?: string;
-  password?: string;
-  repeatPassword?: string;
-}
-
-/**
- * Request body of /register.
- */
-// interface LoginRequestType {
-//   username: string;
-//   password: string;
-// }
-
-interface WireTransferRequestType {
-  iban?: string;
-  subject?: string;
-  amount?: string;
-}
-
-type HashCode = string;
-type EddsaPublicKey = string;
-type EddsaSignature = string;
-type WireTransferIdentifierRawP = string;
-type RelativeTime = {
-  d_us: number | "forever"
-};
-type ImageDataUrl = string;
-
-interface WithId {
-  id: string;
-}
-
-interface Timestamp {
-  // Milliseconds since epoch, or the special
-  // value "forever" to represent an event that will
-  // never happen.
-  t_s: number | "never";
-}
-interface Duration {
-  d_us: number | "forever";
-}
-
-interface WithId {
-  id: string;
-}
-
-type Amount = string;
-type UUID = string;
-type Integer = number;
-
-namespace SandboxBackend {
-  export interface Config {
-    // Name of this API, always "circuit".
-    name: string;
-    // API version in the form $n:$n:$n
-    version: string;
-    // If 'true', the server provides local currency
-    // conversion support.
-    // If missing or false, some parts of the API
-    // are not supported and return 404.
-    have_cashout?: boolean;
-
-    // Fiat currency.  That is the currency in which
-    // cash-out operations ultimately wire money.
-    // Only applicable if have_cashout=true.
-    fiat_currency?: string;
-
-    // How many digits should the amounts be rendered
-    // with by default. Small capitals should
-    // be used to render fractions beyond the number
-    // given here (like on gas stations).
-    currency_fraction_digits?: number;
-
-    // How many decimal digits an operation can
-    // have. Wire transfers with more decimal 
-    // digits will not be accepted.
-    currency_fraction_limit?: number;
-  }
-  interface RatiosAndFees {
-    // Exchange rate to buy the circuit currency from fiat.
-    buy_at_ratio: number;
-    // Exchange rate to sell the circuit currency for fiat.
-    sell_at_ratio: number;
-    // Fee to subtract after applying the buy ratio.
-    buy_in_fee: number;
-    // Fee to subtract after applying the sell ratio.
-    sell_out_fee: number;
-  }
-
-  export interface SandboxError {
-    error?: SandboxErrorDetail;
-  }
-  interface SandboxErrorDetail {
-    // String enum classifying the error.
-    type: ErrorType;
-
-    // Human-readable error description.
-    description: string;
-  }
-  enum ErrorType {
-    /**
-     * This error can be related to a business operation,
-     * a non-existent object requested by the client, or
-     * even when the bank itself fails.
-     */
-    SandboxError = "sandbox-error",
-
-    /**
-     * It is the error type thrown by helper functions
-     * from the Util library.  Those are used by both
-     * Sandbox and Nexus, therefore the actual meaning
-     * must be carried by the error 'message' field.
-     */
-    UtilError = "util-error",
-  }
-
-
-  type EmailAddress = string;
-  type PhoneNumber = string;
-
-  namespace CoreBank {
-
-    interface BankAccountCreateWithdrawalRequest {
-      // Amount to withdraw.
-      amount: Amount;
-    }
-    interface BankAccountCreateWithdrawalResponse {
-      // ID of the withdrawal, can be used to view/modify the withdrawal 
operation.
-      withdrawal_id: string;
-
-      // URI that can be passed to the wallet to initiate the withdrawal.
-      taler_withdraw_uri: string;
-    }
-    interface BankAccountGetWithdrawalResponse {
-      // Amount that will be withdrawn with this withdrawal operation.
-      amount: Amount;
-
-      // Was the withdrawal aborted?
-      aborted: boolean;
-
-      // Has the withdrawal been confirmed by the bank?
-      // The wire transfer for a withdrawal is only executed once
-      // both confirmation_done is true and selection_done is true.
-      confirmation_done: boolean;
-
-      // Did the wallet select reserve details?
-      selection_done: boolean;
-
-      // Reserve public key selected by the exchange,
-      // only non-null if selection_done is true.
-      selected_reserve_pub: string | null;
-
-      // Exchange account selected by the wallet, or by the bank
-      // (with the default exchange) in case the wallet did not provide one
-      // through the Integration API.
-      selected_exchange_account: string | null;
-    }
-
-    interface BankAccountTransactionsResponse {
-      transactions: BankAccountTransactionInfo[];
-    }
-
-    interface BankAccountTransactionInfo {
-      creditor_payto_uri: string;
-      debtor_payto_uri: string;
-
-      amount: Amount;
-      direction: "debit" | "credit";
-
-      subject: string;
-
-      // Transaction unique ID.  Matches
-      // $transaction_id from the URI.
-      row_id: number;
-      date: Timestamp;
-    }
-
-    interface CreateBankAccountTransactionCreate {
-      // Address in the Payto format of the wire transfer receiver.
-      // It needs at least the 'message' query string parameter.
-      payto_uri: string;
-
-      // Transaction amount (in the $currency:x.y format), optional.
-      // However, when not given, its value must occupy the 'amount'
-      // query string parameter of the 'payto' field.  In case it
-      // is given in both places, the paytoUri's takes the precedence.
-      amount?: string;
-    }
-
-    interface RegisterAccountRequest {
-      // Username
-      username: string;
-
-      // Password.
-      password: string;
-
-      // Legal name of the account owner
-      name: string;
-
-      // Defaults to false.
-      is_public?: boolean;
-
-      // Is this a taler exchange account?
-      // If true:
-      // - incoming transactions to the account that do not
-      //   have a valid reserve public key are automatically
-      // - the account provides the taler-wire-gateway-api endpoints
-      // Defaults to false.
-      is_taler_exchange?: boolean;
-
-      // Addresses where to send the TAN for transactions.
-      // Currently only used for cashouts.
-      // If missing, cashouts will fail.
-      // In the future, might be used for other transactions
-      // as well.
-      challenge_contact_data?: ChallengeContactData;
-
-      // 'payto' address pointing a bank account
-      // external to the libeufin-bank.
-      // Payments will be sent to this bank account
-      // when the user wants to convert the local currency
-      // back to fiat currency outside libeufin-bank.
-      cashout_payto_uri?: string;
-
-      // Internal payto URI of this bank account.
-      // Used mostly for testing.
-      internal_payto_uri?: string;
-    }
-    interface ChallengeContactData {
-
-      // E-Mail address
-      email?: EmailAddress;
-
-      // Phone number.
-      phone?: PhoneNumber;
-    }
-
-    interface AccountReconfiguration {
-
-      // Addresses where to send the TAN for transactions.
-      // Currently only used for cashouts.
-      // If missing, cashouts will fail.
-      // In the future, might be used for other transactions
-      // as well.
-      challenge_contact_data?: ChallengeContactData;
-
-      // 'payto' address pointing a bank account
-      // external to the libeufin-bank.
-      // Payments will be sent to this bank account
-      // when the user wants to convert the local currency
-      // back to fiat currency outside libeufin-bank.
-      cashout_address?: string;
-
-      // Legal name associated with $username.
-      // When missing, the old name is kept.
-      name?: string;
-
-      // If present, change the is_exchange configuration.
-      // See RegisterAccountRequest
-      is_exchange?: boolean;
-    }
-
-
-    interface AccountPasswordChange {
-
-      // New password.
-      new_password: string;
-    }
-    interface PublicAccountsResponse {
-      public_accounts: PublicAccount[];
-    }
-    interface PublicAccount {
-      payto_uri: string;
-
-      balance: Balance;
-
-      // The account name (=username) of the
-      // libeufin-bank account.
-      account_name: string;
-    }
-
-    interface ListBankAccountsResponse {
-      accounts: AccountMinimalData[];
-    }
-    interface Balance {
-      amount: Amount;
-      credit_debit_indicator: "credit" | "debit";
-    }
-    interface AccountMinimalData {
-      // Username
-      username: string;
-
-      // Legal name of the account owner.
-      name: string;
-
-      // current balance of the account
-      balance: Balance;
-
-      // Number indicating the max debit allowed for the requesting user.
-      debit_threshold: Amount;
-    }
-
-    interface AccountData {
-      // Legal name of the account owner.
-      name: string;
-
-      // Available balance on the account.
-      balance: Balance;
-
-      // payto://-URI of the account.
-      payto_uri: string;
-
-      // Number indicating the max debit allowed for the requesting user.
-      debit_threshold: Amount;
-
-      contact_data?: ChallengeContactData;
-
-      // 'payto' address pointing the bank account
-      // where to send cashouts.  This field is optional
-      // because not all the accounts are required to participate
-      // in the merchants' circuit.  One example is the exchange:
-      // that never cashouts.  Registering these accounts can
-      // be done via the access API.
-      cashout_payto_uri?: string;
-    }
-
-  }
-
-  namespace Circuit {
-    interface CircuitAccountRequest {
-      // Username
-      username: string;
-
-      // Password.
-      password: string;
-
-      // Addresses where to send the TAN.  If
-      // this field is missing, then the cashout
-      // won't succeed.
-      contact_data: CircuitContactData;
-
-      // Legal subject owning the account.
-      name: string;
-
-      // 'payto' address pointing the bank account
-      // where to send payments, in case the user
-      // wants to convert the local currency back
-      // to fiat.
-      cashout_address: string;
-
-      // IBAN of this bank account, which is therefore
-      // internal to the circuit.  Randomly generated,
-      // when it is not given.
-      internal_iban?: string;
-    }
-    interface CircuitContactData {
-      // E-Mail address
-      email?: string;
-
-      // Phone number.
-      phone?: string;
-    }
-    interface CircuitAccountReconfiguration {
-      // Addresses where to send the TAN.
-      contact_data: CircuitContactData;
-
-      // 'payto' address pointing the bank account
-      // where to send payments, in case the user
-      // wants to convert the local currency back
-      // to fiat.
-      cashout_address: string;
-    }
-    interface AccountPasswordChange {
-      // New password.
-      new_password: string;
-    }
-
-    interface CircuitAccounts {
-      customers: CircuitAccountMinimalData[];
-    }
-    interface CircuitAccountMinimalData {
-      // Username
-      username: string;
-
-      // Legal subject owning the account.
-      name: string;
-
-      // current balance of the account
-      balance: Balance;
-    }
-
-    interface CircuitAccountData {
-      // Username
-      username: string;
-
-      // IBAN hosted at Libeufin Sandbox
-      iban: string;
-
-      contact_data: CircuitContactData;
-
-      // Legal subject owning the account.
-      name: string;
-
-      // 'payto' address pointing the bank account
-      // where to send cashouts.
-      cashout_address: string;
-    }
-    interface CashoutEstimate {
-      // Amount that the user will get deducted from their regional
-      // bank account, according to the 'amount_credit' value.
-      amount_debit: Amount;
-      // Amount that the user will receive in their fiat
-      // bank account, according to 'amount_debit'.
-      amount_credit: Amount;
-    }
-    interface CashoutRequest {
-      // Optional subject to associate to the
-      // cashout operation.  This data will appear
-      // as the incoming wire transfer subject in
-      // the user's external bank account.
-      subject?: string;
-
-      // That is the plain amount that the user specified
-      // to cashout.  Its $currency is the circuit currency.
-      amount_debit: Amount;
-
-      // That is the amount that will effectively be
-      // transferred by the bank to the user's bank
-      // account, that is external to the circuit.
-      // It is expressed in the fiat currency and
-      // is calculated after the cashout fee and the
-      // exchange rate.  See the /cashout-rates call.
-      amount_credit: Amount;
-
-      // Which channel the TAN should be sent to.  If
-      // this field is missing, it defaults to SMS.
-      // The default choice prefers to change the communication
-      // channel respect to the one used to issue this request.
-      tan_channel?: TanChannel;
-    }
-    interface CashoutPending {
-      // UUID identifying the operation being created
-      // and now waiting for the TAN confirmation.
-      uuid: string;
-    }
-    interface CashoutConfirm {
-      // the TAN that confirms $cashoutId.
-      tan: string;
-    }
-    interface Config {
-      // Name of this API, always "circuit".
-      name: string;
-      // API version in the form $n:$n:$n
-      version: string;
-      // Contains ratios and fees related to buying
-      // and selling the circuit currency.
-      ratios_and_fees: RatiosAndFees;
-      // Fiat currency.  That is the currency in which
-      // cash-out operations ultimately wire money.
-      fiat_currency: string;
-    }
-    interface RatiosAndFees {
-      // Exchange rate to buy the circuit currency from fiat.
-      buy_at_ratio: float;
-      // Exchange rate to sell the circuit currency for fiat.
-      sell_at_ratio: float;
-      // Fee to subtract after applying the buy ratio.
-      buy_in_fee: float;
-      // Fee to subtract after applying the sell ratio.
-      sell_out_fee: float;
-    }
-    interface Cashouts {
-      // Every string represents a cash-out operation UUID.
-      cashouts: string[];
-    }
-    interface CashoutStatusResponse {
-      status: CashoutStatus;
-      // Amount debited to the circuit bank account.
-      amount_debit: Amount;
-      // Amount credited to the external bank account.
-      amount_credit: Amount;
-      // Transaction subject.
-      subject: string;
-      // Circuit bank account that created the cash-out.
-      account: string;
-      // Fiat bank account that will receive the cashed out amount.
-      cashout_address: string;
-      // Ratios and fees related to this cash-out at the time
-      // when the operation was created.
-      ratios_and_fees: RatiosAndFees;
-      // Time when the cash-out was created.
-      creation_time: number; // milliseconds since the Unix epoch
-      // Time when the cash-out was confirmed via its TAN.
-      // Missing or null, when the operation wasn't confirmed yet.
-      confirmation_time?: number | null; // milliseconds since the Unix epoch
-    }
-    type CashoutStatusResponseWithId = CashoutStatusResponse & { id: string };
-  }
-}
-
 declare const __VERSION__: string;
 declare const __GIT_HASH__: string;
diff --git a/packages/demobank-ui/src/hooks/access.ts 
b/packages/demobank-ui/src/hooks/access.ts
index 154c43ae6..2533d32fe 100644
--- a/packages/demobank-ui/src/hooks/access.ts
+++ b/packages/demobank-ui/src/hooks/access.ts
@@ -14,168 +14,31 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
-} from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { useBackendContext } from "../context/backend.js";
+import { AccessToken, TalerCoreBankResultByMethod, TalerHttpError } from 
"@gnu-taler/taler-util";
+import { useState } from "preact/hooks";
 import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
-import {
-  useAuthenticatedBackend,
-  useMatchMutate,
-  usePublicBackend,
-} from "./backend.js";
+import { useBackendState } from "./backend.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
 import _useSWR, { SWRHook } from "swr";
-import { Amounts } from "@gnu-taler/taler-util";
+import { useBankCoreApiContext } from "../context/config.js";
 const useSWR = _useSWR as unknown as SWRHook;
 
-export function useAccessAPI(): AccessAPI {
-  const mutateAll = useMatchMutate();
-  const { request } = useAuthenticatedBackend();
-  const { state } = useBackendContext();
-  if (state.status === "loggedOut") {
-    throw Error("access-api can't be used when the user is not logged In");
-  }
-  const account = state.username;
-
-  const createWithdrawal = async (
-    data: SandboxBackend.CoreBank.BankAccountCreateWithdrawalRequest,
-  ): Promise<
-    HttpResponseOk<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>
-  > => {
-    const res =
-      await 
request<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>(
-        `accounts/${account}/withdrawals`,
-        {
-          method: "POST",
-          data,
-          contentType: "json",
-        },
-      );
-    return res;
-  };
-  const createTransaction = async (
-    data: SandboxBackend.CoreBank.CreateBankAccountTransactionCreate,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(
-      `accounts/${account}/transactions`,
-      {
-        method: "POST",
-        data,
-        contentType: "json",
-      },
-    );
-    await mutateAll(/.*accounts\/.*/);
-    return res;
-  };
-  const deleteAccount = async (): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`accounts/${account}`, {
-      method: "DELETE",
-      contentType: "json",
-    });
-    await mutateAll(/.*accounts\/.*/);
-    return res;
-  };
-
-  return {
-    createWithdrawal,
-    createTransaction,
-    deleteAccount,
-  };
-}
-
-export function useAccessAnonAPI(): AccessAnonAPI {
-  const mutateAll = useMatchMutate();
-  const { request } = useAuthenticatedBackend();
-
-  const abortWithdrawal = async (id: string): Promise<HttpResponseOk<void>> => 
{
-    const res = await request<void>(`withdrawals/${id}/abort`, {
-      method: "POST",
-      contentType: "json",
-    });
-    await mutateAll(/.*withdrawals\/.*/);
-    return res;
-  };
-  const confirmWithdrawal = async (
-    id: string,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`withdrawals/${id}/confirm`, {
-      method: "POST",
-      contentType: "json",
-    });
-    await mutateAll(/.*withdrawals\/.*/);
-    return res;
-  };
-
-  return {
-    abortWithdrawal,
-    confirmWithdrawal,
-  };
-}
-
-export function useTestingAPI(): TestingAPI {
-  const mutateAll = useMatchMutate();
-  const { request: noAuthRequest } = usePublicBackend();
-  const register = async (
-    data: SandboxBackend.CoreBank.RegisterAccountRequest,
-  ): Promise<HttpResponseOk<void>> => {
-    // FIXME: This API is deprecated.  The normal account registration API 
should be used instead.
-    const res = await noAuthRequest<void>(`accounts`, {
-      method: "POST",
-      data,
-      contentType: "json",
-    });
-    await mutateAll(/.*accounts\/.*/);
-    return res;
-  };
-
-  return { register };
-}
-
-export interface TestingAPI {
-  register: (
-    data: SandboxBackend.CoreBank.RegisterAccountRequest,
-  ) => Promise<HttpResponseOk<void>>;
-}
-
-export interface AccessAPI {
-  createWithdrawal: (
-    data: SandboxBackend.CoreBank.BankAccountCreateWithdrawalRequest,
-  ) => Promise<
-    HttpResponseOk<SandboxBackend.CoreBank.BankAccountCreateWithdrawalResponse>
-  >;
-  createTransaction: (
-    data: SandboxBackend.CoreBank.CreateBankAccountTransactionCreate,
-  ) => Promise<HttpResponseOk<void>>;
-  deleteAccount: () => Promise<HttpResponseOk<void>>;
-}
-export interface AccessAnonAPI {
-  abortWithdrawal: (wid: string) => Promise<HttpResponseOk<void>>;
-  confirmWithdrawal: (wid: string) => Promise<HttpResponseOk<void>>;
-}
 
 export interface InstanceTemplateFilter {
   //FIXME: add filter to the template list
   position?: string;
 }
 
-export function useAccountDetails(
-  account: string,
-): HttpResponse<
-  SandboxBackend.CoreBank.AccountData,
-  SandboxBackend.SandboxError
-> {
-  const { fetcher } = useAuthenticatedBackend();
-
-  const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.CoreBank.AccountData>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`accounts/${account}`], fetcher, {
+export function useAccountDetails(account: string) {
+  const { state: credentials } = useBackendState();
+  const { api } = useBankCoreApiContext();
+
+  async function fetcher([username, token]: [string, AccessToken]) {
+    return await api.getAccount({ username, token })
+  }
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+  const { data, error } = useSWR<TalerCoreBankResultByMethod<"getAccount">, 
TalerHttpError>([account, token], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -187,26 +50,22 @@ export function useAccountDetails(
     keepPreviousData: true,
   });
 
-  if (data) {
-    return data;
-  }
-  if (error) return error.cause;
-  return { loading: true };
+  if (data) return data
+  if (error) return error;
+  return undefined;
 }
 
 // FIXME: should poll
-export function useWithdrawalDetails(
-  wid: string,
-): HttpResponse<
-  SandboxBackend.CoreBank.BankAccountGetWithdrawalResponse,
-  SandboxBackend.SandboxError
-> {
-  const { fetcher } = useAuthenticatedBackend();
-
-  const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.CoreBank.BankAccountGetWithdrawalResponse>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`withdrawals/${wid}`], fetcher, {
+export function useWithdrawalDetails(wid: string) {
+  // const { state: credentials } = useBackendState();
+  const { api } = useBankCoreApiContext();
+
+  async function fetcher(wid: string) {
+    return await api.getWithdrawalById(wid)
+  }
+
+  const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getWithdrawalById">, TalerHttpError>(
+    [wid], fetcher, {
     refreshInterval: 1000,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -218,25 +77,22 @@ export function useWithdrawalDetails(
     keepPreviousData: true,
   });
 
-  // 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 function useTransactionDetails(
-  account: string,
-  tid: string,
-): HttpResponse<
-  SandboxBackend.CoreBank.BankAccountTransactionInfo,
-  SandboxBackend.SandboxError
-> {
-  const { paginatedFetcher } = useAuthenticatedBackend();
-
-  const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionInfo>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`accounts/${account}/transactions/${tid}`], paginatedFetcher, {
+export function useTransactionDetails(account: string, tid: number) {
+  const { state: credentials } = useBackendState();
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+  const { api } = useBankCoreApiContext();
+
+  async function fetcher([username, token, txid]: [string, AccessToken, 
number]) {
+    return await api.getTransactionById({ username, token }, txid)
+  }
+
+  const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getTransactionById">, TalerHttpError>(
+    [account, token, tid], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -248,60 +104,37 @@ export function useTransactionDetails(
     keepPreviousData: true,
   });
 
-  // 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;
 }
 
-interface PaginationFilter {
-  // page: number;
-}
+export function usePublicAccounts(initial?: number) {
+  const [offset, setOffset] = useState<number | undefined>(initial);
+  const { api } = useBankCoreApiContext();
+
+  async function fetcher(txid: number | undefined) {
+    return await api.getPublicAccounts({
+      limit: MAX_RESULT_SIZE,
+      offset: txid ? String(txid) : undefined,
+      order: "asc"
+    })
+  }
+
+  const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getPublicAccounts">, 
TalerHttpError>([offset], fetcher);
 
-export function usePublicAccounts(
-  args?: PaginationFilter,
-): HttpResponsePaginated<
-  SandboxBackend.CoreBank.PublicAccountsResponse,
-  SandboxBackend.SandboxError
-> {
-  const { paginatedFetcher } = usePublicBackend();
-
-  const [page, setPage] = useState(1);
-
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<SandboxBackend.CoreBank.PublicAccountsResponse>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`public-accounts`, page, PAGE_SIZE], paginatedFetcher);
-
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      SandboxBackend.CoreBank.PublicAccountsResponse,
-      SandboxBackend.SandboxError
-    >
-  >({ loading: true });
-
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData]);
-
-  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.public_accounts.length < PAGE_SIZE;
-  const isReachingStart = false;
+  const isLastPage =
+    data && data.body.public_accounts.length < PAGE_SIZE;
+  const isFirstPage = !initial;
 
   const pagination = {
-    isReachingEnd,
-    isReachingStart,
+    isLastPage,
+    isFirstPage,
     loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data.public_accounts.length < MAX_RESULT_SIZE) {
-        setPage(page + 1);
+      if (isLastPage || data?.type !== "ok") return;
+      const list = data.body.public_accounts
+      if (list.length < MAX_RESULT_SIZE) {
+        // setOffset(list[list.length-1].account_name);
       }
     },
     loadMorePrev: () => {
@@ -309,43 +142,39 @@ export function usePublicAccounts(
     },
   };
 
-  const public_accounts = !afterData
-    ? []
-    : (afterData || lastAfter).data.public_accounts;
-  if (loadingAfter) return { loading: true, data: { public_accounts } };
-  if (afterData) {
-    return { ok: true, data: { public_accounts }, ...pagination };
+  // const public_accountslist = data?.type !== "ok" ? [] : 
data.body.public_accounts;
+  if (data) {
+    return { ok: true, data: data.body, ...pagination }
   }
-  return { loading: true };
+  if (error) {
+    return error;
+  }
+  return undefined;
 }
 
 /**
- * FIXME: mutate result when balance change (transaction )
+
  * @param account
  * @param args
  * @returns
  */
-export function useTransactions(
-  account: string,
-  args?: PaginationFilter,
-): HttpResponsePaginated<
-  SandboxBackend.CoreBank.BankAccountTransactionsResponse,
-  SandboxBackend.SandboxError
-> {
-  const { paginatedFetcher } = useAuthenticatedBackend();
-
-  const [start, setStart] = useState<string>();
-
-  const {
-    data: afterData,
-    error: afterError,
-    isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<SandboxBackend.CoreBank.BankAccountTransactionsResponse>,
-    RequestError<SandboxBackend.SandboxError>
-  >(
-    [`accounts/${account}/transactions`, start, PAGE_SIZE],
-    paginatedFetcher, {
+export function useTransactions(account: string, initial?: number) {
+  const { state: credentials } = useBackendState();
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+
+  const [offset, setOffset] = useState<number | undefined>(initial);
+  const { api } = useBankCoreApiContext();
+
+  async function fetcher([username, token, txid]: [string, AccessToken, number 
| undefined]) {
+    return await api.getTransactions({ username, token }, {
+      limit: MAX_RESULT_SIZE,
+      offset: txid ? String(txid) : undefined,
+      order: "dec"
+    })
+  }
+
+  const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getTransactions">, TalerHttpError>(
+    [account, token, offset], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     refreshWhenOffline: false,
@@ -356,50 +185,30 @@ export function useTransactions(
   }
   );
 
-  const [lastAfter, setLastAfter] = useState<
-    HttpResponse<
-      SandboxBackend.CoreBank.BankAccountTransactionsResponse,
-      SandboxBackend.SandboxError
-    >
-  >({ loading: true });
-
-  useEffect(() => {
-    if (afterData) setLastAfter(afterData);
-  }, [afterData]);
-
-  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.transactions.length < PAGE_SIZE;
-  const isReachingStart = start == undefined;
+  const isLastPage =
+    data && data.type === "ok" && data.body.transactions.length < PAGE_SIZE;
+  const isFirstPage = true;
 
   const pagination = {
-    isReachingEnd,
-    isReachingStart,
+    isLastPage,
+    isFirstPage,
     loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      // if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
-      const l = 
afterData.data.transactions[afterData.data.transactions.length-1]
-      setStart(String(l.row_id));
-      // }
+      if (isLastPage || data?.type !== "ok") return;
+      const list = data.body.transactions
+      if (list.length < MAX_RESULT_SIZE) {
+        setOffset(list[list.length - 1].row_id);
+      }
     },
     loadMorePrev: () => {
-      if (!afterData || isReachingStart) return;
-      // if (afterData.data.transactions.length < MAX_RESULT_SIZE) {
-      setStart(undefined)
-      // }
+      null;
     },
   };
 
-  const transactions = !afterData
-    ? []
-    : (afterData || lastAfter).data.transactions;
-  if (loadingAfter) return { loading: true, data: { transactions } };
-  if (afterData) {
-    return { ok: true, data: { transactions }, ...pagination };
+  if (data) {
+    return { ok: true, data, ...pagination }
+  }
+  if (error) {
+    return error;
   }
-  return { loading: true };
+  return undefined;
 }
diff --git a/packages/demobank-ui/src/hooks/backend.ts 
b/packages/demobank-ui/src/hooks/backend.ts
index 889618646..589d7fab0 100644
--- a/packages/demobank-ui/src/hooks/backend.ts
+++ b/packages/demobank-ui/src/hooks/backend.ts
@@ -15,6 +15,7 @@
  */
 
 import {
+  AccessToken,
   Codec,
   buildCodecForObject,
   buildCodecForUnion,
@@ -24,23 +25,11 @@ import {
   codecForString,
 } from "@gnu-taler/taler-util";
 import {
-  ErrorType,
-  HttpError,
-  RequestError,
   buildStorageKey,
-  useLocalStorage,
+  useLocalStorage
 } from "@gnu-taler/web-util/browser";
-import {
-  HttpResponse,
-  HttpResponseOk,
-  RequestOptions,
-} from "@gnu-taler/web-util/browser";
-import { useApiContext } from "@gnu-taler/web-util/browser";
-import { useCallback, useEffect, useState } from "preact/hooks";
 import { useSWRConfig } from "swr";
-import { useBackendContext } from "../context/backend.js";
 import { bankUiSettings } from "../settings.js";
-import { AccessToken } from "./useCredentialsChecker.js";
 
 /**
  * Has the information to reach and
@@ -91,34 +80,6 @@ export const codecForBackendState = (): Codec<BackendState> 
=>
     .alternative("expired", codecForBackendStateExpired())
     .build("BackendState");
 
-export function getInitialBackendBaseURL(): string {
-  const overrideUrl =
-    typeof localStorage !== "undefined"
-      ? localStorage.getItem("bank-base-url")
-      : undefined;
-  let result: string;
-  if (!overrideUrl) {
-    //normal path
-    if (!bankUiSettings.backendBaseURL) {
-      console.error(
-        "ERROR: backendBaseURL was overridden by a setting file and missing. 
Setting value to 'window.origin'",
-      );
-      result = window.origin
-    } else {
-      result = bankUiSettings.backendBaseURL;
-    }
-  } else {
-    // testing/development path
-    result = overrideUrl
-  }
-  try {
-    return canonicalizeBaseUrl(result)
-  } catch (e) {
-    //fall back
-    return canonicalizeBaseUrl(window.origin)
-  }
-}
-
 export const defaultState: BackendState = {
   status: "loggedOut",
 };
@@ -127,7 +88,7 @@ export interface BackendStateHandler {
   state: BackendState;
   logOut(): void;
   expired(): void;
-  logIn(info: {username: string, token: AccessToken}): void;
+  logIn(info: { username: string, token: AccessToken }): void;
 }
 
 const BACKEND_STATE_KEY = buildStorageKey(
@@ -174,226 +135,6 @@ export function useBackendState(): BackendStateHandler {
   };
 }
 
-interface useBackendType {
-  request: <T>(
-    path: string,
-    options?: RequestOptions,
-  ) => Promise<HttpResponseOk<T>>;
-  fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
-  multiFetcher: <T>(endpoint: string[][]) => Promise<HttpResponseOk<T>[]>;
-  paginatedFetcher: <T>(
-    args: [string, string | undefined, number],
-  ) => Promise<HttpResponseOk<T>>;
-  sandboxAccountsFetcher: <T>(
-    args: [string, number, number, string],
-  ) => Promise<HttpResponseOk<T>>;
-  sandboxCashoutFetcher: <T>(endpoint: string[]) => Promise<HttpResponseOk<T>>;
-}
-export function usePublicBackend(): useBackendType {
-  const { request: requestHandler } = useApiContext();
-
-  const baseUrl = getInitialBackendBaseURL();
-
-  const request = useCallback(
-    function requestImpl<T>(
-      path: string,
-      options: RequestOptions = {},
-    ): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, path, options);
-    },
-    [baseUrl],
-  );
-
-  const fetcher = useCallback(
-    function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint);
-    },
-    [baseUrl],
-  );
-  const paginatedFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, start, size]: [
-      string,
-      string | undefined,
-      number,
-    ]): Promise<HttpResponseOk<T>> {
-      const delta = -1 * size //descending order
-      const params = start ? { delta, start } : { delta }
-      return requestHandler<T>(baseUrl, endpoint, {
-        params,
-      });
-    },
-    [baseUrl],
-  );
-  const multiFetcher = useCallback(
-    function multiFetcherImpl<T>([endpoints]: string[][]): Promise<
-      HttpResponseOk<T>[]
-    > {
-      return Promise.all(
-        endpoints.map((endpoint) => requestHandler<T>(baseUrl, endpoint)),
-      );
-    },
-    [baseUrl],
-  );
-  const sandboxAccountsFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, page, size, account]: [
-      string,
-      number,
-      number,
-      string,
-    ]): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, {
-        params: { page: page || 1, size },
-      });
-    },
-    [baseUrl],
-  );
-  const sandboxCashoutFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, account]: string[]): Promise<
-      HttpResponseOk<T>
-    > {
-      return requestHandler<T>(baseUrl, endpoint);
-    },
-    [baseUrl],
-  );
-  return {
-    request,
-    fetcher,
-    paginatedFetcher,
-    multiFetcher,
-    sandboxAccountsFetcher,
-    sandboxCashoutFetcher,
-  };
-}
-
-type CheckResult = ValidResult | RequestInvalidResult | InvalidationResult;
-
-interface ValidResult {
-  valid: true;
-}
-interface RequestInvalidResult {
-  valid: false;
-  requestError: true;
-  cause: RequestError<any>["cause"];
-}
-interface InvalidationResult {
-  valid: false;
-  requestError: false;
-  error: unknown;
-}
-
-export function useAuthenticatedBackend(): useBackendType {
-  const { state } = useBackendContext();
-  const { request: requestHandler } = useApiContext();
-
-  // FIXME: libeufin returns 400 insteand of 401 if there is no auth token
-  const creds = state.status === "loggedIn" ? state.token : "secret-token:a"; 
-  const baseUrl = getInitialBackendBaseURL();
-
-  const request = useCallback(
-    function requestImpl<T>(
-      path: string,
-      options: RequestOptions = {},
-    ): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, path, { token: creds, ...options });
-    },
-    [baseUrl, creds],
-  );
-
-  const fetcher = useCallback(
-    function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, { token: creds });
-    },
-    [baseUrl, creds],
-  );
-  const paginatedFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, start, size]: [
-      string,
-      string | undefined,
-      number,
-    ]): Promise<HttpResponseOk<T>> {
-      const delta = -1 * size //descending order
-      const params = start ? { delta, start } : { delta }
-      return requestHandler<T>(baseUrl, endpoint, {
-        token: creds,
-        params,
-      });
-    },
-    [baseUrl, creds],
-  );
-  const multiFetcher = useCallback(
-    function multiFetcherImpl<T>([endpoints]: string[][]): Promise<
-      HttpResponseOk<T>[]
-    > {
-      return Promise.all(
-        endpoints.map((endpoint) =>
-          requestHandler<T>(baseUrl, endpoint, { token: creds }),
-        ),
-      );
-    },
-    [baseUrl, creds],
-  );
-  const sandboxAccountsFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, page, size, account]: [
-      string,
-      number,
-      number,
-      string,
-    ]): Promise<HttpResponseOk<T>> {
-      return requestHandler<T>(baseUrl, endpoint, {
-        token: creds,
-        params: { page: page || 1, size },
-      });
-    },
-    [baseUrl],
-  );
-
-  const sandboxCashoutFetcher = useCallback(
-    function fetcherImpl<T>([endpoint, account]: string[]): Promise<
-      HttpResponseOk<T>
-    > {
-      return requestHandler<T>(baseUrl, endpoint, {
-        token: creds,
-        params: { account },
-      });
-    },
-    [baseUrl, creds],
-  );
-  return {
-    request,
-    fetcher,
-    paginatedFetcher,
-    multiFetcher,
-    sandboxAccountsFetcher,
-    sandboxCashoutFetcher,
-  };
-}
-/**
- *
- * @deprecated
- */
-export function useBackendConfig(): HttpResponse<
-  SandboxBackend.Config,
-  SandboxBackend.SandboxError
-> {
-  const { request } = usePublicBackend();
-
-  type Type = SandboxBackend.Config;
-
-  const [result, setResult] = useState<
-    HttpResponse<Type, SandboxBackend.SandboxError>
-  >({ loading: true });
-
-  useEffect(() => {
-    request<Type>(`/config`)
-      .then((data) => setResult(data))
-      .catch((error: RequestError<SandboxBackend.SandboxError>) =>
-        setResult(error.cause),
-      );
-  }, [request]);
-
-  return result;
-}
-
 export function useMatchMutate(): (
   re: RegExp,
   value?: unknown,
diff --git a/packages/demobank-ui/src/hooks/circuit.ts 
b/packages/demobank-ui/src/hooks/circuit.ts
index 5dba60951..208663f8b 100644
--- a/packages/demobank-ui/src/hooks/circuit.ts
+++ b/packages/demobank-ui/src/hooks/circuit.ts
@@ -14,239 +14,18 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import {
-  HttpResponse,
-  HttpResponseOk,
-  HttpResponsePaginated,
-  RequestError,
-  useApiContext,
-} from "@gnu-taler/web-util/browser";
-import { useEffect, useMemo, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
 import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
-import {
-  getInitialBackendBaseURL,
-  useAuthenticatedBackend,
-  useMatchMutate,
-} from "./backend.js";
+import { useBackendState } from "./backend.js";
 
 // FIX default import https://github.com/microsoft/TypeScript/issues/49189
+import { AccessToken, AmountJson, Amounts, OperationOk, 
TalerCoreBankResultByMethod, TalerCorebankApi, TalerError, TalerHttpError } 
from "@gnu-taler/taler-util";
 import _useSWR, { SWRHook } from "swr";
-import { AmountJson, Amounts } from "@gnu-taler/taler-util";
-import { AccessToken } from "./useCredentialsChecker.js";
-const useSWR = _useSWR as unknown as SWRHook;
-
-export function useAdminAccountAPI(): AdminAccountAPI {
-  const { request } = useAuthenticatedBackend();
-  const mutateAll = useMatchMutate();
-  const { state, logIn } = useBackendContext();
-  if (state.status === "loggedOut") {
-    throw Error("access-api can't be used when the user is not logged In");
-  }
-
-  const createAccount = async (
-    data: SandboxBackend.Circuit.CircuitAccountRequest,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/accounts`, {
-      method: "POST",
-      data,
-      contentType: "json",
-    });
-    await mutateAll(/.*circuit-api\/accounts.*/);
-    return res;
-  };
-
-  const updateAccount = async (
-    account: string,
-    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/accounts/${account}`, {
-      method: "PATCH",
-      data,
-      contentType: "json",
-    });
-    await mutateAll(/.*circuit-api\/accounts.*/);
-    return res;
-  };
-  const deleteAccount = async (
-    account: string,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/accounts/${account}`, {
-      method: "DELETE",
-      contentType: "json",
-    });
-    await mutateAll(/.*circuit-api\/accounts.*/);
-    return res;
-  };
-  const changePassword = async (
-    account: string,
-    data: SandboxBackend.Circuit.AccountPasswordChange,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/accounts/${account}/auth`, {
-      method: "PATCH",
-      data,
-      contentType: "json",
-    });
-    if (account === state.username) {
-      await mutateAll(/.*/);
-      logIn({
-        username: account,
-        //FIXME: change password api
-        token: data.new_password as AccessToken,
-      });
-    }
-    return res;
-  };
-
-  return { createAccount, deleteAccount, updateAccount, changePassword };
-}
-
-export function useCircuitAccountAPI(): CircuitAccountAPI {
-  const { request } = useAuthenticatedBackend();
-  const mutateAll = useMatchMutate();
-  const { state } = useBackendContext();
-  if (state.status === "loggedOut") {
-    throw Error("access-api can't be used when the user is not logged In");
-  }
-  const account = state.username;
-
-  const updateAccount = async (
-    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/accounts/${account}`, {
-      method: "PATCH",
-      data,
-      contentType: "json",
-    });
-    await mutateAll(/.*circuit-api\/accounts.*/);
-    return res;
-  };
-  const changePassword = async (
-    data: SandboxBackend.Circuit.AccountPasswordChange,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/accounts/${account}/auth`, {
-      method: "PATCH",
-      data,
-      contentType: "json",
-    });
-    return res;
-  };
-
-  const createCashout = async (
-    data: SandboxBackend.Circuit.CashoutRequest,
-  ): Promise<HttpResponseOk<SandboxBackend.Circuit.CashoutPending>> => {
-    const res = await request<SandboxBackend.Circuit.CashoutPending>(
-      `circuit-api/cashouts`,
-      {
-        method: "POST",
-        data,
-        contentType: "json",
-      },
-    );
-    return res;
-  };
-
-  const confirmCashout = async (
-    cashoutId: string,
-    data: SandboxBackend.Circuit.CashoutConfirm,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(
-      `circuit-api/cashouts/${cashoutId}/confirm`,
-      {
-        method: "POST",
-        data,
-        contentType: "json",
-      },
-    );
-    await mutateAll(/.*circuit-api\/cashout.*/);
-    return res;
-  };
-
-  const abortCashout = async (
-    cashoutId: string,
-  ): Promise<HttpResponseOk<void>> => {
-    const res = await request<void>(`circuit-api/cashouts/${cashoutId}/abort`, 
{
-      method: "POST",
-      contentType: "json",
-    });
-    await mutateAll(/.*circuit-api\/cashout.*/);
-    return res;
-  };
-
-  return {
-    updateAccount,
-    changePassword,
-    createCashout,
-    confirmCashout,
-    abortCashout,
-  };
-}
+import { useBankCoreApiContext } from "../context/config.js";
+import { assertUnreachable } from "../pages/HomePage.js";
 
-export interface AdminAccountAPI {
-  createAccount: (
-    data: SandboxBackend.Circuit.CircuitAccountRequest,
-  ) => Promise<HttpResponseOk<void>>;
-  deleteAccount: (account: string) => Promise<HttpResponseOk<void>>;
-
-  updateAccount: (
-    account: string,
-    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
-  ) => Promise<HttpResponseOk<void>>;
-  changePassword: (
-    account: string,
-    data: SandboxBackend.Circuit.AccountPasswordChange,
-  ) => Promise<HttpResponseOk<void>>;
-}
-
-export interface CircuitAccountAPI {
-  updateAccount: (
-    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
-  ) => Promise<HttpResponseOk<void>>;
-  changePassword: (
-    data: SandboxBackend.Circuit.AccountPasswordChange,
-  ) => Promise<HttpResponseOk<void>>;
-  createCashout: (
-    data: SandboxBackend.Circuit.CashoutRequest,
-  ) => Promise<HttpResponseOk<SandboxBackend.Circuit.CashoutPending>>;
-  confirmCashout: (
-    id: string,
-    data: SandboxBackend.Circuit.CashoutConfirm,
-  ) => Promise<HttpResponseOk<void>>;
-  abortCashout: (id: string) => Promise<HttpResponseOk<void>>;
-}
-
-async function getBusinessStatus(
-  request: ReturnType<typeof useApiContext>["request"],
-  username: string,
-  token: AccessToken,
-): Promise<boolean> {
-  try {
-    const url = getInitialBackendBaseURL();
-    const result = await request<SandboxBackend.Circuit.CircuitAccountData>(
-      url,
-      `circuit-api/accounts/${username}`,
-      { token },
-    );
-    return result.ok;
-  } catch (error) {
-    return false;
-  }
-}
-
-async function getEstimationByCredit(
-  request: ReturnType<typeof useApiContext>["request"],
-  basicAuth: { username: string; password: string },
-): Promise<boolean> {
-  try {
-    const url = getInitialBackendBaseURL();
-    const result = await request<
-      HttpResponseOk<SandboxBackend.Circuit.CircuitAccountData>
-    >(url, `circuit-api/accounts/${basicAuth.username}`, { basicAuth });
-    return result.ok;
-  } catch (error) {
-    return false;
-  }
-}
+const useSWR = _useSWR as unknown as SWRHook;
 
 export type TransferCalculation = {
   debit: AmountJson;
@@ -266,37 +45,27 @@ type CashoutEstimators = {
 
 export function useEstimator(): CashoutEstimators {
   const { state } = useBackendContext();
-  const { request } = useApiContext();
+  const { api } = useBankCoreApiContext();
   const creds =
     state.status !== "loggedIn"
       ? undefined
       : state.token;
   return {
     estimateByCredit: async (amount, fee, rate) => {
-      const zeroBalance = Amounts.zeroOfCurrency(fee.currency);
-      const zeroFiat = Amounts.zeroOfCurrency(fee.currency);
-      const zeroCalc = {
-        debit: zeroBalance,
-        credit: zeroFiat,
-        beforeFee: zeroBalance,
-      };
-      const url = getInitialBackendBaseURL();
-      const result = await request<SandboxBackend.Circuit.CashoutEstimate>(
-        url,
-        `circuit-api/cashouts/estimates`,
-        {
-          token: creds,
-          params: {
-            amount_credit: Amounts.stringify(amount),
-          },
-        },
-      );
-      // const credit = Amounts.parseOrThrow(result.data.data.amount_credit);
+      const resp = await api.getCashoutRate({
+        credit: amount
+      });
+      if (resp.type === "fail") {
+        // can't happen
+        // not-supported: it should not be able to call this function
+        // wrong-calculation: we are using just one parameter
+        throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
+      }
       const credit = amount;
       const _credit = { ...credit, currency: fee.currency };
       const beforeFee = Amounts.sub(_credit, fee).amount;
 
-      const debit = Amounts.parseOrThrow(result.data.amount_debit);
+      const debit = Amounts.parseOrThrow(resp.body.amount_debit);
       return {
         debit,
         beforeFee,
@@ -311,18 +80,14 @@ export function useEstimator(): CashoutEstimators {
         credit: zeroFiat,
         beforeFee: zeroBalance,
       };
-      const url = getInitialBackendBaseURL();
-      const result = await request<SandboxBackend.Circuit.CashoutEstimate>(
-        url,
-        `circuit-api/cashouts/estimates`,
-        {
-          token: creds,
-          params: {
-            amount_debit: Amounts.stringify(amount),
-          },
-        },
-      );
-      const credit = Amounts.parseOrThrow(result.data.amount_credit);
+      const resp = await api.getCashoutRate({ debit: amount });
+      if (resp.type === "fail") {
+        // can't happen
+        // not-supported: it should not be able to call this function
+        // wrong-calculation: we are using just one parameter
+        throw TalerError.fromDetail(resp.detail.code, {}, resp.detail.hint)
+      }
+      const credit = Amounts.parseOrThrow(resp.body.amount_credit);
       const _credit = { ...credit, currency: fee.currency };
       const debit = amount;
       const beforeFee = Amounts.sub(_credit, fee).amount;
@@ -335,67 +100,15 @@ export function useEstimator(): CashoutEstimators {
   };
 }
 
-export function useBusinessAccountFlag(): boolean | undefined {
-  const [isBusiness, setIsBusiness] = useState<boolean | undefined>();
-  const { state } = useBackendContext();
-  const { request } = useApiContext();
-  const creds =
-    state.status !== "loggedIn"
-      ? undefined
-      : {user: state.username, token: state.token};
-
-  useEffect(() => {
-    if (!creds) return;
-    getBusinessStatus(request, creds.user, creds.token)
-      .then((result) => {
-        setIsBusiness(result);
-      })
-      .catch((error) => {
-        setIsBusiness(false);
-      });
-  });
-
-  return isBusiness;
-}
-
-export function useBusinessAccountDetails(
-  account: string,
-): HttpResponse<
-  SandboxBackend.Circuit.CircuitAccountData,
-  SandboxBackend.SandboxError
-> {
-  const { fetcher } = useAuthenticatedBackend();
-
-  const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.Circuit.CircuitAccountData>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`circuit-api/accounts/${account}`], fetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-    errorRetryCount: 0,
-    errorRetryInterval: 1,
-    shouldRetryOnError: false,
-    keepPreviousData: true,
-  });
-
-  if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
-}
-
-export function useRatiosAndFeeConfig(): HttpResponse<
-  SandboxBackend.Circuit.Config,
-  SandboxBackend.SandboxError
-> {
-  const { fetcher } = useAuthenticatedBackend();
+export function useRatiosAndFeeConfig() {
+  const { api } = useBankCoreApiContext();
+  function fetcher() {
+    return api.getConversionRates()
+  }
 
   const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.Circuit.Config>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`circuit-api/config`], fetcher, {
+    TalerCoreBankResultByMethod<"getConversionRates">, TalerHttpError
+  >([], fetcher, {
     refreshInterval: 60 * 1000,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -409,8 +122,8 @@ export function useRatiosAndFeeConfig(): HttpResponse<
   });
 
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
 
 interface PaginationFilter {
@@ -418,58 +131,49 @@ interface PaginationFilter {
   page?: number;
 }
 
-export function useBusinessAccounts(
-  args?: PaginationFilter,
-): HttpResponsePaginated<
-  SandboxBackend.Circuit.CircuitAccounts,
-  SandboxBackend.SandboxError
-> {
-  const { sandboxAccountsFetcher } = useAuthenticatedBackend();
-  const [page, setPage] = useState(0);
+export function useBusinessAccounts() {
+  const { state: credentials } = useBackendState();
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+  const { api } = useBankCoreApiContext();
 
-  const {
-    data: afterData,
-    error: afterError,
-    // isValidating: loadingAfter,
-  } = useSWR<
-    HttpResponseOk<SandboxBackend.Circuit.CircuitAccounts>,
-    RequestError<SandboxBackend.SandboxError>
-  >(
-    [`accounts`, args?.page, PAGE_SIZE, args?.account],
-    sandboxAccountsFetcher,
-    {
-      refreshInterval: 0,
-      refreshWhenHidden: false,
-      revalidateOnFocus: false,
-      revalidateOnReconnect: false,
-      refreshWhenOffline: false,
-      errorRetryCount: 0,
-      errorRetryInterval: 1,
-      shouldRetryOnError: false,
-      keepPreviousData: true,
-    },
-  );
+  const [offset, setOffset] = useState<string | undefined>();
 
-  // const [lastAfter, setLastAfter] = useState<
-  //   HttpResponse<SandboxBackend.Circuit.CircuitAccounts, 
SandboxBackend.SandboxError>
-  // >({ loading: true });
+  function fetcher(token: AccessToken, offset?: string) {
+    return api.getAccounts(token, {
+      limit: MAX_RESULT_SIZE,
+      offset,
+      order: "asc"
+    })
+  }
 
-  // useEffect(() => {
-  //   if (afterData) setLastAfter(afterData);
-  // }, [afterData]);
+  const { data, error } = useSWR<TalerCoreBankResultByMethod<"getAccounts">, 
TalerHttpError>(
+    [token, offset], fetcher, {
+    refreshInterval: 0,
+    refreshWhenHidden: false,
+    revalidateOnFocus: false,
+    revalidateOnReconnect: false,
+    refreshWhenOffline: false,
+    errorRetryCount: 0,
+    errorRetryInterval: 1,
+    shouldRetryOnError: false,
+    keepPreviousData: true,
+  },
+  );
 
-  // if the query returns less that we ask, then we have reach the end or 
beginning
-  const isReachingEnd =
-    afterData && afterData.data?.customers?.length < PAGE_SIZE;
-  const isReachingStart = false;
+  const isLastPage =
+    data && data.type === "ok" && data.body.accounts.length < PAGE_SIZE;
+  const isFirstPage = false;
 
   const pagination = {
-    isReachingEnd,
-    isReachingStart,
+    isLastPage,
+    isFirstPage,
     loadMore: () => {
-      if (!afterData || isReachingEnd) return;
-      if (afterData.data?.customers?.length < MAX_RESULT_SIZE) {
-        setPage(page + 1);
+      if (isLastPage || data?.type !== "ok") return;
+      const list = data.body.accounts
+      if (list.length < MAX_RESULT_SIZE) {
+        //FIXME: define pagination
+
+        // setOffset(list[list.length - 1].row_id);
       }
     },
     loadMorePrev: () => {
@@ -477,85 +181,65 @@ export function useBusinessAccounts(
     },
   };
 
-  const result = useMemo(() => {
-    const customers = !afterData ? [] : afterData?.data?.customers ?? [];
-    return { ok: true as const, data: { customers }, ...pagination };
-  }, [afterData?.data]);
-
-  if (afterError) return afterError.cause;
-  if (afterData) {
-    return result;
-  }
+  if (data) return { ok: true, data, ...pagination };
+  if (error) return error;
+  return undefined;
+}
 
-  // if (loadingAfter)
-  //   return { loading: true, data: { customers } };
-  // if (afterData) {
-  //   return { ok: true, data: { customers }, ...pagination };
-  // }
-  return { loading: true };
+type CashoutWithId = TalerCorebankApi.CashoutStatusResponse & { id: string }
+function notUndefined(c: CashoutWithId | undefined): c is CashoutWithId {
+  return c !== undefined
 }
+export function useCashouts(account: string) {
+  const { state: credentials } = useBackendState();
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+  const { api } = useBankCoreApiContext();
+
+  async function fetcher([username, token]: [string, AccessToken]) {
+    const list = await api.getAccountCashouts({ username, token })
+    if (list.type !== "ok") {
+      assertUnreachable(list.type)
+    }
+    const all: Array<CashoutWithId | undefined> = await 
Promise.all(list.body.cashouts.map(c => {
+      return api.getCashoutById({ username, token }, c.cashout_id).then(r => {
+        if (r.type === "fail") return undefined
+        return { ...r.body, id: c.cashout_id }
+      })
+    }))
 
-export function useCashouts(
-  account: string,
-): HttpResponse<
-  SandboxBackend.Circuit.CashoutStatusResponseWithId[],
-  SandboxBackend.SandboxError
-> {
-  const { sandboxCashoutFetcher, multiFetcher } = useAuthenticatedBackend();
+    const cashouts = all.filter(notUndefined)
+    return { type: "ok" as const, body: { cashouts } }
+  }
 
-  const { data: list, error: listError } = useSWR<
-    HttpResponseOk<SandboxBackend.Circuit.Cashouts>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`circuit-api/cashouts`, account], sandboxCashoutFetcher, {
+  const { data, error } = useSWR<OperationOk<{ cashouts: CashoutWithId[] }>, 
TalerHttpError>(
+    [account, token], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
     refreshWhenOffline: false,
+    errorRetryCount: 0,
+    errorRetryInterval: 1,
+    shouldRetryOnError: false,
+    keepPreviousData: true,
   });
 
-  const paths = ((list?.data && list?.data.cashouts) || []).map(
-    (cashoutId) => `circuit-api/cashouts/${cashoutId}`,
-  );
-  const { data: cashouts, error: productError } = useSWR<
-    HttpResponseOk<SandboxBackend.Circuit.CashoutStatusResponse>[],
-    RequestError<SandboxBackend.SandboxError>
-  >([paths], multiFetcher, {
-    refreshInterval: 0,
-    refreshWhenHidden: false,
-    revalidateOnFocus: false,
-    revalidateOnReconnect: false,
-    refreshWhenOffline: false,
-  });
+  if (data) return data;
+  if (error) return error;
+  return undefined;
+}
 
-  if (listError) return listError.cause;
-  if (productError) return productError.cause;
+export function useCashoutDetails(cashoutId: string) {
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+  const { api } = useBankCoreApiContext();
 
-  if (cashouts) {
-    const dataWithId = cashouts.map((d) => {
-      //take the id from the queried url
-      return {
-        ...d.data,
-        id: d.info?.url.replace(/.*\/cashouts\//, "") || "",
-      };
-    });
-    return { ok: true, data: dataWithId };
+  async function fetcher([username, token, id]: [string, AccessToken, string]) 
{
+    return api.getCashoutById({ username, token }, id)
   }
-  return { loading: true };
-}
-
-export function useCashoutDetails(
-  id: string,
-): HttpResponse<
-  SandboxBackend.Circuit.CashoutStatusResponse,
-  SandboxBackend.SandboxError
-> {
-  const { fetcher } = useAuthenticatedBackend();
 
-  const { data, error } = useSWR<
-    HttpResponseOk<SandboxBackend.Circuit.CashoutStatusResponse>,
-    RequestError<SandboxBackend.SandboxError>
-  >([`circuit-api/cashouts/${id}`], fetcher, {
+  const { data, error } = 
useSWR<TalerCoreBankResultByMethod<"getCashoutById">, TalerHttpError>(
+    [creds?.username, creds?.token, cashoutId], fetcher, {
     refreshInterval: 0,
     refreshWhenHidden: false,
     revalidateOnFocus: false,
@@ -568,6 +252,6 @@ export function useCashoutDetails(
   });
 
   if (data) return data;
-  if (error) return error.cause;
-  return { loading: true };
+  if (error) return error;
+  return undefined;
 }
diff --git a/packages/demobank-ui/src/hooks/config.ts 
b/packages/demobank-ui/src/hooks/config.ts
deleted file mode 100644
index a3bd294db..000000000
--- a/packages/demobank-ui/src/hooks/config.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { LibtoolVersion } from "@gnu-taler/taler-util";
-import { ErrorType, HttpError, HttpResponseServerError, RequestError, 
useApiContext } from "@gnu-taler/web-util/browser";
-import { useEffect, useState } from "preact/hooks";
-import { getInitialBackendBaseURL } from "./backend.js";
-
-/**
- * Protocol version spoken with the bank.
- *
- * Uses libtool's current:revision:age versioning.
- */
-export const BANK_INTEGRATION_PROTOCOL_VERSION = "0:0:0";
-
-async function getConfigState(
-  request: ReturnType<typeof useApiContext>["request"],
-): Promise<SandboxBackend.Config> {
-  const url = getInitialBackendBaseURL();
-  const result = await request<SandboxBackend.Config>(url, `config`);
-  return result.data;
-}
-
-export type ConfigResult = undefined
-  | { type: "ok", result: Required<SandboxBackend.Config> }
-  | { type: "wrong", result: SandboxBackend.Config }
-  | { type: "error", result: HttpError<SandboxBackend.SandboxError> }
-
-export function useConfigState(): ConfigResult {
-  const [checked, setChecked] = useState<ConfigResult>()
-  const { request } = useApiContext();
-
-  useEffect(() => {
-    getConfigState(request)
-      .then((result) => {
-        const r = LibtoolVersion.compare(BANK_INTEGRATION_PROTOCOL_VERSION, 
result.version)
-        if (r?.compatible) {
-          const complete: Required<SandboxBackend.Config> = {
-            currency_fraction_digits: result.currency_fraction_digits ?? 2,
-            currency_fraction_limit: result.currency_fraction_limit ?? 2,
-            fiat_currency: "",
-            have_cashout: result.have_cashout ?? false,
-            name: result.name,
-            version: result.version,
-          }
-          setChecked({ type: "ok", result: complete });
-        } else {
-          setChecked({ type: "wrong", result })
-        }
-      })
-      .catch((error: unknown) => {
-        if (error instanceof RequestError) {
-          const result = error.cause
-          setChecked({ type: "error", result });
-        }
-      });
-  }, []);
-
-  return checked;
-}
-
-
diff --git a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts 
b/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
deleted file mode 100644
index b3dedb654..000000000
--- a/packages/demobank-ui/src/hooks/useCredentialsChecker.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { AbsoluteTime, HttpStatusCode } from "@gnu-taler/taler-util";
-import { ErrorType, HttpError, RequestError, useApiContext } from 
"@gnu-taler/web-util/browser";
-import { getInitialBackendBaseURL } from "./backend.js";
-
-export function useCredentialsChecker() {
-  const { request } = useApiContext();
-  const baseUrl = getInitialBackendBaseURL();
-  //check against instance details endpoint
-  //while merchant backend doesn't have a login endpoint
-  async function requestNewLoginToken(
-    username: string,
-    password: string,
-  ): Promise<LoginResult> {
-    const data: LoginTokenRequest = {
-      scope: "readwrite" as "write", //FIX: different than merchant
-      duration: {
-        // d_us: "forever" //FIX: should return shortest
-        d_us: 60 * 60 * 24 * 7 * 1000 * 1000
-      },
-      refreshable: true,
-    }
-    try {
-      const response = await request<LoginTokenSuccessResponse>(baseUrl, 
`accounts/${username}/token`, {
-        method: "POST",
-        basicAuth: {
-          username,
-          password,
-        },
-        data,
-        contentType: "json"
-      });
-      return { valid: true, token: 
`secret-token:${response.data.access_token}` as AccessToken, expiration: 
response.data.expiration };
-    } catch (error) {
-      if (error instanceof RequestError) {
-        return { valid: false, cause: error.cause };
-      }
-
-      return {
-        valid: false, cause: {
-          type: ErrorType.UNEXPECTED,
-          loading: false,
-          info: {
-            hasToken: true,
-            status: 0,
-            options: {},
-            url: `/private/token`,
-            payload: {}
-          },
-          exception: error,
-          message: (error instanceof Error ? error.message : "unpexepected 
error")
-        }
-      };
-    }
-  };
-
-  async function refreshLoginToken(
-    baseUrl: string,
-    token: LoginToken
-  ): Promise<LoginResult> {
-
-    if 
(AbsoluteTime.isExpired(AbsoluteTime.fromProtocolTimestamp(token.expiration))) {
-      return {
-        valid: false, cause: {
-          type: ErrorType.CLIENT,
-          status: HttpStatusCode.Unauthorized,
-          message: "login token expired, login again.",
-          info: {
-            hasToken: true,
-            status: 401,
-            options: {},
-            url: `/private/token`,
-            payload: {}
-          },
-          payload: {}
-        },
-      }
-    }
-
-    return requestNewLoginToken(baseUrl, token.token)
-  }
-  return { requestNewLoginToken, refreshLoginToken }
-}
-
-export interface LoginToken {
-  token: AccessToken,
-  expiration: Timestamp,
-}
-// token used to get loginToken
-// must forget after used
-declare const __ac_token: unique symbol;
-export type AccessToken = string & {
-  [__ac_token]: true;
-};
-
-type YesOrNo = "yes" | "no";
-export type LoginResult = {
-  valid: true;
-  token: AccessToken;
-  expiration: Timestamp;
-} | {
-  valid: false;
-  cause: HttpError<{}>;
-}
-
-
-//   DELETE /private/instances/$INSTANCE
-export interface LoginTokenRequest {
-  // Scope of the token (which kinds of operations it will allow)
-  scope: "readonly" | "write";
-
-  // Server may impose its own upper bound
-  // on the token validity duration
-  duration?: RelativeTime;
-
-  // Can this token be refreshed?
-  // Defaults to false.
-  refreshable?: boolean;
-}
-export interface LoginTokenSuccessResponse {
-  // The login token that can be used to access resources
-  // that are in scope for some time. Must be prefixed
-  // with "Bearer " when used in the "Authorization" HTTP header.
-  // Will already begin with the RFC 8959 prefix.
-  access_token: AccessToken;
-
-  // Scope of the token (which kinds of operations it will allow)
-  scope: "readonly" | "write";
-
-  // Server may impose its own upper bound
-  // on the token validity duration
-  expiration: Timestamp;
-
-  // Can this token be refreshed?
-  refreshable: boolean;
-}
diff --git a/packages/demobank-ui/src/pages/AccountPage/index.ts 
b/packages/demobank-ui/src/pages/AccountPage/index.ts
index 9230fb6b1..ef6b4fede 100644
--- a/packages/demobank-ui/src/pages/AccountPage/index.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/index.ts
@@ -14,20 +14,17 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpError, HttpResponseOk, HttpResponsePaginated, utils } from 
"@gnu-taler/web-util/browser";
-import { AbsoluteTime, AmountJson, PaytoUriIBAN, PaytoUriTalerBank } from 
"@gnu-taler/taler-util";
-import { Loading } from "../../components/Loading.js";
-import { useComponentState } from "./state.js";
-import { ReadyView, InvalidIbanView } from "./views.js";
+import { AbsoluteTime, AmountJson, TalerCorebankApi, TalerError, 
TalerErrorDetail } from "@gnu-taler/taler-util";
+import { HttpResponsePaginated, utils } from "@gnu-taler/web-util/browser";
 import { VNode } from "preact";
-import { LoginForm } from "../LoginForm.js";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { Loading } from "../../components/Loading.js";
+import { LoginForm } from "../LoginForm.js";
+import { useComponentState } from "./state.js";
+import { InvalidIbanView, ReadyView } from "./views.js";
 
 export interface Props {
   account: string;
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
   goToBusinessAccount: () => void;
   goToConfirmOperation: (id: string) => void;
 }
@@ -42,7 +39,7 @@ export namespace State {
 
   export interface LoadingError {
     status: "loading-error";
-    error: HttpError<SandboxBackend.SandboxError>;
+    error: TalerError;
   }
 
   export interface BaseInfo {
@@ -60,12 +57,12 @@ export namespace State {
 
   export interface InvalidIban {
     status: "invalid-iban",
-    error: HttpResponseOk<SandboxBackend.CoreBank.AccountData>;
+    error: TalerCorebankApi.AccountData;
   }
 
   export interface UserNotFound {
-    status: "error-user-not-found",
-    error: HttpError<any>;
+    status: "login",
+    reason: "not-found" | "forbidden";
     onRegister?: () => void;
   }
 }
@@ -80,7 +77,7 @@ export interface Transaction {
 
 const viewMapping: utils.StateViewMap<State> = {
   loading: Loading,
-  "error-user-not-found": LoginForm,
+  "login": LoginForm,
   "invalid-iban": InvalidIbanView,
   "loading-error": ErrorLoading,
   ready: ReadyView,
diff --git a/packages/demobank-ui/src/pages/AccountPage/state.ts 
b/packages/demobank-ui/src/pages/AccountPage/state.ts
index ca7e1d447..96d45b7bd 100644
--- a/packages/demobank-ui/src/pages/AccountPage/state.ts
+++ b/packages/demobank-ui/src/pages/AccountPage/state.ts
@@ -14,54 +14,47 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, HttpStatusCode, parsePaytoUri } from "@gnu-taler/taler-util";
+import { Amounts, HttpStatusCode, TalerError, TalerErrorCode, parsePaytoUri } 
from "@gnu-taler/taler-util";
 import { ErrorType, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
-import { useBackendContext } from "../../context/backend.js";
 import { useAccountDetails } from "../../hooks/access.js";
 import { Props, State } from "./index.js";
+import { assertUnreachable } from "../HomePage.js";
 
 export function useComponentState({ account, goToBusinessAccount, 
goToConfirmOperation }: Props): State {
   const result = useAccountDetails(account);
-  const backend = useBackendContext();
   const { i18n } = useTranslationContext();
 
-  if (result.loading) {
+  if (!result) {
     return {
       status: "loading",
       error: undefined,
     };
   }
 
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return {
-        status: "loading-error",
-        error: result,
-      };
-    }
-    //logout if there is any error, not if loading
-    // backend.logOut();
-    if (result.status === HttpStatusCode.NotFound) {
-      notifyError(i18n.str`Username or account label "${account}" not found`, 
undefined);
-      return {
-        status: "error-user-not-found",
-        error: result,
-      };
-    }
-    if (result.status === HttpStatusCode.Unauthorized) {
-      notifyError(i18n.str`Authorization denied`, i18n.str`Maybe the session 
has expired, login again.`);
-      return {
-        status: "error-user-not-found",
-        error: result,
-      };
-    }
+  if (result instanceof TalerError) {
     return {
       status: "loading-error",
       error: result,
     };
   }
 
-  const { data } = result;
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "unauthorized": return {
+        status: "login",
+        reason: "forbidden"
+      }
+      case "not-found": return {
+        status: "login",
+        reason: "not-found",
+      }
+      default: {
+        assertUnreachable(result)
+      }
+    }
+  }
+
+  const { body: data } = result;
 
   const balance = Amounts.parseOrThrow(data.balance.amount);
 
@@ -71,7 +64,7 @@ export function useComponentState({ account, 
goToBusinessAccount, goToConfirmOpe
   if (!payto || !payto.isKnown || (payto.targetType !== "iban" && 
payto.targetType !== "x-taler-bank")) {
     return {
       status: "invalid-iban",
-      error: result
+      error: data
     };
   }
 
diff --git a/packages/demobank-ui/src/pages/AccountPage/views.tsx 
b/packages/demobank-ui/src/pages/AccountPage/views.tsx
index 483cb579a..0604001e3 100644
--- a/packages/demobank-ui/src/pages/AccountPage/views.tsx
+++ b/packages/demobank-ui/src/pages/AccountPage/views.tsx
@@ -18,14 +18,13 @@ import { useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Attention } from "../../components/Attention.js";
 import { Transactions } from "../../components/Transactions/index.js";
-import { useBusinessAccountDetails } from "../../hooks/circuit.js";
 import { useSettings } from "../../hooks/settings.js";
 import { PaymentOptions } from "../PaymentOptions.js";
 import { State } from "./index.js";
 
 export function InvalidIbanView({ error }: State.InvalidIban) {
   return (
-    <div>Payto from server is not valid 
&quot;{error.data.payto_uri}&quot;</div>
+    <div>Payto from server is not valid &quot;{error.payto_uri}&quot;</div>
   );
 }
 
@@ -75,19 +74,20 @@ function MaybeBusinessButton({
   onClick: () => void;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const result = useBusinessAccountDetails(account);
-  if (!result.ok) return <Fragment />;
-  return (
-    <div class="w-full flex justify-end">
-      <button
-        class="disabled:opacity-50 disabled:cursor-default cursor-pointer 
rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm 
hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
-        onClick={(e) => {
-          e.preventDefault()
-          onClick()
-        }}
-      >
-        <i18n.Translate>Business Profile</i18n.Translate>
-      </button>
-    </div>
-  );
+  return <Fragment />
+  // const result = useBusinessAccountDetails(account);
+  // if (!result.ok) return <Fragment />;
+  // return (
+  //   <div class="w-full flex justify-end">
+  //     <button
+  //       class="disabled:opacity-50 disabled:cursor-default cursor-pointer 
rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm 
hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
+  //       onClick={(e) => {
+  //         e.preventDefault()
+  //         onClick()
+  //       }}
+  //     >
+  //       <i18n.Translate>Business Profile</i18n.Translate>
+  //     </button>
+  //   </div>
+  // );
 }
diff --git a/packages/demobank-ui/src/pages/BankFrame.tsx 
b/packages/demobank-ui/src/pages/BankFrame.tsx
index 6ab6ba3e4..c75964f8e 100644
--- a/packages/demobank-ui/src/pages/BankFrame.tsx
+++ b/packages/demobank-ui/src/pages/BankFrame.tsx
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, Logger, TranslatedString, parsePaytoUri } from 
"@gnu-taler/taler-util";
+import { Amounts, Logger, TalerError, TranslatedString, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { notifyError, notifyException, useNotifications, useTranslationContext 
} from "@gnu-taler/web-util/browser";
 import { ComponentChildren, Fragment, VNode, h } from "preact";
 import { useEffect, useErrorBoundary, useState } from "preact/hooks";
@@ -27,6 +27,7 @@ import { useAccountDetails } from "../hooks/access.js";
 import { useSettings } from "../hooks/settings.js";
 import { bankUiSettings } from "../settings.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
+import { Loading } from "../components/Loading.js";
 
 const GIT_HASH = typeof __GIT_HASH__ !== "undefined" ? __GIT_HASH__ : 
undefined;
 const VERSION = typeof __VERSION__ !== "undefined" ? __VERSION__ : undefined;
@@ -237,7 +238,6 @@ export function BankFrame({
                                       </span>
                                     </span>
                                     <button type="button" 
data-enabled={settings.showDebugInfo} class="bg-indigo-600 
data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 
cursor-pointer rounded-full border-2 border-transparent transition-colors 
duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 
focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
-
                                       onClick={() => {
                                         updateSettings("showDebugInfo", 
!settings.showDebugInfo);
                                       }}>
@@ -346,14 +346,6 @@ function StatusBanner(): VNode {
               </div>
             }
             <MaybeShowDebugInfo info={n.message.debug} />
-            {/* <a href="#" class="text-gray-500">
-                show debug info
-              </a>
-              {n.message.debug &&
-                <div class="mt-2 text-sm text-red-700 font-mono break-all">
-                  {n.message.debug}
-                </div>
-              } */}
           </Attention>
         case "info":
           return <Attention type="success" title={n.message.title} onClose={() 
=> {
@@ -411,16 +403,22 @@ function WelcomeAccount({ account }: { account: string 
}): VNode {
   const { i18n } = useTranslationContext();
 
   const result = useAccountDetails(account);
-  if (!result.ok) return <div />
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <div />
+  }
+  if (result.type === "fail") return <div />
 
-  const payto = parsePaytoUri(result.data.payto_uri)
+  const payto = parsePaytoUri(result.body.payto_uri)
   if (!payto) return <div />
 
   const accountNumber = !payto.isKnown ? undefined : payto.targetType === 
"iban" ? payto.iban : payto.targetType === "x-taler-bank" ? payto.account : 
undefined;
   return <i18n.Translate>
     Welcome,  {account} {accountNumber !== undefined ?
       <span>
-        (<a href={result.data.payto_uri}>{accountNumber}</a> <CopyButton 
getContent={() => result.data.payto_uri} />)
+        (<a href={result.body.payto_uri}>{accountNumber}</a> <CopyButton 
getContent={() => result.body.payto_uri} />)
       </span>
       : <Fragment />}!
   </i18n.Translate>
@@ -429,10 +427,16 @@ function WelcomeAccount({ account }: { account: string 
}): VNode {
 
 function AccountBalance({ account }: { account: string }): VNode {
   const result = useAccountDetails(account);
-  if (!result.ok) return <div />
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <div />
+  }
+  if (result.type === "fail") return <div />
 
   return <RenderAmount
-    value={Amounts.parseOrThrow(result.data.balance.amount)}
-    negative={result.data.balance.credit_debit_indicator === "debit"}
+    value={Amounts.parseOrThrow(result.body.balance.amount)}
+    negative={result.body.balance.credit_debit_indicator === "debit"}
   />
 }
diff --git a/packages/demobank-ui/src/pages/HomePage.tsx 
b/packages/demobank-ui/src/pages/HomePage.tsx
index 95144f086..bd85cea1e 100644
--- a/packages/demobank-ui/src/pages/HomePage.tsx
+++ b/packages/demobank-ui/src/pages/HomePage.tsx
@@ -31,12 +31,11 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { Loading } from "../components/Loading.js";
-import { getInitialBackendBaseURL } from "../hooks/backend.js";
+import { useBankCoreApiContext } from "../context/config.js";
 import { useSettings } from "../hooks/settings.js";
 import { AccountPage } from "./AccountPage/index.js";
 import { LoginForm } from "./LoginForm.js";
 import { WithdrawalQRCode } from "./WithdrawalQRCode.js";
-import { route } from "preact-router";
 
 const logger = new Logger("AccountPage");
 
@@ -68,7 +67,6 @@ export function HomePage({
       account={account}
       goToConfirmOperation={goToConfirmOperation}
       goToBusinessAccount={goToBusinessAccount}
-      onLoadNotOk={handleNotOkResult(i18n)}
     />
   );
 }
@@ -82,9 +80,9 @@ export function WithdrawalOperationPage({
 }): VNode {
   //FIXME: libeufin sandbox should return show to create the integration api 
endpoint
   //or return withdrawal uri from response
-  const baseUrl = getInitialBackendBaseURL()
+  const { api } = useBankCoreApiContext()
   const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl: `${baseUrl}/taler-integration`,
+    bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
     withdrawalOperationId: operationId,
   });
   const parsedUri = parseWithdrawUri(uri);
@@ -110,76 +108,6 @@ export function WithdrawalOperationPage({
   );
 }
 
-export function handleNotOkResult(
-  i18n: ReturnType<typeof useTranslationContext>["i18n"],
-): <T>(
-  result:
-    | HttpResponsePaginated<T, SandboxBackend.SandboxError>
-    | HttpResponse<T, SandboxBackend.SandboxError>,
-) => VNode {
-  return function handleNotOkResult2<T>(
-    result:
-      | HttpResponsePaginated<T, SandboxBackend.SandboxError | undefined>
-      | HttpResponse<T, SandboxBackend.SandboxError | undefined>,
-  ): VNode {
-    if (result.loading) return <Loading />;
-    if (!result.ok) {
-      switch (result.type) {
-        case ErrorType.TIMEOUT: {
-          notifyError(i18n.str`Request timeout, try again later.`, undefined);
-          break;
-        }
-        case ErrorType.CLIENT: {
-          if (result.status === HttpStatusCode.Unauthorized) {
-            notifyError(i18n.str`Wrong credentials`, undefined);
-            return <LoginForm />;
-          }
-          const errorData = result.payload;
-          notify({
-            type: "error",
-            title: i18n.str`Could not load due to a request error`,
-            description: i18n.str`Request to url "${result.info.url}" returned 
${result.info.status}`,
-            debug: JSON.stringify(result),
-          });
-          break;
-        }
-        case ErrorType.SERVER: {
-          notify({
-            type: "error",
-            title: i18n.str`Server returned with error`,
-            description: result.payload?.error?.description as 
TranslatedString,
-            debug: JSON.stringify(result.payload),
-          });
-          break;
-        }
-        case ErrorType.UNREADABLE: {
-          notify({
-            type: "error",
-            title: i18n.str`Unexpected error.`,
-            description: i18n.str`Response from ${result.info?.url} is 
unreadable, http status: ${result.status}`,
-            debug: JSON.stringify(result),
-          });
-          break;
-        }
-        case ErrorType.UNEXPECTED: {
-          notify({
-            type: "error",
-            title: i18n.str`Unexpected error.`,
-            description: i18n.str`Diagnostic from ${result.info?.url} is 
"${result.message}"`,
-            debug: JSON.stringify(result),
-          });
-          break;
-        }
-        default: {
-          assertUnreachable(result);
-        }
-      }
-      // route("/")
-      return <div>error</div>;
-    }
-    return <div />;
-  };
-}
 export function assertUnreachable(x: never): never {
   throw new Error("Didn't expect to get here");
 }
diff --git a/packages/demobank-ui/src/pages/LoginForm.tsx 
b/packages/demobank-ui/src/pages/LoginForm.tsx
index 3ea94b899..a8167cca5 100644
--- a/packages/demobank-ui/src/pages/LoginForm.tsx
+++ b/packages/demobank-ui/src/pages/LoginForm.tsx
@@ -14,28 +14,29 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { ErrorType, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { HttpStatusCode, TalerAuthentication, TranslatedString } from 
"@gnu-taler/taler-util";
+import { ErrorType, notify, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { useBackendContext } from "../context/backend.js";
-import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
 import { bankUiSettings } from "../settings.js";
 import { undefinedIfEmpty } from "../utils.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { assertUnreachable } from "./HomePage.js";
 
 
 /**
  * Collect and submit login data.
  */
-export function LoginForm({ onRegister }: { onRegister?: () => void }): VNode {
+export function LoginForm({ reason, onRegister }: { reason?: "not-found" | 
"forbidden", onRegister?: () => void }): VNode {
   const backend = useBackendContext();
   const currentUser = backend.state.status !== "loggedOut" ? 
backend.state.username : undefined
   const [username, setUsername] = useState<string | undefined>(currentUser);
   const [password, setPassword] = useState<string | undefined>();
   const { i18n } = useTranslationContext();
-  const { requestNewLoginToken, refreshLoginToken } = useCredentialsChecker();
+  const { api } = useBankCoreApiContext();
 
 
   /**
@@ -70,10 +71,6 @@ export function LoginForm({ onRegister }: { onRegister?: () 
=> void }): VNode {
     password: !password ? i18n.str`Missing password` : undefined,
   }) ?? busy;
 
-  function saveError({ title, description, debug }: { title: TranslatedString, 
description?: TranslatedString, debug?: any }) {
-    notifyError(title, description, debug)
-  }
-
   async function doLogout() {
     backend.logOut()
   }
@@ -81,63 +78,42 @@ export function LoginForm({ onRegister }: { onRegister?: () 
=> void }): VNode {
   async function doLogin() {
     if (!username || !password) return;
     setBusy({})
-    const result = await requestNewLoginToken(username, password);
-    if (result.valid) {
-      backend.logIn({ username, token: result.token });
+    const data: TalerAuthentication.TokenRequest = {
+      // scope: "readwrite" as "write", //FIX: different than merchant
+      scope: "readwrite",
+      duration: {
+        d_us: "forever" //FIX: should return shortest
+        // d_us: 60 * 60 * 24 * 7 * 1000 * 1000
+      },
+      refreshable: true,
+    }
+    const resp = await 
api.getAuthenticationAPI(username).createAccessToken(password, {
+      // scope: "readwrite" as "write", //FIX: different than merchant
+      scope: "readwrite",
+      duration: {
+        d_us: "forever" //FIX: should return shortest
+        // d_us: 60 * 60 * 24 * 7 * 1000 * 1000
+      },
+      refreshable: true,
+    })
+    if (resp.type === "ok") {
+      backend.logIn({ username, token: resp.body.access_token });
     } else {
-      const { cause } = result;
-      switch (cause.type) {
-        case ErrorType.CLIENT: {
-          if (cause.status === HttpStatusCode.Unauthorized) {
-            saveError({
-              title: i18n.str`Wrong credentials for "${username}"`,
-            });
-          } else
-            if (cause.status === HttpStatusCode.NotFound) {
-              saveError({
-                title: i18n.str`Account not found`,
-              });
-            } else {
-              saveError({
-                title: i18n.str`Could not load due to a request error`,
-                description: i18n.str`Request to url "${cause.info.url}" 
returned ${cause.info.status}`,
-                debug: JSON.stringify(cause.payload),
-              });
-            }
-          break;
-        }
-        case ErrorType.SERVER: {
-          saveError({
-            title: i18n.str`Server had a problem, try again later or report.`,
-            // description: cause.payload.error.description,
-            debug: JSON.stringify(cause.payload),
-          });
-          break;
-        }
-        case ErrorType.TIMEOUT: {
-          saveError({
-            title: i18n.str`Request timeout, try again later.`,
-          });
-          break;
-        }
-        case ErrorType.UNREADABLE: {
-          saveError({
-            title: i18n.str`Unexpected error.`,
-            description: `Response from ${cause.info?.url} is unreadable, http 
status: ${cause.status}` as TranslatedString,
-            debug: JSON.stringify(cause),
-          });
-          break;
-        }
-        default: {
-          saveError({
-            title: i18n.str`Unexpected error, please report.`,
-            description: `Diagnostic from ${cause.info?.url} is 
"${cause.message}"` as TranslatedString,
-            debug: JSON.stringify(cause),
-          });
-          break;
-        }
+      switch (resp.case) {
+        case "wrong-credentials": return notify({
+          type: "error",
+          title: i18n.str`Wrong credentials for "${username}"`,
+          description: resp.detail.hint as TranslatedString,
+          debug: resp.detail,
+        })
+        case "not-found": return notify({
+          type: "error",
+          title: i18n.str`Account not found`,
+          description: resp.detail.hint as TranslatedString,
+          debug: resp.detail,
+        })
+        default: assertUnreachable(resp)
       }
-      // backend.logOut();
     }
     setPassword(undefined);
     setBusy(undefined)
diff --git a/packages/demobank-ui/src/pages/OperationState/index.ts 
b/packages/demobank-ui/src/pages/OperationState/index.ts
index b347fd942..bc3555c48 100644
--- a/packages/demobank-ui/src/pages/OperationState/index.ts
+++ b/packages/demobank-ui/src/pages/OperationState/index.ts
@@ -14,8 +14,8 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, AmountJson, WithdrawUriResult } from 
"@gnu-taler/taler-util";
-import { HttpError, utils } from "@gnu-taler/web-util/browser";
+import { AbsoluteTime, AmountJson, TalerCoreBankErrorsByMethod, TalerError, 
TalerErrorDetail, TranslatedString, WithdrawUriResult } from 
"@gnu-taler/taler-util";
+import { utils } from "@gnu-taler/web-util/browser";
 import { ErrorLoading } from "../../components/ErrorLoading.js";
 import { Loading } from "../../components/Loading.js";
 import { useComponentState } from "./state.js";
@@ -44,7 +44,7 @@ export namespace State {
 
   export interface LoadingError {
     status: "loading-error";
-    error: HttpError<SandboxBackend.SandboxError>;
+    error: TalerError;
   }
 
   /**
@@ -61,7 +61,7 @@ export namespace State {
   export interface InvalidPayto {
     status: "invalid-payto",
     error: undefined;
-    payto: string | null;
+    payto: string | undefined;
     onClose: () => void;
   }
   export interface InvalidWithdrawal {
@@ -74,7 +74,7 @@ export namespace State {
     status: "invalid-reserve",
     error: undefined;
     onClose: () => void;
-    reserve: string | null;
+    reserve: string | undefined;
   }
   export interface NeedConfirmation {
     status: "need-confirmation",
diff --git a/packages/demobank-ui/src/pages/OperationState/state.ts 
b/packages/demobank-ui/src/pages/OperationState/state.ts
index 4be680377..148571ec9 100644
--- a/packages/demobank-ui/src/pages/OperationState/state.ts
+++ b/packages/demobank-ui/src/pages/OperationState/state.ts
@@ -14,20 +14,26 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Amounts, HttpStatusCode, TranslatedString, parsePaytoUri, 
parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
+import { Amounts, HttpStatusCode, TalerError, TranslatedString, parsePaytoUri, 
parseWithdrawUri, stringifyWithdrawUri } from "@gnu-taler/taler-util";
 import { RequestError, notify, notifyError, notifyInfo, useTranslationContext, 
utils } from "@gnu-taler/web-util/browser";
 import { useEffect, useState } from "preact/hooks";
-import { useAccessAPI, useAccessAnonAPI, useWithdrawalDetails } from 
"../../hooks/access.js";
-import { getInitialBackendBaseURL } from "../../hooks/backend.js";
+import { useBankCoreApiContext } from "../../context/config.js";
+import { useWithdrawalDetails } from "../../hooks/access.js";
+import { useBackendState } from "../../hooks/backend.js";
 import { useSettings } from "../../hooks/settings.js";
 import { buildRequestErrorMessage } from "../../utils.js";
 import { Props, State } from "./index.js";
+import { assertUnreachable } from "../HomePage.js";
+import { mutate } from "swr";
 
 export function useComponentState({ currency, onClose }: Props): 
utils.RecursiveState<State> {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
-  const { createWithdrawal } = useAccessAPI();
-  const { abortWithdrawal, confirmWithdrawal } = useAccessAnonAPI();
+  const { state: credentials } = useBackendState()
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+  const { api } = useBankCoreApiContext()
+  // const { createWithdrawal } = useAccessAPI();
+  // const { abortWithdrawal, confirmWithdrawal } = useAccessAnonAPI();
   const [busy, setBusy] = useState<Record<string, undefined>>()
 
   const amount = settings.maxWithdrawalAmount
@@ -37,27 +43,33 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
     const parsedAmount = Amounts.parseOrThrow(`${currency}:${amount}`)
 
     try {
-      const result = await createWithdrawal({
+      if (!creds) return;
+      const resp = await api.createWithdrawal(creds, {
         amount: Amounts.stringify(parsedAmount),
       });
-      const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
+      if (resp.type === "fail") {
+        switch (resp.case) {
+          case "insufficient-funds": return notify({
+            type: "error",
+            title: i18n.str`The operation was rejected due to insufficient 
funds`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          default: assertUnreachable(resp.case)
+        }
+      }
+
+      const uri = parseWithdrawUri(resp.body.taler_withdraw_uri);
       if (!uri) {
         return notifyError(
           i18n.str`Server responded with an invalid withdraw URI`,
-          i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`);
+          i18n.str`Withdraw URI: ${resp.body.taler_withdraw_uri}`);
       } else {
         updateSettings("currentWithdrawalOperationId", 
uri.withdrawalOperationId)
       }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Forbidden
-                ? i18n.str`The operation was rejected due to insufficient 
funds`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -76,8 +88,6 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
     }
   }, [settings.fastWithdrawal, amount])
 
-  const baseUrl = getInitialBackendBaseURL()
-
   if (!withdrawalOperationId) {
     return {
       status: "loading",
@@ -90,18 +100,24 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
   async function doAbort() {
     try {
       setBusy({})
-      await abortWithdrawal(wid);
-      onClose();
+      const resp = await api.abortWithdrawalById(wid);
+      setBusy(undefined)
+      if (resp.type === "ok") {
+        onClose();
+      } else {
+        switch (resp.case) {
+          case "previously-confirmed": return notify({
+            type: "error",
+            title: i18n.str`The reserve operation has been confirmed 
previously and can't be aborted`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          default: assertUnreachable(resp.case)
+        }
+      }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`The reserve operation has been confirmed previously 
and can't be aborted`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -111,28 +127,38 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
         )
       }
     }
-    setBusy(undefined)
   }
 
   async function doConfirm() {
     try {
       setBusy({})
-      await confirmWithdrawal(wid);
-      if (!settings.showWithdrawalSuccess) {
-        notifyInfo(i18n.str`Wire transfer completed!`)
+      const resp = await api.confirmWithdrawalById(wid);
+      if (resp.type === "ok") {
+        mutate(() => true)//clean withdrawal state
+        if (!settings.showWithdrawalSuccess) {
+          notifyInfo(i18n.str`Wire transfer completed!`)
+        }
+        setBusy(undefined)
+      } else {
+        switch (resp.case) {
+          case "previously-aborted": return notify({
+            type: "error",
+            title: i18n.str`The withdrawal has been aborted previously and 
can't be confirmed`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "no-exchange-or-reserve-selected": return notify({
+            type: "error",
+            title: i18n.str`The withdraw operation cannot be confirmed because 
no exchange and reserve public key selection happened before`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          default: assertUnreachable(resp)
+        }
       }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`The withdrawal has been aborted previously and 
can't be confirmed`
-                : status === HttpStatusCode.UnprocessableEntity
-                  ? i18n.str`The withdraw operation cannot be confirmed 
because no exchange and reserve public key selection happened before`
-                  : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -142,11 +168,10 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
         )
       }
     }
-    setBusy(undefined)
   }
-  const bankIntegrationApiBaseUrl = `${baseUrl}/taler-integration`
+
   const uri = stringifyWithdrawUri({
-    bankIntegrationApiBaseUrl,
+    bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
     withdrawalOperationId,
   });
   const parsedUri = parseWithdrawUri(uri);
@@ -161,32 +186,43 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
 
   return (): utils.RecursiveState<State> => {
     const result = useWithdrawalDetails(withdrawalOperationId);
-    const shouldCreateNewOperation = !result.ok && !result.loading && 
result.info.status === HttpStatusCode.NotFound
+    const shouldCreateNewOperation = result && !(result instanceof TalerError)
 
     useEffect(() => {
       if (shouldCreateNewOperation) {
         doSilentStart()
       }
     }, [])
-    if (!result.ok) {
-      if (result.loading) {
-        return {
-          status: "loading",
-          error: undefined
-        }
-      }
-      if (result.info.status === HttpStatusCode.NotFound) {
-        return {
-          status: "loading",
-          error: undefined,
-        }
+    if (!result) {
+      return {
+        status: "loading",
+        error: undefined
       }
+    }
+    if (result instanceof TalerError) {
       return {
         status: "loading-error",
         error: result
       }
     }
-    const { data } = result;
+
+    if (result.type === "fail") {
+      switch (result.case) {
+        case "not-found": {
+          return {
+            status: "aborted",
+            error: undefined,
+            onClose: async () => {
+              updateSettings("currentWithdrawalOperationId", undefined)
+              onClose()
+            },
+          }
+        }
+        default: assertUnreachable(result.case)
+      }
+    }
+
+    const { body: data } = result;
     if (data.aborted) {
       return {
         status: "aborted",
@@ -247,8 +283,6 @@ export function useComponentState({ currency, onClose }: 
Props): utils.Recursive
       }
     }
 
-
-    // goToConfirmOperation(withdrawalOperationId)
     return {
       status: "need-confirmation",
       error: undefined,
diff --git a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx 
b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
index 52dbd4ff6..7861bb0b3 100644
--- a/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
+++ b/packages/demobank-ui/src/pages/PaytoWireTransferForm.tsx
@@ -16,9 +16,11 @@
 
 import {
   AmountJson,
+  AmountString,
   Amounts,
   HttpStatusCode,
   Logger,
+  TalerError,
   TranslatedString,
   buildPayto,
   parsePaytoUri,
@@ -30,17 +32,18 @@ import {
   notifyError,
   useTranslationContext,
 } from "@gnu-taler/web-util/browser";
-import { h, VNode, Fragment, Ref } from "preact";
+import { Fragment, Ref, VNode, h } from "preact";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { useAccessAPI } from "../hooks/access.js";
 import {
   buildRequestErrorMessage,
   undefinedIfEmpty,
   validateIBAN,
 } from "../utils.js";
-import { useConfigState } from "../hooks/config.js";
-import { useConfigContext } from "../context/config.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { useBackendState } from "../hooks/backend.js";
+import { assertUnreachable } from "./HomePage.js";
+import { mutate } from "swr";
 
 const logger = new Logger("PaytoWireTransferForm");
 
@@ -59,6 +62,8 @@ export function PaytoWireTransferForm({
 }): VNode {
   const [isRawPayto, setIsRawPayto] = useState(false);
   // FIXME: remove this
+  const { state: credentials } = useBackendState()
+  const { api } = useBankCoreApiContext();
   const [iban, setIban] = useState<string | undefined>();
   const [subject, setSubject] = useState<string | undefined>();
   const [amount, setAmount] = useState<string | undefined>();
@@ -95,7 +100,7 @@ export function PaytoWireTransferForm({
             : undefined,
   });
 
-  const { createTransaction } = useAccessAPI();
+  // const { createTransaction } = useAccessAPI();
 
   const parsed = !rawPaytoInput ? undefined : parsePaytoUri(rawPaytoInput);
 
@@ -119,36 +124,54 @@ export function PaytoWireTransferForm({
 
   async function doSend() {
     let payto_uri: string | undefined;
-
+    let sendingAmount: AmountString | undefined;
     if (rawPaytoInput) {
-      payto_uri = rawPaytoInput
+      const p = parsePaytoUri(rawPaytoInput)
+      if (!p) return;
+      sendingAmount = p.params.amount
+      delete p.params.amount
+      //it should have message
+      payto_uri = stringifyPaytoUri(p)
     } else {
       if (!iban || !subject) return;
       const ibanPayto = buildPayto("iban", iban, undefined);
       ibanPayto.params.message = encodeURIComponent(subject);
       payto_uri = stringifyPaytoUri(ibanPayto);
+      sendingAmount = `${limit.currency}:${trimmedAmountStr}`
     }
 
     try {
-      await createTransaction({
+      if (credentials.status !== "loggedIn") return;
+      const res = await api.createTransaction(credentials, {
         payto_uri,
-        amount: `${limit.currency}:${amount}`,
+        amount: sendingAmount,
       });
+      mutate(() => true)
+      if (res.type === "fail") {
+        switch (res.case) {
+          case "invalid-input": return notify({
+            type: "error",
+            title: i18n.str`The request was invalid or the payto://-URI used 
unacceptable features.`,
+            description: res.detail.hint as TranslatedString,
+            debug: res.detail,
+          })
+          case "unauthorized": return notify({
+            type: "error",
+            title: i18n.str`Not enough permission to complete the operation.`,
+            description: res.detail.hint as TranslatedString,
+            debug: res.detail,
+          })
+          default: assertUnreachable(res)
+        }
+      }
       onSuccess();
       setAmount(undefined);
       setIban(undefined);
       setSubject(undefined);
       rawPaytoInputSetter(undefined)
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.BadRequest
-                ? i18n.str`The request was invalid or the payto://-URI used 
unacceptable features.`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -179,11 +202,13 @@ export function PaytoWireTransferForm({
                 if (amount) {
                   setAmount(Amounts.stringifyValue(amount))
                 }
-                const subject = parsed.params["subject"]
+                const subject = parsed.params["message"]
                 if (subject) {
                   setSubject(subject)
                 }
               }
+              
//payto://iban/DE9714548806481?amount=LOCAL%3A2&message=011Y8V8KDCPFDEKPDZTHS7KZ14GHX7BVWKRDDPZ1N75TJ90166T0
+              
//payto://iban/DE9714548806481?receiver-name=Exchanger&amount=LOCAL%3A2&message=011Y8V8KDCPFDEKPDZTHS7KZ14GHX7BVWKRDDPZ1N75TJ90166T0
               setIsRawPayto(false)
             }} />
             <span class="flex flex-1">
@@ -298,7 +323,7 @@ export function PaytoWireTransferForm({
               />
               <ShowInputErrorLabel
                 message={errorsWire?.amount}
-                isDirty={subject !== undefined}
+                isDirty={trimmedAmountStr !== undefined}
               />
               <p class="mt-2 text-sm text-gray-500" >amount to transfer</p>
             </div>
@@ -394,7 +419,7 @@ export function InputAmount(
   },
   ref: Ref<HTMLInputElement>,
 ): VNode {
-  const cfg = useConfigContext()
+  const { config } = useBankCoreApiContext()
   return (
     <div class="mt-2">
       <div class="flex rounded-md shadow-sm border-0 ring-1 ring-inset 
ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-indigo-600">
@@ -418,8 +443,8 @@ export function InputAmount(
             if (!onChange) return;
             const l = e.currentTarget.value.length
             const sep_pos = e.currentTarget.value.indexOf(FRAC_SEPARATOR)
-            if (sep_pos !== -1 && l - sep_pos - 1 > 
cfg.currency_fraction_limit) {
-              e.currentTarget.value = e.currentTarget.value.substring(0, 
sep_pos + cfg.currency_fraction_limit + 1)
+            if (sep_pos !== -1 && l - sep_pos - 1 > 
config.currency.num_fractional_input_digits) {
+              e.currentTarget.value = e.currentTarget.value.substring(0, 
sep_pos + config.currency.num_fractional_input_digits + 1)
             }
             onChange(e.currentTarget.value);
           }}
@@ -431,11 +456,11 @@ export function InputAmount(
 }
 
 export function RenderAmount({ value, negative }: { value: AmountJson, 
negative?: boolean }): VNode {
-  const cfg = useConfigContext()
+  const { config } = useBankCoreApiContext()
   const str = Amounts.stringifyValue(value)
   const sep_pos = str.indexOf(FRAC_SEPARATOR)
-  if (sep_pos !== -1 && str.length - sep_pos - 1 > 
cfg.currency_fraction_digits) {
-    const limit = sep_pos + cfg.currency_fraction_digits + 1
+  if (sep_pos !== -1 && str.length - sep_pos - 1 > 
config.currency.num_fractional_normal_digits) {
+    const limit = sep_pos + config.currency.num_fractional_normal_digits + 1
     const normal = str.substring(0, limit)
     const small = str.substring(limit)
     return <span class="whitespace-nowrap">
diff --git a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx 
b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
index 680368919..d33353180 100644
--- a/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
+++ b/packages/demobank-ui/src/pages/PublicHistoriesPage.tsx
@@ -14,35 +14,36 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { Logger } from "@gnu-taler/taler-util";
+import { Logger, TalerError } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
+import { Loading } from "../components/Loading.js";
 import { Transactions } from "../components/Transactions/index.js";
 import { usePublicAccounts } from "../hooks/access.js";
-import { handleNotOkResult } from "./HomePage.js";
-import { Loading } from "../components/Loading.js";
 
 const logger = new Logger("PublicHistoriesPage");
 
-interface Props {}
+interface Props { }
 
 /**
  * Show histories of public accounts.
  */
-export function PublicHistoriesPage({}: Props): VNode {
+export function PublicHistoriesPage({ }: Props): VNode {
   const { i18n } = useTranslationContext();
 
   const result = usePublicAccounts();
+  const firstAccount = result && !(result instanceof TalerError) && 
result.data.public_accounts.length > 0
+    ? result.data.public_accounts[0].account_name
+    : undefined;
 
-  const [showAccount, setShowAccount] = useState(
-    result.ok && result.data.public_accounts.length > 0
-      ? result.data.public_accounts[0].account_name
-      : undefined,
-  );
+  const [showAccount, setShowAccount] = useState(firstAccount);
 
-  if (!result.ok) {
-    return handleNotOkResult(i18n)(result);
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <Loading />
   }
 
   const { data } = result;
diff --git a/packages/demobank-ui/src/pages/QrCodeSection.tsx 
b/packages/demobank-ui/src/pages/QrCodeSection.tsx
index e07525ab4..109993aae 100644
--- a/packages/demobank-ui/src/pages/QrCodeSection.tsx
+++ b/packages/demobank-ui/src/pages/QrCodeSection.tsx
@@ -17,6 +17,7 @@
 import {
   HttpStatusCode,
   stringifyWithdrawUri,
+  TalerError,
   TranslatedString,
   WithdrawUriResult,
 } from "@gnu-taler/taler-util";
@@ -29,8 +30,9 @@ import {
 import { Fragment, h, VNode } from "preact";
 import { useEffect } from "preact/hooks";
 import { QR } from "../components/QR.js";
-import { useAccessAnonAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage } from "../utils.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { assertUnreachable } from "./HomePage.js";
 
 export function QrCodeSection({
   withdrawUri,
@@ -50,22 +52,30 @@ export function QrCodeSection({
   }, []);
   const talerWithdrawUri = stringifyWithdrawUri(withdrawUri);
 
-  const { abortWithdrawal } = useAccessAnonAPI();
+  const { api } = useBankCoreApiContext()
 
   async function doAbort() {
     try {
-      await abortWithdrawal(withdrawUri.withdrawalOperationId);
-      onAborted();
+      const result = await 
api.abortWithdrawalById(withdrawUri.withdrawalOperationId);
+      if (result.type === "ok") {
+        onAborted();
+      } else {
+        switch (result.case) {
+          case "previously-confirmed": {
+            notify({
+              type: "info",
+              title: i18n.str`The reserve operation has been confirmed 
previously and can't be aborted`
+            })
+            break;
+          }
+          default: {
+            assertUnreachable(result.case)
+          }
+        }
+      }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`The reserve operation has been confirmed previously 
and can't be aborted`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -120,13 +130,13 @@ export function QrCodeSection({
           </div>
         </div>
         <div class="flex items-center justify-center gap-x-6 border-t 
border-gray-900/10 px-4 py-4 sm:px-8">
-        <button type="button"
-              // class="disabled:opacity-50 disabled:cursor-default 
cursor-pointer rounded-md  px-3 py-2 text-sm font-semibold text-black shadow-sm 
hover:bg-red-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-red-600"
-              class="text-sm font-semibold leading-6 text-gray-900"
-              onClick={doAbort}
-            >
-              Cancel
-            </button>
+          <button type="button"
+            // class="disabled:opacity-50 disabled:cursor-default 
cursor-pointer rounded-md  px-3 py-2 text-sm font-semibold text-black shadow-sm 
hover:bg-red-500 focus-visible:outline focus-visible:outline-2 
focus-visible:outline-offset-2 focus-visible:outline-red-600"
+            class="text-sm font-semibold leading-6 text-gray-900"
+            onClick={doAbort}
+          >
+            Cancel
+          </button>
         </div>
       </div>
 
diff --git a/packages/demobank-ui/src/pages/RegistrationPage.tsx 
b/packages/demobank-ui/src/pages/RegistrationPage.tsx
index 9ac93bb34..fda2d904d 100644
--- a/packages/demobank-ui/src/pages/RegistrationPage.tsx
+++ b/packages/demobank-ui/src/pages/RegistrationPage.tsx
@@ -13,7 +13,7 @@
  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, Logger, TranslatedString } from 
"@gnu-taler/taler-util";
+import { AccessToken, HttpStatusCode, Logger, TalerError, TranslatedString } 
from "@gnu-taler/taler-util";
 import {
   RequestError,
   notify,
@@ -23,12 +23,11 @@ import {
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { useBackendContext } from "../context/backend.js";
-import { useTestingAPI } from "../hooks/access.js";
 import { bankUiSettings } from "../settings.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
 import { getRandomPassword, getRandomUsername } from "./rnd.js";
-import { useCredentialsChecker } from "../hooks/useCredentialsChecker.js";
+import { useBankCoreApiContext } from "../context/config.js";
 
 const logger = new Logger("RegistrationPage");
 
@@ -63,9 +62,9 @@ function RegistrationForm({ onComplete, onCancel }: { 
onComplete: () => void, on
   const [phone, setPhone] = useState<string | undefined>();
   const [email, setEmail] = useState<string | undefined>();
   const [repeatPassword, setRepeatPassword] = useState<string | undefined>();
-  const { requestNewLoginToken } = useCredentialsChecker()
 
-  const { register } = useTestingAPI();
+  const { api } = useBankCoreApiContext()
+  // const { register } = useTestingAPI();
   const { i18n } = useTranslationContext();
 
   const errors = undefinedIfEmpty({
@@ -95,26 +94,77 @@ function RegistrationForm({ onComplete, onCancel }: { 
onComplete: () => void, on
         : undefined,
   });
 
+  async function doRegistrationAndLogin(name: string | undefined, username: 
string, password: string) {
+    const creationResponse = await api.createAccount("" as AccessToken, { 
name: name ?? "", username, password });
+    if (creationResponse.type === "fail") {
+      switch (creationResponse.case) {
+        case "invalid-input": return notify({
+          type: "error",
+          title: i18n.str`Some of the input fields are invalid.`,
+          description: creationResponse.detail.hint as TranslatedString,
+          debug: creationResponse.detail,
+        })
+        case "unable-to-create": return notify({
+          type: "error",
+          title: i18n.str`Unable to create that account.`,
+          description: creationResponse.detail.hint as TranslatedString,
+          debug: creationResponse.detail,
+        })
+        case "unauthorized": return notify({
+          type: "error",
+          title: i18n.str`No enough permission to create that account.`,
+          description: creationResponse.detail.hint as TranslatedString,
+          debug: creationResponse.detail,
+        })
+        case "already-exist": return notify({
+          type: "error",
+          title: i18n.str`That username is already taken`,
+          description: creationResponse.detail.hint as TranslatedString,
+          debug: creationResponse.detail,
+        })
+        default: assertUnreachable(creationResponse)
+      }
+    }
+    const resp = await 
api.getAuthenticationAPI(username).createAccessToken(password, {
+      // scope: "readwrite" as "write", //FIX: different than merchant
+      scope: "readwrite",
+      duration: {
+        d_us: "forever" //FIX: should return shortest
+        // d_us: 60 * 60 * 24 * 7 * 1000 * 1000
+      },
+      refreshable: true,
+    })
+
+    if (resp.type === "ok") {
+      backend.logIn({ username, token: resp.body.access_token });
+    } else {
+      switch (resp.case) {
+        case "wrong-credentials": return notify({
+          type: "error",
+          title: i18n.str`Wrong credentials for "${username}"`,
+          description: resp.detail.hint as TranslatedString,
+          debug: resp.detail,
+        })
+        case "not-found": return notify({
+          type: "error",
+          title: i18n.str`Account not found`,
+          description: resp.detail.hint as TranslatedString,
+          debug: resp.detail,
+        })
+        default: assertUnreachable(resp)
+      }
+    }
+  }
+
   async function doRegistrationStep() {
     if (!username || !password) return;
     try {
-      await register({ name: name ?? "", username, password });
-      const resp = await requestNewLoginToken(username, password)
+      await doRegistrationAndLogin(name, username, password)
       setUsername(undefined);
-      if (resp.valid) {
-        backend.logIn({ username, token: resp.token });
-      }
       onComplete();
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`That username is already taken`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -143,27 +193,11 @@ function RegistrationForm({ onComplete, onCancel }: { 
onComplete: () => void, on
       setPassword(undefined);
       setRepeatPassword(undefined);
       const username = `_${user.first}-${user.second}_`
-      await register({ username, name: `${user.first} ${user.second}`, 
password: pass });
-      const resp = await requestNewLoginToken(username, pass)
-      if (resp.valid) {
-        backend.logIn({ username, token: resp.token });
-      }
+      await doRegistrationAndLogin(name, username, pass)
       onComplete();
     } catch (error) {
-      if (error instanceof RequestError) {
-        if (tries > 0) {
-          await delay(200)
-          await doRandomRegistration(tries - 1)
-        } else {
-          notify(
-            buildRequestErrorMessage(i18n, error.cause, {
-              onClientError: (status) =>
-                status === HttpStatusCode.Conflict
-                  ? i18n.str`Could not create a random user`
-                  : undefined,
-            }),
-          );
-        }
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
diff --git a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx 
b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
index 6acf0361e..3534f9733 100644
--- a/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
+++ b/packages/demobank-ui/src/pages/ShowAccountDetails.tsx
@@ -1,67 +1,88 @@
-import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
+import { HttpStatusCode, TalerCorebankApi, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
+import { HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
 import { VNode, h } from "preact";
-import { useAdminAccountAPI, useBusinessAccountDetails } from 
"../hooks/circuit.js";
 import { useState } from "preact/hooks";
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { buildRequestErrorMessage } from "../utils.js";
+import { ErrorLoading } from "../components/ErrorLoading.js";
+import { Loading } from "../components/Loading.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { useAccountDetails } from "../hooks/access.js";
+import { useBackendState } from "../hooks/backend.js";
+import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { assertUnreachable } from "./HomePage.js";
+import { LoginForm } from "./LoginForm.js";
 import { AccountForm } from "./admin/AccountForm.js";
 
 export function ShowAccountDetails({
   account,
   onClear,
   onUpdateSuccess,
-  onLoadNotOk,
   onChangePassword,
 }: {
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
   onClear?: () => void;
   onChangePassword: () => void;
   onUpdateSuccess: () => void;
   account: string;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const result = useBusinessAccountDetails(account);
-  const { updateAccount } = useAdminAccountAPI();
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+  const { api } = useBankCoreApiContext()
+
   const [update, setUpdate] = useState(false);
-  const [submitAccount, setSubmitAccount] = useState<
-    SandboxBackend.Circuit.CircuitAccountData | undefined
-  >();
+  const [submitAccount, setSubmitAccount] = 
useState<TalerCorebankApi.AccountData | undefined>();
 
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return onLoadNotOk(result);
-    }
-    if (result.status === HttpStatusCode.NotFound) {
-      return <div>account not found</div>;
+  const result = useAccountDetails(account);
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "not-found": return <LoginForm reason="not-found" />
+      case "unauthorized": return <LoginForm reason="forbidden" />
+      default: assertUnreachable(result)
     }
-    return onLoadNotOk(result);
   }
 
   async function doUpdate() {
     if (!update) {
       setUpdate(true);
     } else {
-      if (!submitAccount) return;
+      if (!submitAccount || !creds) return;
       try {
-        await updateAccount(account, {
-          cashout_address: submitAccount.cashout_address,
-          contact_data: submitAccount.contact_data,
+        const resp = await api.updateAccount(creds, {
+          cashout_address: submitAccount.cashout_payto_uri,
+          challenge_contact_data: undefinedIfEmpty({
+            email: submitAccount.contact_data?.email,
+            phone: submitAccount.contact_data?.phone,
+          }),
+          is_exchange: false,
+          name: submitAccount.name,
         });
-        onUpdateSuccess();
+        if (resp.type === "ok") {
+          onUpdateSuccess();
+        } else {
+          switch (resp.case) {
+            case "unauthorized": return notify({
+              type: "error",
+              title: i18n.str`The rights to change the account are not 
sufficient`,
+              description: resp.detail.hint as TranslatedString,
+              debug: resp.detail,
+            })
+            case "not-found": return notify({
+              type: "error",
+              title: i18n.str`The username was not found`,
+              description: resp.detail.hint as TranslatedString,
+              debug: resp.detail,
+            })
+            default: assertUnreachable(resp)
+          }
+        }
       } catch (error) {
-        if (error instanceof RequestError) {
-          notify(
-            buildRequestErrorMessage(i18n, error.cause, {
-              onClientError: (status) =>
-                status === HttpStatusCode.Forbidden
-                  ? i18n.str`The rights to change the account are not 
sufficient`
-                  : status === HttpStatusCode.NotFound
-                    ? i18n.str`The username was not found`
-                    : undefined,
-            }),
-          );
+        if (error instanceof TalerError) {
+          notify(buildRequestErrorMessage(i18n, error))
         } else {
           notifyError(
             i18n.str`Operation failed, please report`,
@@ -86,24 +107,24 @@ export function ShowAccountDetails({
             }
           </h2>
           <div class="mt-4">
-        <div class="flex items-center justify-between">
-          <span class="flex flex-grow flex-col">
-            <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
-              <i18n.Translate>change the account details</i18n.Translate>
-            </span>
-          </span>
-          <button type="button" data-enabled={!update} class="bg-indigo-600 
data-[enabled=true]:bg-gray-200 relative inline-flex h-5 w-10 flex-shrink-0 
cursor-pointer rounded-full ring-2 border-gray-600 transition-colors 
duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 
focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
-            onClick={() => {
-              setUpdate(!update)
-            }}>
-            <span aria-hidden="true" data-enabled={!update} 
class="translate-x-5 data-[enabled=true]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
-          </button>
-        </div>
-      </div>
+            <div class="flex items-center justify-between">
+              <span class="flex flex-grow flex-col">
+                <span class="text-sm text-black font-medium leading-6 " 
id="availability-label">
+                  <i18n.Translate>change the account details</i18n.Translate>
+                </span>
+              </span>
+              <button type="button" data-enabled={!update} 
class="bg-indigo-600 data-[enabled=true]:bg-gray-200 relative inline-flex h-5 
w-10 flex-shrink-0 cursor-pointer rounded-full ring-2 border-gray-600 
transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 
focus:ring-indigo-600 focus:ring-offset-2" role="switch" aria-checked="false" 
aria-labelledby="availability-label" aria-describedby="availability-description"
+                onClick={() => {
+                  setUpdate(!update)
+                }}>
+                <span aria-hidden="true" data-enabled={!update} 
class="translate-x-5 data-[enabled=true]:translate-x-0 pointer-events-none 
inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition 
duration-200 ease-in-out"></span>
+              </button>
+            </div>
+          </div>
 
         </div>
         <AccountForm
-          template={result.data}
+          template={result.body}
           purpose={update ? "update" : "show"}
           onChange={(a) => setSubmitAccount(a)}
         >
diff --git a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx 
b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
index 46f4fe0ef..ac6e9fa9b 100644
--- a/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
+++ b/packages/demobank-ui/src/pages/UpdateAccountPassword.tsx
@@ -1,43 +1,33 @@
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
-import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
+import { TalerError, TranslatedString } from "@gnu-taler/taler-util";
+import { HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
-import { useEffect, useRef, useState } from "preact/hooks";
+import { useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { useAdminAccountAPI, useBusinessAccountDetails } from 
"../hooks/circuit.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { doAutoFocus } from "./PaytoWireTransferForm.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { assertUnreachable } from "./HomePage.js";
+import { useBackendState } from "../hooks/backend.js";
 
 export function UpdateAccountPassword({
   account,
   onCancel,
   onUpdateSuccess,
-  onLoadNotOk,
   focus,
 }: {
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
   onCancel: () => void;
   focus?: boolean,
   onUpdateSuccess: () => void;
   account: string;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const result = useBusinessAccountDetails(account);
-  const { changePassword } = useAdminAccountAPI();
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+  const { api } = useBankCoreApiContext();
+
   const [password, setPassword] = useState<string | undefined>();
   const [repeat, setRepeat] = useState<string | undefined>();
 
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return onLoadNotOk(result);
-    }
-    if (result.status === HttpStatusCode.NotFound) {
-      return <div>account not found</div>;
-    }
-    return onLoadNotOk(result);
-  }
-
   const errors = undefinedIfEmpty({
     password: !password ? i18n.str`required` : undefined,
     repeat: !repeat
@@ -48,15 +38,35 @@ export function UpdateAccountPassword({
   });
 
   async function doChangePassword() {
-    if (!!errors || !password) return;
+    if (!!errors || !password || !creds) return;
     try {
-      const r = await changePassword(account, {
+      const resp = await api.updatePassword(creds, {
         new_password: password,
       });
-      onUpdateSuccess();
+      if (resp.type === "ok") {
+        onUpdateSuccess();
+      } else {
+        switch (resp.case) {
+          case "unauthorized": {
+            notify({
+              type: "error",
+              title: i18n.str`Not authorized to change the password, maybe the 
session is invalid.`
+            })
+            break;
+          }
+          case "not-found": {
+            notify({
+              type: "error",
+              title: i18n.str`Account not found`
+            })
+            break;
+          }
+          default: assertUnreachable(resp)
+        }
+      }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(buildRequestErrorMessage(i18n, error.cause));
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(i18n.str`Operation failed, please report`, (error 
instanceof Error
           ? error.message
diff --git a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx 
b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
index da299b1c8..2d80bad1f 100644
--- a/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
+++ b/packages/demobank-ui/src/pages/WalletWithdrawForm.tsx
@@ -19,9 +19,9 @@ import {
   Amounts,
   HttpStatusCode,
   Logger,
+  TalerError,
   TranslatedString,
-  WithdrawUriResult,
-  parseWithdrawUri,
+  parseWithdrawUri
 } from "@gnu-taler/taler-util";
 import {
   RequestError,
@@ -31,13 +31,15 @@ import {
 } from "@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { forwardRef } from "preact/compat";
-import { useEffect, useRef, useState } from "preact/hooks";
-import { useAccessAPI } from "../hooks/access.js";
-import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
-import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
+import { useState } from "preact/hooks";
+import { Attention } from "../components/Attention.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { useBackendState } from "../hooks/backend.js";
 import { useSettings } from "../hooks/settings.js";
+import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
+import { assertUnreachable } from "./HomePage.js";
 import { OperationState } from "./OperationState/index.js";
-import { Attention } from "../components/Attention.js";
+import { InputAmount, doAutoFocus } from "./PaytoWireTransferForm.js";
 
 const logger = new Logger("WalletWithdrawForm");
 const RefAmount = forwardRef(InputAmount);
@@ -52,7 +54,10 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, 
onCancel, focus }: {
   const { i18n } = useTranslationContext();
   const [settings, updateSettings] = useSettings()
 
-  const { createWithdrawal } = useAccessAPI();
+  const { state: credentials } = useBackendState();
+  const creds = credentials.status !== "loggedIn" ? undefined : credentials
+
+  const { api } = useBankCoreApiContext()
   const [amountStr, setAmountStr] = useState<string | 
undefined>(`${settings.maxWithdrawalAmount}`);
 
   if (!!settings.currentWithdrawalOperationId) {
@@ -81,30 +86,33 @@ function OldWithdrawalForm({ goToConfirmOperation, limit, 
onCancel, focus }: {
   });
 
   async function doStart() {
-    if (!parsedAmount) return;
+    if (!parsedAmount || !creds) return;
     try {
-      const result = await createWithdrawal({
+      const result = await api.createWithdrawal(creds, {
         amount: Amounts.stringify(parsedAmount),
       });
-      const uri = parseWithdrawUri(result.data.taler_withdraw_uri);
-      if (!uri) {
-        return notifyError(
-          i18n.str`Server responded with an invalid  withdraw URI`,
-          i18n.str`Withdraw URI: ${result.data.taler_withdraw_uri}`);
+      if (result.type === "ok") {
+        const uri = parseWithdrawUri(result.body.taler_withdraw_uri);
+        if (!uri) {
+          return notifyError(
+            i18n.str`Server responded with an invalid  withdraw URI`,
+            i18n.str`Withdraw URI: ${result.body.taler_withdraw_uri}`);
+        } else {
+          updateSettings("currentWithdrawalOperationId", 
uri.withdrawalOperationId)
+          goToConfirmOperation(uri.withdrawalOperationId);
+        }
       } else {
-        updateSettings("currentWithdrawalOperationId", 
uri.withdrawalOperationId)
-        goToConfirmOperation(uri.withdrawalOperationId);
+        switch (result.case) {
+          case "insufficient-funds": {
+            notify({ type: "error", title: i18n.str`The operation was rejected 
due to insufficient funds` })
+            break;
+          }
+          default: assertUnreachable(result.case)
+        }
       }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Forbidden
-                ? i18n.str`The operation was rejected due to insufficient 
funds`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
diff --git a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx 
b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
index ddcd2492d..602ec9bd8 100644
--- a/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalConfirmationQuestion.tsx
@@ -22,6 +22,7 @@ import {
   PaytoUri,
   PaytoUriIBAN,
   PaytoUriTalerBank,
+  TalerError,
   TranslatedString,
   WithdrawUriResult
 } from "@gnu-taler/taler-util";
@@ -35,10 +36,12 @@ import {
 import { Fragment, VNode, h } from "preact";
 import { useMemo, useState } from "preact/hooks";
 import { ShowInputErrorLabel } from "../components/ShowInputErrorLabel.js";
-import { useAccessAnonAPI } from "../hooks/access.js";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../utils.js";
 import { useSettings } from "../hooks/settings.js";
 import { RenderAmount } from "./PaytoWireTransferForm.js";
+import { useBankCoreApiContext } from "../context/config.js";
+import { assertUnreachable } from "./HomePage.js";
+import { mutate } from "swr";
 
 const logger = new Logger("WithdrawalConfirmationQuestion");
 
@@ -70,7 +73,7 @@ export function WithdrawalConfirmationQuestion({
     };
   }, []);
 
-  const { confirmWithdrawal, abortWithdrawal } = useAccessAnonAPI();
+  const { api } = useBankCoreApiContext()
   const [captchaAnswer, setCaptchaAnswer] = useState<string | undefined>();
   const answer = parseInt(captchaAnswer ?? "", 10);
   const [busy, setBusy] = useState<Record<string, undefined>>()
@@ -87,24 +90,32 @@ export function WithdrawalConfirmationQuestion({
   async function doTransfer() {
     try {
       setBusy({})
-      await confirmWithdrawal(
-        withdrawUri.withdrawalOperationId,
-      );
-      if (!settings.showWithdrawalSuccess) {
-        notifyInfo(i18n.str`Wire transfer completed!`)
+      const resp = await 
api.confirmWithdrawalById(withdrawUri.withdrawalOperationId);
+      if (resp.type === "ok") {
+        mutate(() => true)// clean any info that we have
+        if (!settings.showWithdrawalSuccess) {
+          notifyInfo(i18n.str`Wire transfer completed!`)
+        }
+      } else {
+        switch (resp.case) {
+          case "previously-aborted": return notify({
+            type: "error",
+            title: i18n.str`The withdrawal has been aborted previously and 
can't be confirmed`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          case "no-exchange-or-reserve-selected": return notify({
+            type: "error",
+            title: i18n.str`The withdraw operation cannot be confirmed because 
no exchange and reserve public key selection happened before`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          });
+          default: assertUnreachable(resp)
+        }
       }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`The withdrawal has been aborted previously and 
can't be confirmed`
-                : status === HttpStatusCode.UnprocessableEntity
-                  ? i18n.str`The withdraw operation cannot be confirmed 
because no exchange and reserve public key selection happened before`
-                  : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
@@ -120,18 +131,26 @@ export function WithdrawalConfirmationQuestion({
   async function doCancel() {
     try {
       setBusy({})
-      await abortWithdrawal(withdrawUri.withdrawalOperationId);
-      onAborted();
+      const resp = await 
api.abortWithdrawalById(withdrawUri.withdrawalOperationId);
+      if (resp.type === "ok") {
+        onAborted();
+      } else {
+        switch (resp.case) {
+          case "previously-confirmed": {
+            notify({
+              type: "error",
+              title: i18n.str`The reserve operation has been confirmed 
previously and can't be aborted`
+            });
+            break;
+          }
+          default: {
+            assertUnreachable(resp.case)
+          }
+        }
+      }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Conflict
-                ? i18n.str`The reserve operation has been confirmed previously 
and can't be aborted`
-                : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
diff --git a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx 
b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
index 35fb94a6c..15910201e 100644
--- a/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
+++ b/packages/demobank-ui/src/pages/WithdrawalQRCode.tsx
@@ -16,17 +16,17 @@
 
 import {
   Amounts,
-  HttpStatusCode,
   Logger,
+  TalerError,
   WithdrawUriResult,
   parsePaytoUri
 } from "@gnu-taler/taler-util";
-import { ErrorType, notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
+import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
+import { ErrorLoading } from "../components/ErrorLoading.js";
 import { Loading } from "../components/Loading.js";
 import { useWithdrawalDetails } from "../hooks/access.js";
-import { useSettings } from "../hooks/settings.js";
-import { handleNotOkResult } from "./HomePage.js";
+import { assertUnreachable } from "./HomePage.js";
 import { QrCodeSection } from "./QrCodeSection.js";
 import { WithdrawalConfirmationQuestion } from 
"./WithdrawalConfirmationQuestion.js";
 
@@ -48,48 +48,20 @@ export function WithdrawalQRCode({
   const { i18n } = useTranslationContext();
   const result = useWithdrawalDetails(withdrawUri.withdrawalOperationId);
 
-  if (!result.ok) {
-    if (result.loading) {
-      return <Loading />;
-    }
-    if (result.type === ErrorType.CLIENT && result.status === 
HttpStatusCode.NotFound) {
-      return <div class="relative ml-auto mr-auto transform overflow-hidden 
rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 
sm:w-full sm:max-w-sm sm:p-6">
-        <div>
-          <div class="mx-auto flex h-12 w-12 items-center justify-center 
rounded-full bg-red-100 ">
-            <svg class="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" aria-hidden="true">
-              <path stroke-linecap="round" stroke-linejoin="round" d="M12 
9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 
1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 
15.75h.007v.008H12v-.008z" />
-            </svg>
-          </div>
-
-          <div class="mt-3 text-center sm:mt-5">
-            <h3 class="text-base font-semibold leading-6 text-gray-900" 
id="modal-title">
-              <i18n.Translate>Operation not found</i18n.Translate>
-            </h3>
-            <div class="mt-2">
-              <p class="text-sm text-gray-500">
-                <i18n.Translate>
-                  This operation is not known by the server. The operation id 
is wrong or the
-                  server deleted the operation information before reaching 
here.
-                </i18n.Translate>
-              </p>
-            </div>
-          </div>
-        </div>
-        <div class="mt-5 sm:mt-6">
-          <button type="button"
-            class="inline-flex w-full justify-center rounded-md bg-indigo-600 
px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
-            onClick={async (e) => {
-              e.preventDefault();
-              onClose()
-            }}>
-            <i18n.Translate>Cotinue to dashboard</i18n.Translate>
-          </button>
-        </div>
-      </div>
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "not-found": return <OperationNotFound onClose={onClose} />
+      default: assertUnreachable(result.case)
     }
-    return handleNotOkResult(i18n)(result);
   }
-  const { data } = result;
+
+  const { body: data } = result;
 
   if (data.aborted) {
     return <section id="main" class="content">
@@ -194,3 +166,41 @@ export function WithdrawalQRCode({
     />
   );
 }
+
+
+function OperationNotFound({ onClose }: { onClose: () => void }): VNode {
+  const { i18n } = useTranslationContext();
+  return <div class="relative ml-auto mr-auto transform overflow-hidden 
rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 
sm:w-full sm:max-w-sm sm:p-6">
+    <div>
+      <div class="mx-auto flex h-12 w-12 items-center justify-center 
rounded-full bg-red-100 ">
+        <svg class="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" 
stroke-width="1.5" stroke="currentColor" aria-hidden="true">
+          <path stroke-linecap="round" stroke-linejoin="round" d="M12 
9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 
1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 
15.75h.007v.008H12v-.008z" />
+        </svg>
+      </div>
+
+      <div class="mt-3 text-center sm:mt-5">
+        <h3 class="text-base font-semibold leading-6 text-gray-900" 
id="modal-title">
+          <i18n.Translate>Operation not found</i18n.Translate>
+        </h3>
+        <div class="mt-2">
+          <p class="text-sm text-gray-500">
+            <i18n.Translate>
+              This operation is not known by the server. The operation id is 
wrong or the
+              server deleted the operation information before reaching here.
+            </i18n.Translate>
+          </p>
+        </div>
+      </div>
+    </div>
+    <div class="mt-5 sm:mt-6">
+      <button type="button"
+        class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 
py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 
focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 
focus-visible:outline-indigo-600"
+        onClick={async (e) => {
+          e.preventDefault();
+          onClose()
+        }}>
+        <i18n.Translate>Cotinue to dashboard</i18n.Translate>
+      </button>
+    </div>
+  </div>
+}
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/Account.tsx 
b/packages/demobank-ui/src/pages/admin/Account.tsx
index 676fc43d0..bf2fa86f0 100644
--- a/packages/demobank-ui/src/pages/admin/Account.tsx
+++ b/packages/demobank-ui/src/pages/admin/Account.tsx
@@ -1,10 +1,13 @@
-import { Amounts } from "@gnu-taler/taler-util";
-import { PaytoWireTransferForm } from "../PaytoWireTransferForm.js";
-import { handleNotOkResult } from "../HomePage.js";
-import { useAccountDetails } from "../../hooks/access.js";
-import { useBackendContext } from "../../context/backend.js";
+import { Amounts, TalerError } from "@gnu-taler/taler-util";
 import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
-import { Fragment, h, VNode } from "preact";
+import { Fragment, VNode, h } from "preact";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { Loading } from "../../components/Loading.js";
+import { useBackendContext } from "../../context/backend.js";
+import { useAccountDetails } from "../../hooks/access.js";
+import { assertUnreachable } from "../HomePage.js";
+import { LoginForm } from "../LoginForm.js";
+import { PaytoWireTransferForm } from "../PaytoWireTransferForm.js";
 
 export function AdminAccount({ onRegister }: { onRegister: () => void }): 
VNode {
   const { i18n } = useTranslationContext();
@@ -12,15 +15,25 @@ export function AdminAccount({ onRegister }: { onRegister: 
() => void }): VNode
   const account = r.state.status !== "loggedOut" ? r.state.username : "admin";
   const result = useAccountDetails(account);
 
-  if (!result.ok) {
-    return handleNotOkResult(i18n)(result);
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
   }
-  const { data } = result;
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "unauthorized": return <LoginForm reason="forbidden" 
onRegister={onRegister} />
+      case "not-found": return <LoginForm reason="not-found" 
onRegister={onRegister} />
+      default: assertUnreachable(result)
+    }
+  }
+  const { body: data } = result;
 
   const balance = Amounts.parseOrThrow(data.balance.amount);
-  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
-  
-  const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
+  const balanceIsDebit = data.balance.credit_debit_indicator == "debit";
+
+  const debitThreshold = Amounts.parseOrThrow(data.debit_threshold);
   const limit = balanceIsDebit
     ? Amounts.sub(debitThreshold, balance).amount
     : Amounts.add(balance, debitThreshold).amount;
diff --git a/packages/demobank-ui/src/pages/admin/AccountForm.tsx 
b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
index ed8bf610d..8470930bf 100644
--- a/packages/demobank-ui/src/pages/admin/AccountForm.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountForm.tsx
@@ -3,7 +3,7 @@ import { ShowInputErrorLabel } from 
"../../components/ShowInputErrorLabel.js";
 import { PartialButDefined, RecursivePartial, WithIntermediate, 
undefinedIfEmpty, validateIBAN } from "../../utils.js";
 import { useEffect, useRef, useState } from "preact/hooks";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
-import { buildPayto, parsePaytoUri } from "@gnu-taler/taler-util";
+import { TalerCorebankApi, buildPayto, parsePaytoUri } from 
"@gnu-taler/taler-util";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
 
 const IBAN_REGEX = /^[A-Z][A-Z0-9]*$/;
@@ -28,8 +28,8 @@ export function AccountForm({
 }: {
   focus?: boolean,
   children: ComponentChildren,
-  template: SandboxBackend.Circuit.CircuitAccountData | undefined;
-  onChange: (a: SandboxBackend.Circuit.CircuitAccountData | undefined) => void;
+  template: TalerCorebankApi.AccountData | undefined;
+  onChange: (a: TalerCorebankApi.AccountData | undefined) => void;
   purpose: "create" | "update" | "show";
 }): VNode {
   const initial = initializeFromTemplate(template);
@@ -41,12 +41,12 @@ export function AccountForm({
 
   function updateForm(newForm: typeof initial): void {
 
-    const parsed = !newForm.cashout_address
+    const parsed = !newForm.cashout_payto_uri
       ? undefined
-      : buildPayto("iban", newForm.cashout_address, undefined);;
+      : buildPayto("iban", newForm.cashout_payto_uri, undefined);;
 
     const errors = undefinedIfEmpty<RecursivePartial<typeof initial>>({
-      cashout_address: !newForm.cashout_address
+      cashout_payto_uri: !newForm.cashout_payto_uri
         ? i18n.str`required`
         : !parsed
           ? i18n.str`does not follow the pattern`
@@ -75,7 +75,8 @@ export function AccountForm({
       //     ? i18n.str`IBAN should have just uppercased letters and numbers`
       //     : validateIBAN(newForm.iban, i18n),
       name: !newForm.name ? i18n.str`required` : undefined,
-      username: !newForm.username ? i18n.str`required` : undefined,
+
+      // username: !newForm.username ? i18n.str`required` : undefined,
     });
     setErrors(errors);
     setForm(newForm);
@@ -94,7 +95,7 @@ export function AccountForm({
       <div class="px-4 py-6 sm:p-8">
         <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
 
-          <div class="sm:col-span-5">
+          {/* <div class="sm:col-span-5">
             <label
               class="block text-sm font-medium leading-6 text-gray-900"
               for="username"
@@ -127,7 +128,7 @@ export function AccountForm({
             <p class="mt-2 text-sm text-gray-500" >
               <i18n.Translate>account identification in the 
bank</i18n.Translate>
             </p>
-          </div>
+          </div> */}
 
           <div class="sm:col-span-5">
             <label
@@ -178,7 +179,7 @@ export function AccountForm({
                 name="internal-iban"
                 id="internal-iban"
                 disabled={true}
-                value={form.iban ?? ""}
+                value={form.payto_uri ?? ""}
               />
             </div>
             <p class="mt-2 text-sm text-gray-500" >
@@ -200,18 +201,20 @@ export function AccountForm({
                 class="block w-full disabled:bg-gray-100 rounded-md border-0 
py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 
data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 
focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                 name="email"
                 id="email"
-                data-error={!!errors?.contact_data?.email && 
form.contact_data.email !== undefined}
+                data-error={!!errors?.contact_data?.email && 
form.contact_data?.email !== undefined}
                 disabled={purpose !== "create"}
-                value={form.contact_data.email ?? ""}
+                value={form.contact_data?.email ?? ""}
                 onChange={(e) => {
-                  form.contact_data.email = e.currentTarget.value;
-                  updateForm(structuredClone(form));
+                  if (form.contact_data) {
+                    form.contact_data.email = e.currentTarget.value;
+                    updateForm(structuredClone(form));
+                  }
                 }}
                 autocomplete="off"
               />
               <ShowInputErrorLabel
                 message={errors?.contact_data?.email}
-                isDirty={form.contact_data.email !== undefined}
+                isDirty={form.contact_data?.email !== undefined}
               />
             </div>
           </div>
@@ -231,18 +234,20 @@ export function AccountForm({
                 name="phone"
                 id="phone"
                 disabled={purpose !== "create"}
-                value={form.contact_data.phone ?? ""}
-                data-error={!!errors?.contact_data?.phone && 
form.contact_data.phone !== undefined}
+                value={form.contact_data?.phone ?? ""}
+                data-error={!!errors?.contact_data?.phone && 
form.contact_data?.phone !== undefined}
                 onChange={(e) => {
-                  form.contact_data.phone = e.currentTarget.value;
-                  updateForm(structuredClone(form));
+                  if (form.contact_data) {
+                    form.contact_data.phone = e.currentTarget.value;
+                    updateForm(structuredClone(form));
+                  }
                 }}
                 // placeholder=""
                 autocomplete="off"
               />
               <ShowInputErrorLabel
                 message={errors?.contact_data?.phone}
-                isDirty={form.contact_data.phone !== undefined}
+                isDirty={form.contact_data?.phone !== undefined}
               />
             </div>
           </div>
@@ -259,21 +264,21 @@ export function AccountForm({
             <div class="mt-2">
               <input
                 type="text"
-                data-error={!!errors?.cashout_address && form.cashout_address 
!== undefined}
+                data-error={!!errors?.cashout_payto_uri && 
form.cashout_payto_uri !== undefined}
                 class="block w-full disabled:bg-gray-100 rounded-md border-0 
py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 
data-[error=true]:ring-red-500 placeholder:text-gray-400 focus:ring-2 
focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                 name="cashout"
                 id="cashout"
                 disabled={purpose === "show"}
-                value={form.cashout_address ?? ""}
+                value={form.cashout_payto_uri ?? ""}
                 onChange={(e) => {
-                  form.cashout_address = e.currentTarget.value;
+                  form.cashout_payto_uri = e.currentTarget.value;
                   updateForm(structuredClone(form));
                 }}
                 autocomplete="off"
               />
               <ShowInputErrorLabel
-                message={errors?.cashout_address}
-                isDirty={form.cashout_address !== undefined}
+                message={errors?.cashout_payto_uri}
+                isDirty={form.cashout_payto_uri !== undefined}
               />
             </div>
             <p class="mt-2 text-sm text-gray-500" >
@@ -289,26 +294,27 @@ export function AccountForm({
 }
 
 function initializeFromTemplate(
-  account: SandboxBackend.Circuit.CircuitAccountData | undefined,
-): WithIntermediate<SandboxBackend.Circuit.CircuitAccountData> {
+  account: TalerCorebankApi.AccountData | undefined,
+): WithIntermediate<TalerCorebankApi.AccountData> {
   const emptyAccount = {
-    cashout_address: undefined,
-    iban: undefined,
-    name: undefined,
-    username: undefined,
+    cashout_payto_uri: undefined,
     contact_data: undefined,
+    payto_uri: undefined,
+    balance: undefined,
+    debit_threshold: undefined,
+    name: undefined,
   };
   const emptyContact = {
     email: undefined,
     phone: undefined,
   };
 
-  const initial: PartialButDefined<SandboxBackend.Circuit.CircuitAccountData> =
+  const initial: PartialButDefined<TalerCorebankApi.AccountData> =
     structuredClone(account) ?? emptyAccount;
   if (typeof initial.contact_data === "undefined") {
     initial.contact_data = emptyContact;
   }
-  initial.contact_data.email;
+  // initial.contact_data.email;
   return initial as any;
 }
 
diff --git a/packages/demobank-ui/src/pages/admin/AccountList.tsx 
b/packages/demobank-ui/src/pages/admin/AccountList.tsx
index a6899e679..8a1e8294a 100644
--- a/packages/demobank-ui/src/pages/admin/AccountList.tsx
+++ b/packages/demobank-ui/src/pages/admin/AccountList.tsx
@@ -1,10 +1,12 @@
-import { h, VNode } from "preact";
-import { useBusinessAccounts } from "../../hooks/circuit.js";
-import { handleNotOkResult } from "../HomePage.js";
-import { AccountAction } from "./Home.js";
-import { Amounts } from "@gnu-taler/taler-util";
+import { Amounts, TalerError } from "@gnu-taler/taler-util";
 import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { VNode, h } from "preact";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { Loading } from "../../components/Loading.js";
+import { useBusinessAccounts } from "../../hooks/circuit.js";
+import { assertUnreachable } from "../HomePage.js";
 import { RenderAmount } from "../PaytoWireTransferForm.js";
+import { AccountAction } from "./Home.js";
 
 interface Props {
   onAction: (type: AccountAction, account: string) => void;
@@ -13,15 +15,23 @@ interface Props {
 }
 
 export function AccountList({ account, onAction, onCreateAccount }: Props): 
VNode {
-  const result = useBusinessAccounts({ account });
+  const result = useBusinessAccounts();
   const { i18n } = useTranslationContext();
 
-  if (result.loading) return <div />;
-  if (!result.ok) {
-    return handleNotOkResult(i18n)(result);
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
+  }
+  if (result.data.type === "fail") {
+    switch (result.data.case) {
+      case "unauthorized": return <div>un auth</div>
+      default: assertUnreachable(result.data.case)
+    }
   }
 
-  const { customers } = result.data;
+  const { accounts } = result.data.body;
   return <div class="px-4 sm:px-6 lg:px-8">
     <div class="sm:flex sm:items-center">
       <div class="sm:flex-auto">
@@ -45,7 +55,7 @@ export function AccountList({ account, onAction, 
onCreateAccount }: Props): VNod
     <div class="mt-8 flow-root">
       <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
         <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
-          {!customers.length ? (
+          {!accounts.length ? (
             <div></div>
           ) : (
             <table class="min-w-full divide-y divide-gray-300">
@@ -60,7 +70,7 @@ export function AccountList({ account, onAction, 
onCreateAccount }: Props): VNod
                 </tr>
               </thead>
               <tbody class="divide-y divide-gray-200">
-                {customers.map((item, idx) => {
+                {accounts.map((item, idx) => {
                   const balance = !item.balance
                     ? undefined
                     : Amounts.parse(item.balance.amount);
diff --git a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx 
b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
index 2146fc6f0..f6176e772 100644
--- a/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/CreateNewAccount.tsx
@@ -1,11 +1,14 @@
+import { HttpStatusCode, TalerCorebankApi, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
 import { RequestError, notify, notifyError, useTranslationContext } from 
"@gnu-taler/web-util/browser";
-import { VNode, h, Fragment } from "preact";
-import { useAdminAccountAPI } from "../../hooks/circuit.js";
+import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { buildRequestErrorMessage } from "../../utils.js";
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
 import { getRandomPassword } from "../rnd.js";
 import { AccountForm } from "./AccountForm.js";
+import { useBackendState } from "../../hooks/backend.js";
+import { useBankCoreApiContext } from "../../context/config.js";
+import { assertUnreachable } from "../HomePage.js";
+import { mutate } from "swr";
 
 export function CreateNewAccount({
   onCancel,
@@ -15,40 +18,63 @@ export function CreateNewAccount({
   onCreateSuccess: (password: string) => void;
 }): VNode {
   const { i18n } = useTranslationContext();
-  const { createAccount } = useAdminAccountAPI();
+  // const { createAccount } = useAdminAccountAPI();
+  const { state: credentials } = useBackendState()
+  const token = credentials.status !== "loggedIn" ? undefined : 
credentials.token
+  const { api } = useBankCoreApiContext();
+
   const [submitAccount, setSubmitAccount] = useState<
-    SandboxBackend.Circuit.CircuitAccountData | undefined
+    TalerCorebankApi.AccountData | undefined
   >();
 
   async function doCreate() {
-    if (!submitAccount) return;
+    if (!submitAccount || !token) return;
     try {
-      const account: SandboxBackend.Circuit.CircuitAccountRequest =
-      {
-        cashout_address: submitAccount.cashout_address,
-        contact_data: submitAccount.contact_data,
-        internal_iban: submitAccount.iban,
+      const account: TalerCorebankApi.RegisterAccountRequest = {
+        cashout_payto_uri: submitAccount.cashout_payto_uri,
+        challenge_contact_data: submitAccount.contact_data,
+        internal_payto_uri: submitAccount.payto_uri,
         name: submitAccount.name,
-        username: submitAccount.username,
+        username: "",//FIXME: not in account data
         password: getRandomPassword(),
       };
 
-      await createAccount(account);
-      onCreateSuccess(account.password);
+      const resp = await api.createAccount(token, account);
+      if (resp.type === "ok") {
+        mutate(() => true)// clean account list
+        onCreateSuccess(account.password);
+      } else {
+        switch (resp.case) {
+          case "invalid-input": return notify({
+            type: "error",
+            title: i18n.str`Server replied that input data was invalid`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "unable-to-create": return notify({
+            type: "error",
+            title: i18n.str`The account name is registered.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "unauthorized": return notify({
+            type: "error",
+            title: i18n.str`The rights to perform the operation are not 
sufficient`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "already-exist": return notify({
+            type: "error",
+            title: i18n.str`Account name is already taken`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          default: assertUnreachable(resp)
+        }
+      }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Forbidden
-                ? i18n.str`The rights to perform the operation are not 
sufficient`
-                : status === HttpStatusCode.BadRequest
-                  ? i18n.str`Server replied that input data was invalid`
-                  : status === HttpStatusCode.Conflict
-                    ? i18n.str`At least one registration detail was not 
available`
-                    : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(
           i18n.str`Operation failed, please report`,
diff --git a/packages/demobank-ui/src/pages/admin/Home.tsx 
b/packages/demobank-ui/src/pages/admin/Home.tsx
index d50ff14b4..71ea8ce1b 100644
--- a/packages/demobank-ui/src/pages/admin/Home.tsx
+++ b/packages/demobank-ui/src/pages/admin/Home.tsx
@@ -2,15 +2,14 @@ import { notifyInfo, useTranslationContext } from 
"@gnu-taler/web-util/browser";
 import { Fragment, VNode, h } from "preact";
 import { useState } from "preact/hooks";
 import { Cashouts } from "../../components/Cashouts/index.js";
-import { ShowCashoutDetails } from "../business/Home.js";
-import { handleNotOkResult } from "../HomePage.js";
+import { Transactions } from "../../components/Transactions/index.js";
 import { ShowAccountDetails } from "../ShowAccountDetails.js";
 import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
+import { ShowCashoutDetails } from "../business/Home.js";
 import { AdminAccount } from "./Account.js";
 import { AccountList } from "./AccountList.js";
 import { CreateNewAccount } from "./CreateNewAccount.js";
 import { RemoveAccount } from "./RemoveAccount.js";
-import { Transactions } from "../../components/Transactions/index.js";
 
 /**
  * Query account information and show QR code if there is pending withdrawal
@@ -38,7 +37,6 @@ export function AdminHome({ onRegister }: Props): VNode {
     switch (action.type) {
       case "show-cashouts-details": return <ShowCashoutDetails
         id={action.account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setAction(undefined);
         }}
@@ -74,7 +72,6 @@ export function AdminHome({ onRegister }: Props): VNode {
       )
       case "update-password": return <UpdateAccountPassword
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Password changed`);
           setAction(undefined);
@@ -85,7 +82,6 @@ export function AdminHome({ onRegister }: Props): VNode {
       />
       case "remove-account": return <RemoveAccount
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Account removed`);
           setAction(undefined);
@@ -96,7 +92,6 @@ export function AdminHome({ onRegister }: Props): VNode {
       />
       case "show-details": return <ShowAccountDetails
         account={action.account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onChangePassword={() => {
           setAction({
             type: "update-password",
@@ -137,12 +132,12 @@ export function AdminHome({ onRegister }: Props): VNode {
         }}
         account={undefined}
         onAction={(type, account) => setAction({ account, type })}
-        
+
       />
 
       <AdminAccount onRegister={onRegister} />
 
-      <Transactions account="admin"/>
+      <Transactions account="admin" />
     </Fragment>
   );
 }
\ No newline at end of file
diff --git a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx 
b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
index b323b0d01..ce8a53ca1 100644
--- a/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
+++ b/packages/demobank-ui/src/pages/admin/RemoveAccount.tsx
@@ -1,24 +1,25 @@
-import { ErrorType, HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
-import { VNode, h, Fragment } from "preact";
+import { Amounts, HttpStatusCode, TalerError, TranslatedString } from 
"@gnu-taler/taler-util";
+import { HttpResponsePaginated, RequestError, notify, notifyError, 
useTranslationContext } from "@gnu-taler/web-util/browser";
+import { Fragment, VNode, h } from "preact";
+import { useState } from "preact/hooks";
+import { Attention } from "../../components/Attention.js";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { Loading } from "../../components/Loading.js";
+import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
 import { useAccountDetails } from "../../hooks/access.js";
-import { useAdminAccountAPI } from "../../hooks/circuit.js";
-import { Amounts, HttpStatusCode, TranslatedString } from 
"@gnu-taler/taler-util";
 import { buildRequestErrorMessage, undefinedIfEmpty } from "../../utils.js";
-import { useEffect, useRef, useState } from "preact/hooks";
-import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
-import { Attention } from "../../components/Attention.js";
+import { assertUnreachable } from "../HomePage.js";
+import { LoginForm } from "../LoginForm.js";
 import { doAutoFocus } from "../PaytoWireTransferForm.js";
+import { useBankCoreApiContext } from "../../context/config.js";
+import { useBackendState } from "../../hooks/backend.js";
 
 export function RemoveAccount({
   account,
   onCancel,
   onUpdateSuccess,
-  onLoadNotOk,
   focus,
 }: {
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
   focus?: boolean;
   onCancel: () => void;
   onUpdateSuccess: () => void;
@@ -27,18 +28,26 @@ export function RemoveAccount({
   const { i18n } = useTranslationContext();
   const result = useAccountDetails(account);
   const [accountName, setAccountName] = useState<string | undefined>()
-  const { deleteAccount } = useAdminAccountAPI();
 
-  if (!result.ok) {
-    if (result.loading || result.type === ErrorType.TIMEOUT) {
-      return onLoadNotOk(result);
-    }
-    if (result.status === HttpStatusCode.NotFound) {
-      return <div>account not found</div>;
+  const { state } = useBackendState();
+  const token = state.status !== "loggedIn" ? undefined : state.token
+  const { api } = useBankCoreApiContext()
+
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "unauthorized": return <LoginForm reason="forbidden" />
+      case "not-found": return <LoginForm reason="not-found" />
+      default: assertUnreachable(result)
     }
-    return onLoadNotOk(result);
   }
-  const balance = Amounts.parse(result.data.balance.amount);
+
+  const balance = Amounts.parse(result.body.balance.amount);
   if (!balance) {
     return <div>there was an error reading the balance</div>;
   }
@@ -50,23 +59,45 @@ export function RemoveAccount({
   }
 
   async function doRemove() {
+    if (!token) return;
     try {
-      const r = await deleteAccount(account);
-      onUpdateSuccess();
+      const resp = await api.deleteAccount({ username: account, token });
+      if (resp.type === "ok") {
+        onUpdateSuccess();
+      } else {
+        switch (resp.case) {
+          case "unauthorized": return notify({
+            type: "error",
+            title: i18n.str`No enough permission to delete the account.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "not-found": return notify({
+            type: "error",
+            title: i18n.str`The username was not found.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "unable-to-delete": return notify({
+            type: "error",
+            title: i18n.str`The administrator specified a institutional 
username.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          case "balance-not-zero": return notify({
+            type: "error",
+            title: i18n.str`Can't delete an account with balance different 
than zero.`,
+            description: resp.detail.hint as TranslatedString,
+            debug: resp.detail,
+          })
+          default: {
+            assertUnreachable(resp)
+          }
+        }
+      }
     } catch (error) {
-      if (error instanceof RequestError) {
-        notify(
-          buildRequestErrorMessage(i18n, error.cause, {
-            onClientError: (status) =>
-              status === HttpStatusCode.Forbidden
-                ? i18n.str`The administrator specified a institutional 
username`
-                : status === HttpStatusCode.NotFound
-                  ? i18n.str`The username was not found`
-                  : status === HttpStatusCode.PreconditionFailed
-                    ? i18n.str`Balance was not zero`
-                    : undefined,
-          }),
-        );
+      if (error instanceof TalerError) {
+        notify(buildRequestErrorMessage(i18n, error))
       } else {
         notifyError(i18n.str`Operation failed, please report`,
           (error instanceof Error
diff --git a/packages/demobank-ui/src/pages/business/Home.tsx 
b/packages/demobank-ui/src/pages/business/Home.tsx
index 1a84effcd..03d7895e3 100644
--- a/packages/demobank-ui/src/pages/business/Home.tsx
+++ b/packages/demobank-ui/src/pages/business/Home.tsx
@@ -16,26 +16,28 @@
 import {
   AmountJson,
   Amounts,
-  HttpStatusCode,
+  TalerError,
   TranslatedString
 } from "@gnu-taler/taler-util";
 import {
-  HttpResponse,
-  HttpResponsePaginated,
-  RequestError,
   notify,
   notifyError,
   notifyInfo,
-  useTranslationContext,
+  useTranslationContext
 } from "@gnu-taler/web-util/browser";
+import { format } from "date-fns";
 import { Fragment, VNode, h } from "preact";
 import { useEffect, useState } from "preact/hooks";
+import { mutate } from "swr";
 import { Cashouts } from "../../components/Cashouts/index.js";
+import { ErrorLoading } from "../../components/ErrorLoading.js";
+import { Loading } from "../../components/Loading.js";
 import { ShowInputErrorLabel } from "../../components/ShowInputErrorLabel.js";
+import { useBankCoreApiContext } from "../../context/config.js";
 import { useAccountDetails } from "../../hooks/access.js";
+import { useBackendState } from "../../hooks/backend.js";
 import {
   useCashoutDetails,
-  useCircuitAccountAPI,
   useEstimator,
   useRatiosAndFeeConfig,
 } from "../../hooks/circuit.js";
@@ -44,7 +46,7 @@ import {
   buildRequestErrorMessage,
   undefinedIfEmpty,
 } from "../../utils.js";
-import { handleNotOkResult } from "../HomePage.js";
+import { LoginForm } from "../LoginForm.js";
 import { InputAmount } from "../PaytoWireTransferForm.js";
 import { ShowAccountDetails } from "../ShowAccountDetails.js";
 import { UpdateAccountPassword } from "../UpdateAccountPassword.js";
@@ -53,12 +55,10 @@ interface Props {
   account: string,
   onClose: () => void;
   onRegister: () => void;
-  onLoadNotOk: () => void;
 }
 export function BusinessAccount({
   onClose,
   account,
-  onLoadNotOk,
   onRegister,
 }: Props): VNode {
   const { i18n } = useTranslationContext();
@@ -68,12 +68,10 @@ export function BusinessAccount({
     string | undefined
   >();
 
-
   if (newCashout) {
     return (
       <CreateCashout
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setNewcashout(false);
         }}
@@ -91,7 +89,6 @@ export function BusinessAccount({
     return (
       <ShowCashoutDetails
         id={showCashoutDetails}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onCancel={() => {
           setShowCashoutDetails(undefined);
         }}
@@ -102,7 +99,6 @@ export function BusinessAccount({
     return (
       <UpdateAccountPassword
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Password changed`);
           setUpdatePassword(false);
@@ -117,7 +113,6 @@ export function BusinessAccount({
     <div>
       <ShowAccountDetails
         account={account}
-        onLoadNotOk={handleNotOkResult(i18n)}
         onUpdateSuccess={() => {
           notifyInfo(i18n.str`Account updated`);
         }}
@@ -158,11 +153,6 @@ interface PropsCashout {
   account: string;
   onComplete: (id: string) => void;
   onCancel: () => void;
-  onLoadNotOk: <T>(
-    error:
-      | HttpResponsePaginated<T, SandboxBackend.SandboxError>
-      | HttpResponse<T, SandboxBackend.SandboxError>,
-  ) => VNode;
 }
 
 type FormType = {
@@ -175,88 +165,78 @@ type ErrorFrom<T> = {
   [P in keyof T]+?: string;
 };
 
-// check #7719
-function useRatiosAndFeeConfigWithChangeDetection(): HttpResponse<
-  SandboxBackend.Circuit.Config & { hasChanged?: boolean },
-  SandboxBackend.SandboxError
-> {
-  const result = useRatiosAndFeeConfig();
-  const [oldResult, setOldResult] = useState<
-    SandboxBackend.Circuit.Config | undefined
-  >(undefined);
-  const dataFromBackend = result.ok ? result.data : undefined;
-  useEffect(() => {
-    // save only the first result of /config to the backend
-    if (!dataFromBackend || oldResult !== undefined) return;
-    setOldResult(dataFromBackend);
-  }, [dataFromBackend]);
-
-  if (!result.ok) return result;
-
-  const data = !oldResult ? result.data : oldResult;
-  const hasChanged =
-    oldResult &&
-    (result.data.name !== oldResult.name ||
-      result.data.version !== oldResult.version ||
-      result.data.ratios_and_fees.buy_at_ratio !==
-      oldResult.ratios_and_fees.buy_at_ratio ||
-      result.data.ratios_and_fees.buy_in_fee !==
-      oldResult.ratios_and_fees.buy_in_fee ||
-      result.data.ratios_and_fees.sell_at_ratio !==
-      oldResult.ratios_and_fees.sell_at_ratio ||
-      result.data.ratios_and_fees.sell_out_fee !==
-      oldResult.ratios_and_fees.sell_out_fee ||
-      result.data.fiat_currency !== oldResult.fiat_currency);
-
-  return {
-    ...result,
-    data: { ...data, hasChanged },
-  };
-}
 
 function CreateCashout({
-  account,
+  account: accountName,
   onComplete,
   onCancel,
-  onLoadNotOk,
 }: PropsCashout): VNode {
   const { i18n } = useTranslationContext();
-  const ratiosResult = useRatiosAndFeeConfig();
-  const result = useAccountDetails(account);
+  const resultRatios = useRatiosAndFeeConfig();
+  const resultAccount = useAccountDetails(accountName);
   const {
     estimateByCredit: calculateFromCredit,
     estimateByDebit: calculateFromDebit,
   } = useEstimator();
+  const { state } = useBackendState()
+  const creds = state.status !== "loggedIn" ? undefined : state
+  const { api, config } = useBankCoreApiContext()
   const [form, setForm] = useState<Partial<FormType>>({ isDebit: true });
 
-  const { createCashout } = useCircuitAccountAPI();
-  if (!result.ok) return onLoadNotOk(result);
-  if (!ratiosResult.ok) return onLoadNotOk(ratiosResult);
-  const config = ratiosResult.data;
+  if (!resultAccount || !resultRatios) {
+    return <Loading />
+  }
+  if (resultAccount instanceof TalerError) {
+    return <ErrorLoading error={resultAccount} />
+  }
+  if (resultRatios instanceof TalerError) {
+    return <ErrorLoading error={resultRatios} />
+  }
+  if (resultAccount.type === "fail") {
+    switch (resultAccount.case) {
+      case "unauthorized": return <LoginForm reason="forbidden" />
+      case "not-found": return <LoginForm reason="not-found" />
+      default: assertUnreachable(resultAccount)
+    }
+  }
+
+  if (resultRatios.type === "fail") {
+    switch (resultRatios.case) {
+      case "not-supported": return <div>cashout operations are not 
supported</div>
+      default: assertUnreachable(resultRatios.case)
+    }
+  }
+  if (!config.fiat_currency) {
+    return <div>cashout operations are not supported</div>
+  }
 
-  const balance = Amounts.parseOrThrow(result.data.balance.amount);
-  const balanceIsDebit = result.data.balance.credit_debit_indicator == "debit";
+  const ratio = resultRatios.body
 
-  const debitThreshold = Amounts.parseOrThrow(result.data.debit_threshold);
-  const zero = Amounts.zeroOfCurrency(balance.currency);
-  const limit = balanceIsDebit
-    ? Amounts.sub(debitThreshold, balance).amount
-    : Amounts.add(balance, debitThreshold).amount;
+  const account = {
+    balance: Amounts.parseOrThrow(resultAccount.body.balance.amount),
+    balanceIsDebit: resultAccount.body.balance.credit_debit_indicator == 
"debit",
+    debitThreshold: Amounts.parseOrThrow(resultAccount.body.debit_threshold)
+  }
+
+  const zero = Amounts.zeroOfCurrency(account.balance.currency);
+  const limit = account.balanceIsDebit
+    ? Amounts.sub(account.debitThreshold, account.balance).amount
+    : Amounts.add(account.balance, account.debitThreshold).amount;
 
   const zeroCalc = { debit: zero, credit: zero, beforeFee: zero };
   const [calc, setCalc] = useState(zeroCalc);
-  const sellRate = config.ratios_and_fees.sell_at_ratio;
-  const sellFee = !config.ratios_and_fees.sell_out_fee
+
+  const sellRate = ratio.sell_at_ratio;
+  const sellFee = !ratio.sell_out_fee
     ? zero
     : Amounts.parseOrThrow(
-      `${balance.currency}:${config.ratios_and_fees.sell_out_fee}`,
+      `${account.balance.currency}:${ratio.sell_out_fee}`,
     );
-  const fiatCurrency = config.fiat_currency;
 
   if (!sellRate || sellRate < 0) return <div>error rate</div>;
 
   const amount = Amounts.parseOrThrow(
-    `${!form.isDebit ? fiatCurrency : balance.currency}:${!form.amount ? "0" : 
form.amount
+    `${!form.isDebit ? config.fiat_currency.name : 
account.balance.currency}:${!form.amount ? "0" : form.amount
     }`,
   );
 
@@ -267,15 +247,16 @@ function CreateCashout({
           setCalc(r);
         })
         .catch((error) => {
-          notify(
-            error instanceof RequestError
-              ? buildRequestErrorMessage(i18n, error.cause)
-              : {
-                type: "error",
-                title: i18n.str`Could not estimate the cashout`,
-                description: error.message as TranslatedString
-              },
-          );
+          if (error instanceof TalerError) {
+            notify(buildRequestErrorMessage(i18n, error))
+          } else {
+            notifyError(
+              i18n.str`Operation failed, please report`,
+              (error instanceof Error
+                ? error.message
+                : JSON.stringify(error)) as TranslatedString
+            )
+          }
         });
     } else {
       calculateFromCredit(amount, sellFee, sellRate)
@@ -283,20 +264,21 @@ function CreateCashout({
           setCalc(r);
         })
         .catch((error) => {
-          notify(
-            error instanceof RequestError
-              ? buildRequestErrorMessage(i18n, error.cause)
-              : {
-                type: "error",
-                title: i18n.str`Could not estimate the cashout`,
-                description: error.message,
-              },
-          );
+          if (error instanceof TalerError) {
+            notify(buildRequestErrorMessage(i18n, error))
+          } else {
+            notifyError(
+              i18n.str`Operation failed, please report`,
+              (error instanceof Error
+                ? error.message
+                : JSON.stringify(error)) as TranslatedString
+            )
+          }
         });
     }
   }, [form.amount, form.isDebit]);
 
-  const balanceAfter = Amounts.sub(balance, calc.debit).amount;
+  const balanceAfter = Amounts.sub(account.balance, calc.debit).amount;
 
   function updateForm(newForm: typeof form): void {
     setForm(newForm);
@@ -374,8 +356,8 @@ function CreateCashout({
           <label for="balance-now">{i18n.str`Balance now`}</label>
           <InputAmount
             name="banace-now"
-            currency={balance.currency}
-            value={Amounts.stringifyValue(balance)}
+            currency={account.balance.currency}
+            value={Amounts.stringifyValue(account.balance)}
           />
         </fieldset>
         <fieldset>
@@ -384,7 +366,7 @@ function CreateCashout({
           >{i18n.str`Total cost`}</label>
           <InputAmount
             name="total-cost"
-            currency={balance.currency}
+            currency={account.balance.currency}
             value={Amounts.stringifyValue(calc.debit)}
           />
         </fieldset>
@@ -392,7 +374,7 @@ function CreateCashout({
           <label for="balance-after">{i18n.str`Balance after`}</label>
           <InputAmount
             name="balance-after"
-            currency={balance.currency}
+            currency={account.balance.currency}
             value={balanceAfter ? Amounts.stringifyValue(balanceAfter) : ""}
           />
         </fieldset>{" "}
@@ -402,7 +384,7 @@ function CreateCashout({
               <label for="amount-conversiojn">{i18n.str`Amount after 
conversion`}</label>
               <InputAmount
                 name="amount-conversion"
-                currency={fiatCurrency}
+                currency={config.fiat_currency.name}
                 value={Amounts.stringifyValue(calc.beforeFee)}
               />
             </fieldset>
@@ -411,7 +393,7 @@ function CreateCashout({
               <label form="cashout-fee">{i18n.str`Cashout fee`}</label>
               <InputAmount
                 name="cashout-fee"
-                currency={fiatCurrency}
+                currency={config.fiat_currency.name}
                 value={Amounts.stringifyValue(sellFee)}
               />
             </fieldset>
@@ -423,7 +405,7 @@ function CreateCashout({
           >{i18n.str`Total cashout transfer`}</label>
           <InputAmount
             name="total"
-            currency={fiatCurrency}
+            currency={config.fiat_currency.name}
             value={Amounts.stringifyValue(calc.credit)}
           />
         </fieldset>
@@ -501,35 +483,55 @@ function CreateCashout({
             onClick={async (e) => {
               e.preventDefault();
 
-              if (errors) return;
+              if (errors || !creds) return;
               try {
-                const res = await createCashout({
+                const resp = await api.createCashout(creds, {
                   amount_credit: Amounts.stringify(calc.credit),
                   amount_debit: Amounts.stringify(calc.debit),
                   subject: form.subject,
                   tan_channel: form.channel,
                 });
-                onComplete(res.data.uuid);
+                if (resp.type === "ok") {
+                  mutate(() => true)// clean cashout list
+                  onComplete(resp.body.cashout_id);
+                } else {
+                  switch (resp.case) {
+                    case "incorrect-exchange-rate": return notify({
+                      type: "error",
+                      title: i18n.str`The exchange rate was incorrectly 
applied`,
+                      description: resp.detail.hint as TranslatedString,
+                      debug: resp.detail,
+                    });
+                    case "no-allowed": return notify({
+                      type: "error",
+                      title: i18n.str`This user is not allowed to make a 
cashout`,
+                      description: resp.detail.hint as TranslatedString,
+                      debug: resp.detail,
+                    });
+                    case "no-contact-info": return notify({
+                      type: "error",
+                      title: i18n.str`Need a contact data where to send the 
TAN`,
+                      description: resp.detail.hint as TranslatedString,
+                      debug: resp.detail,
+                    });
+                    case "no-enough-balance": return notify({
+                      type: "error",
+                      title: i18n.str`The account does not have sufficient 
funds`,
+                      description: resp.detail.hint as TranslatedString,
+                      debug: resp.detail,
+                    });
+                    case "tan-not-supported": return notify({
+                      type: "error",
+                      title: i18n.str`The bank does not support the TAN 
channel for this operation`,
+                      description: resp.detail.hint as TranslatedString,
+                      debug: resp.detail,
+                    });
+                    default: assertUnreachable(resp)
+                  }
+                }
               } catch (error) {
-                if (error instanceof RequestError) {
-                  notify(
-                    buildRequestErrorMessage(i18n, error.cause, {
-                      onClientError: (status) =>
-                        status === HttpStatusCode.BadRequest
-                          ? i18n.str`The exchange rate was incorrectly applied`
-                          : status === HttpStatusCode.Forbidden
-                            ? i18n.str`A institutional user tried the 
operation`
-                            : status === HttpStatusCode.Conflict
-                              ? i18n.str`Need a contact data where to send the 
TAN`
-                              : status === HttpStatusCode.PreconditionFailed
-                                ? i18n.str`The account does not have 
sufficient funds`
-                                : undefined,
-                      onServerError: (status) =>
-                        status === HttpStatusCode.ServiceUnavailable
-                          ? i18n.str`The bank does not support the TAN channel 
for this operation`
-                          : undefined,
-                    }),
-                  );
+                if (error instanceof TalerError) {
+                  notify(buildRequestErrorMessage(i18n, error))
                 } else {
                   notifyError(
                     i18n.str`Operation failed, please report`,
@@ -552,24 +554,34 @@ function CreateCashout({
 interface ShowCashoutProps {
   id: string;
   onCancel: () => void;
-  onLoadNotOk: <T>(
-    error: HttpResponsePaginated<T, SandboxBackend.SandboxError>,
-  ) => VNode;
 }
 export function ShowCashoutDetails({
   id,
   onCancel,
-  onLoadNotOk,
 }: ShowCashoutProps): VNode {
   const { i18n } = useTranslationContext();
+  const { state } = useBackendState();
+  const creds = state.status !== "loggedIn" ? undefined : state
+  const { api } = useBankCoreApiContext()
   const result = useCashoutDetails(id);
-  const { abortCashout, confirmCashout } = useCircuitAccountAPI();
   const [code, setCode] = useState<string | undefined>(undefined);
-  if (!result.ok) return onLoadNotOk(result);
+
+  if (!result) {
+    return <Loading />
+  }
+  if (result instanceof TalerError) {
+    return <ErrorLoading error={result} />
+  }
+  if (result.type === "fail") {
+    switch (result.case) {
+      case "already-aborted": return <div>this cashout is already aborted</div>
+      default: assertUnreachable(result.case)
+    }
+  }
   const errors = undefinedIfEmpty({
     code: !code ? i18n.str`required` : undefined,
   });
-  const isPending = String(result.data.status).toUpperCase() === "PENDING";
+  const isPending = String(result.body.status).toUpperCase() === "PENDING";
   return (
     <div>
       <h1>Cashout details {id}</h1>
@@ -578,43 +590,47 @@ export function ShowCashoutDetails({
           <label>
             <i18n.Translate>Subject</i18n.Translate>
           </label>
-          <input readOnly value={result.data.subject} />
+          <input readOnly value={result.body.subject} />
         </fieldset>
         <fieldset>
           <label>
             <i18n.Translate>Created</i18n.Translate>
           </label>
-          <input readOnly value={result.data.creation_time ?? ""} />
+          <input readOnly value={result.body.creation_time.t_s === "never" ? 
i18n.str`never` : format(result.body.creation_time.t_s, "dd/MM/yyyy HH:mm:ss")} 
/>
         </fieldset>
         <fieldset>
           <label>
             <i18n.Translate>Confirmed</i18n.Translate>
           </label>
-          <input readOnly value={result.data.confirmation_time ?? ""} />
+          <input readOnly value={result.body.confirmation_time === undefined ? 
"-" :
+            (result.body.confirmation_time.t_s === "never" ?
+              i18n.str`never` :
+              format(result.body.confirmation_time.t_s, "dd/MM/yyyy HH:mm:ss"))
+          } />
         </fieldset>
         <fieldset>
           <label>
             <i18n.Translate>Debited</i18n.Translate>
           </label>
-          <input readOnly value={result.data.amount_debit} />
+          <input readOnly value={result.body.amount_debit} />
         </fieldset>
         <fieldset>
           <label>
             <i18n.Translate>Credit</i18n.Translate>
           </label>
-          <input readOnly value={result.data.amount_credit} />
+          <input readOnly value={result.body.amount_credit} />
         </fieldset>
         <fieldset>
           <label>
             <i18n.Translate>Status</i18n.Translate>
           </label>
-          <input readOnly value={result.data.status} />
+          <input readOnly value={result.body.status} />
         </fieldset>
         <fieldset>
           <label>
             <i18n.Translate>Destination</i18n.Translate>
           </label>
-          <input readOnly value={result.data.cashout_address} />
+          <input readOnly value={result.body.credit_payto_uri} />
         </fieldset>
         {isPending ? (
           <fieldset>
@@ -652,21 +668,33 @@ export function ShowCashoutDetails({
               class="pure-button pure-button-primary button-error"
               onClick={async (e) => {
                 e.preventDefault();
+                if (!creds) return;
                 try {
-                  await abortCashout(id);
-                  onCancel();
+                  const resp = await api.abortCashoutById(creds, id);
+                  if (resp.type === "ok") {
+                    onCancel();
+                  } else {
+                    switch (resp.case) {
+                      case "not-found": return notify({
+                        type: "error",
+                        title: i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      case "already-confirmed": return notify({
+                        type: "error",
+                        title: i18n.str`Cashout was already confimed.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      default: {
+                        assertUnreachable(resp)
+                      }
+                    }
+                  }
                 } catch (error) {
-                  if (error instanceof RequestError) {
-                    notify(
-                      buildRequestErrorMessage(i18n, error.cause, {
-                        onClientError: (status) =>
-                          status === HttpStatusCode.NotFound
-                            ? i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`
-                            : status === HttpStatusCode.PreconditionFailed
-                              ? i18n.str`Cashout was already confimed`
-                              : undefined,
-                      }),
-                    );
+                  if (error instanceof TalerError) {
+                    notify(buildRequestErrorMessage(i18n, error))
                   } else {
                     notifyError(
                       i18n.str`Operation failed, please report`,
@@ -687,27 +715,40 @@ export function ShowCashoutDetails({
               class="pure-button pure-button-primary "
               onClick={async (e) => {
                 e.preventDefault();
+                if (!creds) return;
                 try {
                   if (!code) return;
-                  const rest = await confirmCashout(id, {
+                  const resp = await api.confirmCashoutById(creds, id, {
                     tan: code,
                   });
+                  if (resp.type === "ok") {
+                    mutate(() => true)//clean cashout state
+                  } else {
+                    switch (resp.case) {
+                      case "not-found": return notify({
+                        type: "error",
+                        title: i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      case "wrong-tan-or-credential": return notify({
+                        type: "error",
+                        title: i18n.str`Invalid code or credentials.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      case "cashout-address-changed": return notify({
+                        type: "error",
+                        title: i18n.str`The cash-out address between the 
creation and the confirmation changed.`,
+                        description: resp.detail.hint as TranslatedString,
+                        debug: resp.detail,
+                      })
+                      default: assertUnreachable(resp)
+                    }
+                  }
                 } catch (error) {
-                  if (error instanceof RequestError) {
-                    notify(
-                      buildRequestErrorMessage(i18n, error.cause, {
-                        onClientError: (status) =>
-                          status === HttpStatusCode.NotFound
-                            ? i18n.str`Cashout not found. It may be also mean 
that it was already aborted.`
-                            : status === HttpStatusCode.PreconditionFailed
-                              ? i18n.str`Cashout was already confimed`
-                              : status === HttpStatusCode.Conflict
-                                ? i18n.str`Confirmation failed. Maybe the user 
changed their cash-out address between the creation and the confirmation`
-                                : status === HttpStatusCode.Forbidden
-                                  ? i18n.str`Invalid code`
-                                  : undefined,
-                      }),
-                    );
+                  if (error instanceof TalerError) {
+                    notify(buildRequestErrorMessage(i18n, error))
                   } else {
                     notifyError(
                       i18n.str`Operation failed, please report`,
diff --git a/packages/demobank-ui/src/stories.test.ts 
b/packages/demobank-ui/src/stories.test.ts
index 07db7d8cf..265304b25 100644
--- a/packages/demobank-ui/src/stories.test.ts
+++ b/packages/demobank-ui/src/stories.test.ts
@@ -18,7 +18,7 @@
  *
  * @author Sebastian Javier Marchano (sebasjm)
  */
-import { setupI18n } from "@gnu-taler/taler-util";
+import { AccessToken, setupI18n } from "@gnu-taler/taler-util";
 import { parseGroupImport } from "@gnu-taler/web-util/browser";
 import * as tests from "@gnu-taler/web-util/testing";
 import * as components from "./components/index.examples.js";
@@ -26,7 +26,6 @@ import * as pages from "./pages/index.stories.js";
 
 import { ComponentChildren, VNode, h as create } from "preact";
 import { BackendStateProviderTesting } from "./context/backend.js";
-import { AccessToken } from "./hooks/useCredentialsChecker.js";
 
 setupI18n("en", { en: {} });
 
diff --git a/packages/demobank-ui/src/utils.ts 
b/packages/demobank-ui/src/utils.ts
index e7673f078..310e80cd6 100644
--- a/packages/demobank-ui/src/utils.ts
+++ b/packages/demobank-ui/src/utils.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { HttpStatusCode, TranslatedString } from "@gnu-taler/taler-util";
+import { HttpStatusCode, TalerError, TalerErrorCode, TranslatedString } from 
"@gnu-taler/taler-util";
 import {
   ErrorNotification,
   ErrorType,
@@ -62,15 +62,15 @@ export type PartialButDefined<T> = {
 
 export type WithIntermediate<Type extends object> = {
   [prop in keyof Type]: Type[prop] extends object
-    ? WithIntermediate<Type[prop]>
-    : Type[prop] | undefined;
+  ? WithIntermediate<Type[prop]>
+  : Type[prop] | undefined;
 };
 export type RecursivePartial<T> = {
   [P in keyof T]?: T[P] extends (infer U)[]
-    ? RecursivePartial<U>[]
-    : T[P] extends object
-    ? RecursivePartial<T[P]>
-    : T[P];
+  ? RecursivePartial<U>[]
+  : T[P] extends object
+  ? RecursivePartial<T[P]>
+  : T[P];
 };
 
 export enum TanChannel {
@@ -94,59 +94,61 @@ export const MAX_RESULT_SIZE = PAGE_SIZE * 2 - 1;
 
 export function buildRequestErrorMessage(
   i18n: ReturnType<typeof useTranslationContext>["i18n"],
-  cause: HttpError<SandboxBackend.SandboxError>,
-  specialCases: {
-    onClientError?: (status: HttpStatusCode) => TranslatedString | undefined;
-    onServerError?: (status: HttpStatusCode) => TranslatedString | undefined;
-  } = {},
+  cause: TalerError<{}>,
 ): ErrorNotification {
   let result: ErrorNotification;
-  switch (cause.type) {
-    case ErrorType.TIMEOUT: {
+  switch (cause.errorDetail.code) {
+    case TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT: {
       result = {
         type: "error",
         title: i18n.str`Request timeout`,
+        description: cause.message as TranslatedString,
+        debug: JSON.stringify(cause.errorDetail, undefined, 2),
       };
       break;
     }
-    case ErrorType.CLIENT: {
-      const title =
-        specialCases.onClientError && specialCases.onClientError(cause.status);
+    case TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED: {
       result = {
         type: "error",
-        title: title ? title : i18n.str`The server didn't accept the request`,
-        description: cause?.payload?.error?.description as TranslatedString,
-        debug: JSON.stringify(cause),
+        title: i18n.str`Request throttled`,
+        description: cause.message as TranslatedString,
+        debug: JSON.stringify(cause.errorDetail, undefined, 2),
       };
       break;
     }
-    case ErrorType.SERVER: {
-      const title =
-        specialCases.onServerError && specialCases.onServerError(cause.status);
+    case TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE: {
       result = {
         type: "error",
-        title: title
-          ? title
-          : i18n.str`The server had problems processing the request`,
-        description: cause?.payload?.error?.description as TranslatedString,
-        debug: JSON.stringify(cause),
+        title: i18n.str`Malformed response`,
+        description: cause.message as TranslatedString,
+        debug: JSON.stringify(cause.errorDetail, undefined, 2),
       };
       break;
     }
-    case ErrorType.UNREADABLE: {
+    case TalerErrorCode.WALLET_NETWORK_ERROR: {
       result = {
         type: "error",
-        title: i18n.str`Unexpected error`,
-        description: `Response from ${cause?.info?.url} is unreadable, status: 
${cause?.status}` as TranslatedString,
-        debug: JSON.stringify(cause),
+        title: i18n.str`Network error`,
+        description: cause.message as TranslatedString,
+        debug: JSON.stringify(cause.errorDetail, undefined, 2),
+      };
+      break;
+    }
+    case TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR: {
+      result = {
+        type: "error",
+        title: i18n.str`Unexpected request error`,
+        description: cause.message as TranslatedString,
+        debug: JSON.stringify(cause.errorDetail, undefined, 2),
       };
       break;
     }
-    case ErrorType.UNEXPECTED: {
+    default: {
       result = {
         type: "error",
         title: i18n.str`Unexpected error`,
-        debug: JSON.stringify(cause),
+        description: cause.message as TranslatedString,
+        debug: JSON.stringify(cause.errorDetail, undefined, 2),
       };
       break;
     }
diff --git a/packages/merchant-backoffice-ui/src/hooks/testing.tsx 
b/packages/merchant-backoffice-ui/src/hooks/testing.tsx
index 847d512b0..386944854 100644
--- a/packages/merchant-backoffice-ui/src/hooks/testing.tsx
+++ b/packages/merchant-backoffice-ui/src/hooks/testing.tsx
@@ -21,11 +21,13 @@
 
 import { MockEnvironment } from "@gnu-taler/web-util/testing";
 import { ComponentChildren, FunctionalComponent, h, VNode } from "preact";
+import { HttpRequestLibrary, HttpRequestOptions, HttpResponse } from 
"@gnu-taler/taler-util/http";
 import { SWRConfig } from "swr";
 import { ApiContextProvider } from "@gnu-taler/web-util/browser";
 import { BackendContextProvider } from "../context/backend.js";
 import { InstanceContextProvider } from "../context/instance.js";
 import { HttpResponseOk, RequestOptions } from "@gnu-taler/web-util/browser";
+import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient } from 
"@gnu-taler/taler-util";
 
 export class ApiMockEnvironment extends MockEnvironment {
   constructor(debug = false) {
@@ -47,6 +49,7 @@ export class ApiMockEnvironment extends MockEnvironment {
     }: {
       children: ComponentChildren;
     }): VNode {
+
       async function request<T>(
         base: string,
         path: string,
@@ -89,6 +92,62 @@ export class ApiMockEnvironment extends MockEnvironment {
       }
       const SC: any = SWRConfig;
 
+      const mockHttpClient = new class implements HttpRequestLibrary {
+        async fetch(url: string, options?: HttpRequestOptions | undefined): 
Promise<HttpResponse> {
+          const _url = new URL(url);
+          const mocked = __SAVE_REQUEST_AND_GET_MOCKED_RESPONSE(
+            {
+              method: options?.method ?? "GET",
+              url: _url.href,
+            },
+            {
+              qparam: _url.searchParams,
+              auth: options as any,
+              request: options?.body as any,
+            },
+          );
+          const status = mocked.expectedQuery?.query.code ?? 200;
+          const requestPayload = mocked.expectedQuery?.params?.request;
+          const responsePayload = mocked.expectedQuery?.params?.response;
+
+          // FIXME: complete this implementation to mock any query
+          const resp: HttpResponse = {
+            requestUrl: _url.href,
+            status: status,
+            headers: {} as any,
+            requestMethod: options?.method ?? "GET",
+            json: async () => responsePayload,
+            text: async () => responsePayload as any as string,
+            bytes: async () => responsePayload as ArrayBuffer,
+          };
+          return resp
+        }
+        get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse> {
+          return this.fetch(url, {
+            method: "GET",
+            ...opt,
+          });
+        }
+
+        postJson(
+          url: string,
+          body: any,
+          opt?: HttpRequestOptions,
+        ): Promise<HttpResponse> {
+          return this.fetch(url, {
+            method: "POST",
+            headers: { "Content-Type": "application/json" },
+            body: JSON.stringify(body),
+            ...opt,
+          });
+        }
+
+      }
+      const bankCore = new TalerCoreBankHttpClient("", mockHttpClient)
+      const bankIntegration = bankCore.getIntegrationAPI()
+      const bankRevenue = bankCore.getRevenueAPI("")
+      const bankWire = bankCore.getWireGatewayAPI("")
+
       return (
         <BackendContextProvider defaultUrl="http://backend";>
           <InstanceContextProvider
@@ -99,7 +158,7 @@ export class ApiMockEnvironment extends MockEnvironment {
               changeToken: () => null,
             }}
           >
-            <ApiContextProvider value={{ request }}>
+            <ApiContextProvider value={{ request, bankCore, bankIntegration, 
bankRevenue, bankWire }}>
               <SC
                 value={{
                   loadingTimeout: 0,
diff --git a/packages/taler-util/src/errors.ts 
b/packages/taler-util/src/errors.ts
index dcdf56c39..f17f7db6a 100644
--- a/packages/taler-util/src/errors.ts
+++ b/packages/taler-util/src/errors.ts
@@ -34,19 +34,6 @@ import {
 
 type empty = Record<string, never>;
 
-export interface HttpErrors {
-  // timeout
-  [TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT]: empty;
-  // throttled
-  [TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED]: empty;
-  // parsing
-  [TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE]: empty;
-  // network
-  [TalerErrorCode.WALLET_NETWORK_ERROR]: empty;
-  // everything else
-  [TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR]: empty;
-}
-
 export interface DetailsMap {
   [TalerErrorCode.WALLET_PENDING_OPERATION_FAILED]: {
     innerError: TalerErrorDetail;
@@ -102,7 +89,11 @@ export interface DetailsMap {
     requestMethod: string;
     throttleStats: Record<string, unknown>;
   };
-  [TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT]: empty;
+  [TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT]: {
+    requestUrl: string;
+    requestMethod: string;
+    timeoutMs: number;
+  };
   [TalerErrorCode.WALLET_NETWORK_ERROR]: {
     requestUrl: string;
     requestMethod: string;
@@ -205,6 +196,19 @@ export class TalerProtocolViolationError extends Error {
   }
 }
 
+// compute a subset of TalerError, just for http request
+type HttpErrors = TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT
+  | TalerErrorCode.WALLET_HTTP_REQUEST_THROTTLED
+  | TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE
+  | TalerErrorCode.WALLET_NETWORK_ERROR
+  | TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR;
+
+type TalerHttpErrorsDetails = {
+  [code in HttpErrors]: TalerError<DetailsMap[code]>
+}
+
+export type TalerHttpError = TalerHttpErrorsDetails[keyof 
TalerHttpErrorsDetails]
+
 export class TalerError<T = any> extends Error {
   errorDetail: TalerErrorDetail & T;
   private constructor(d: TalerErrorDetail & T) {
@@ -239,6 +243,7 @@ export class TalerError<T = any> extends Error {
   ): this is TalerError<DetailsMap[C]> {
     return this.errorDetail.code === code;
   }
+
 }
 
 /**
diff --git a/packages/taler-util/src/http-client/authentication.ts 
b/packages/taler-util/src/http-client/authentication.ts
new file mode 100644
index 000000000..0c59c9308
--- /dev/null
+++ b/packages/taler-util/src/http-client/authentication.ts
@@ -0,0 +1,83 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 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/>
+ */
+
+import { HttpStatusCode } from "../http-status-codes.js";
+import { HttpRequestLibrary, createPlatformHttpLib, makeBasicAuthHeader } from 
"../http.js";
+import { LibtoolVersion } from "../libtool-version.js";
+import { AccessToken, TalerAuthentication, UserAndPassword, UserAndToken, 
codecForTokenSuccessResponse } from "./types.js";
+import { makeBearerTokenAuthHeader, opEmptySuccess, opKnownFailure, opSuccess, 
opUnknownFailure } from "./utils.js";
+
+export class TalerAuthenticationHttpClient {
+  public readonly PROTOCOL_VERSION = "0:0:0";
+
+  httpLib: HttpRequestLibrary;
+
+  constructor(
+    readonly baseUrl: string,
+    readonly username: string,
+    httpClient?: HttpRequestLibrary,
+  ) {
+    this.httpLib = httpClient ?? createPlatformHttpLib();
+  }
+
+  isCompatible(version: string): boolean {
+    const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version)
+    return compare?.compatible ?? false
+  }
+
+  /**
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
+   * 
+   * @returns 
+   */
+  async createAccessToken(
+    password: string,
+    body: TalerAuthentication.TokenRequest,
+  ) {
+    const url = new URL(`token`, this.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "POST",
+      headers: {
+        Authorization: makeBasicAuthHeader(this.username, password),
+      },
+      body
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForTokenSuccessResponse())
+      //FIXME: missing in docs
+      case HttpStatusCode.Unauthorized: return 
opKnownFailure("wrong-credentials", resp)
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
+      default: return opUnknownFailure(resp, await resp.text())
+    }
+  }
+
+  async deleteAccessToken(token: AccessToken) {
+    const url = new URL(`token`, this.baseUrl);
+    const resp = await this.httpLib.fetch(url.href, {
+      method: "DELETE",
+      headers: {
+        Authorization: makeBearerTokenAuthHeader(token),
+      }
+    });
+    switch (resp.status) {
+      case HttpStatusCode.Ok: return opEmptySuccess()
+      //FIXME: missing in docs
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
+      default: return opUnknownFailure(resp, await resp.text())
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index 7b4bb53d4..593daa2c3 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -18,63 +18,50 @@ import {
   AmountJson,
   Amounts,
   HttpStatusCode,
-  Logger
+  LibtoolVersion
 } from "@gnu-taler/taler-util";
 import {
   HttpRequestLibrary,
-  createPlatformHttpLib,
-  expectSuccessResponseOrThrow,
-  readSuccessResponseJsonOrThrow
+  createPlatformHttpLib
 } from "@gnu-taler/taler-util/http";
 import { TalerBankIntegrationHttpClient } from "./bank-integration.js";
 import { TalerRevenueHttpClient } from "./bank-revenue.js";
 import { TalerWireGatewayHttpClient } from "./bank-wire.js";
-import { AccessToken, TalerAuthentication, TalerCorebankApi, 
codecForAccountData, codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, 
codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, 
codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, 
codecForConversionRatesResponse, codecForCoreBankConfig, 
codecForGlobalCashouts, codecForListBankAccountsResponse, 
codecForMonitorResponse [...]
-import { PaginationParams, UserAndPassword, UserAndToken, addPaginationParams, 
httpEmptySuccess, httpSuccess, knownFailure, makeBasicAuthHeader, 
makeBearerTokenAuthHeader, unknownFailure } from "./utils.js";
+import { AccessToken, OperationOk, PaginationParams, TalerCorebankApi, 
UserAndToken, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, 
codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, 
codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, 
codecForConversionRatesResponse, codecForCoreBankConfig, 
codecForGlobalCashouts, codecForListBankAccountsResponse, [...]
+import { addPaginationParams, opFixedSuccess, opEmptySuccess, opSuccess, 
opKnownFailure, makeBearerTokenAuthHeader, opUnknownFailure } from "./utils.js";
+import { TalerAuthenticationHttpClient } from "./authentication.js";
 
-const logger = new Logger("http-client/core-bank.ts");
 
+type props = keyof TalerCoreBankHttpClient
+
+export type TalerCoreBankResultByMethod<p extends props> = 
TalerCoreBankHttpClient[p] extends (...args: any[]) => infer Ret ?
+  Ret extends Promise<infer Result> ?
+  Result :
+  never : //api always use Promises
+  never; //error cases just for functions
+
+export type TalerCoreBankErrorsByMethod<p extends props> = 
Exclude<TalerCoreBankResultByMethod<p>, OperationOk<any>>
+
+/**
+ * Protocol version spoken with the bank.
+ *
+ * Uses libtool's current:revision:age versioning.
+ */
 export class TalerCoreBankHttpClient {
+  public readonly PROTOCOL_VERSION = "0:0:0";
+
   httpLib: HttpRequestLibrary;
 
   constructor(
-    private baseUrl: string,
+    readonly baseUrl: string,
     httpClient?: HttpRequestLibrary,
   ) {
     this.httpLib = httpClient ?? createPlatformHttpLib();
   }
 
-  /**
-   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
-   * 
-   * @returns 
-   */
-  async createAccessToken(
-    auth: UserAndPassword,
-    body: TalerAuthentication.TokenRequest,
-  ): Promise<TalerAuthentication.TokenSuccessResponse> {
-    const url = new URL(`accounts/${auth.username}/token`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "POST",
-      headers: {
-        Authorization: makeBasicAuthHeader(auth.username, auth.password),
-      },
-      body
-    });
-    return readSuccessResponseJsonOrThrow(resp, 
codecForTokenSuccessResponse());
-  }
-
-  async deleteAccessToken(
-    auth: UserAndToken,
-  ): Promise<void> {
-    const url = new URL(`accounts/${auth.username}/token`, this.baseUrl);
-    const resp = await this.httpLib.fetch(url.href, {
-      method: "DELETE",
-      headers: {
-        Authorization: makeBearerTokenAuthHeader(auth.token),
-      }
-    });
-    return expectSuccessResponseOrThrow(resp);
+  isCompatible(version: string): boolean {
+    const compare = LibtoolVersion.compare(this.PROTOCOL_VERSION, version)
+    return compare?.compatible ?? false
   }
 
   /**
@@ -88,8 +75,8 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: missing in docs
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForCoreBankConfig())
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, codecForCoreBankConfig())
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -111,17 +98,19 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.BadRequest: return knownFailure("invalid-input", 
resp);
+      //FIXME: NOT IN THE DOOOCS
+      case HttpStatusCode.Created: return opEmptySuccess()
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
       case HttpStatusCode.Forbidden: {
         if (body.username === "bank" || body.username === "admin") {
-          return knownFailure("unable-to-create", resp);
+          return opKnownFailure("unable-to-create", resp);
         } else {
-          return knownFailure("unauthorized", resp);
+          return opKnownFailure("unauthorized", resp);
         }
       }
-      case HttpStatusCode.Conflict: return knownFailure("already-exist", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Conflict: return opKnownFailure("already-exist", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
   /**
@@ -137,17 +126,17 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       case HttpStatusCode.Forbidden: {
         if (auth.username === "bank" || auth.username === "admin") {
-          return knownFailure("unable-to-delete", resp);
+          return opKnownFailure("unable-to-delete", resp);
         } else {
-          return knownFailure("unauthorized", resp);
+          return opKnownFailure("unauthorized", resp);
         }
       }
-      case HttpStatusCode.PreconditionFailed: return 
knownFailure("balance-not-zero", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.PreconditionFailed: return 
opKnownFailure("balance-not-zero", resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -165,10 +154,10 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -186,12 +175,12 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
+      case HttpStatusCode.NoContent: return opEmptySuccess()
       //FIXME: missing in docs
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       //FIXME: missing in docs
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -199,8 +188,10 @@ export class TalerCoreBankHttpClient {
    * https://docs.taler.net/core/get-$BANK_API_BASE_URL-public-accounts
    * 
    */
-  async getPublicAccounts() {
+  async getPublicAccounts(pagination?: PaginationParams) {
     const url = new URL(`public-accounts`, this.baseUrl);
+    //FIXME: missing pagination in docs
+    addPaginationParams(url, pagination)
     const resp = await this.httpLib.fetch(url.href, {
       method: "GET",
       headers: {
@@ -208,10 +199,10 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: missing in docs
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForPublicAccountsResponse())
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForPublicAccountsResponse())
       //FIXME: missing in docs
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.NoContent: return opFixedSuccess({ public_accounts: 
[] })
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -219,8 +210,9 @@ export class TalerCoreBankHttpClient {
    * https://docs.taler.net/core/api-corebank.html#get--accounts
    * 
    */
-  async getAccounts(auth: AccessToken) {
+  async getAccounts(auth: AccessToken, pagination?: PaginationParams) {
     const url = new URL(`accounts`, this.baseUrl);
+    addPaginationParams(url, pagination)
     const resp = await this.httpLib.fetch(url.href, {
       method: "GET",
       headers: {
@@ -228,10 +220,10 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForListBankAccountsResponse())
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForListBankAccountsResponse())
+      case HttpStatusCode.NoContent: return opFixedSuccess({ accounts: [] })
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -248,12 +240,12 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, codecForAccountData())
+      case HttpStatusCode.Ok: return opSuccess(resp, codecForAccountData())
       //FIXME: missing in docs
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       //FIXME: missing in docs
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -275,14 +267,14 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForBankAccountTransactionsResponse())
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountTransactionsResponse())
       //FIXME: missing in docs
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
+      case HttpStatusCode.NoContent: return opFixedSuccess({ transactions: [] 
})
       //FIXME: missing in docs
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       //FIXME: missing in docs
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -299,12 +291,12 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForBankAccountTransactionInfo())
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountTransactionInfo())
       //FIXME: missing in docs
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
       //FIXME: missing in docs
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -323,12 +315,12 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: fix docs... it should be NoContent 
-      case HttpStatusCode.Ok: return httpEmptySuccess()
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.BadRequest: return knownFailure("invalid-input", 
resp);
+      case HttpStatusCode.Ok: return opEmptySuccess()
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
       //FIXME: missing in docs
-      case HttpStatusCode.Forbidden: return knownFailure("unauthorized", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Forbidden: return opKnownFailure("unauthorized", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -351,9 +343,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: missing in docs
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForBankAccountCreateWithdrawalResponse())
-      case HttpStatusCode.Forbidden: return knownFailure("insufficient-funds", 
resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountCreateWithdrawalResponse())
+      case HttpStatusCode.Forbidden: return 
opKnownFailure("insufficient-funds", resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -368,8 +360,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: missing in docs
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForBankAccountGetWithdrawalResponse())
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountGetWithdrawalResponse())
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -384,10 +377,10 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: fix docs... it should be NoContent 
-      case HttpStatusCode.Ok: return httpEmptySuccess()
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.Conflict: return 
knownFailure("previously-confirmed", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opEmptySuccess()
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.Conflict: return 
opKnownFailure("previously-confirmed", resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -402,11 +395,11 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: fix docs... it should be NoContent 
-      case HttpStatusCode.Ok: return httpEmptySuccess()
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.Conflict: return knownFailure("previously-aborted", 
resp);
-      case HttpStatusCode.UnprocessableEntity: return 
knownFailure("no-exchange-or-reserve-selected", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opEmptySuccess()
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.Conflict: return 
opKnownFailure("previously-aborted", resp);
+      case HttpStatusCode.UnprocessableEntity: return 
opKnownFailure("no-exchange-or-reserve-selected", resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -428,11 +421,17 @@ export class TalerCoreBankHttpClient {
       body,
     });
     switch (resp.status) {
-      case HttpStatusCode.Accepted: return httpSuccess(resp, 
codecForCashoutPending())
+      case HttpStatusCode.Accepted: return opSuccess(resp, 
codecForCashoutPending())
       //FIXME: it should be precondition-failed
-      case HttpStatusCode.Conflict: return knownFailure("invalid-state", resp);
-      case HttpStatusCode.ServiceUnavailable: return 
knownFailure("tan-not-supported", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Conflict: return opKnownFailure("no-contact-info", 
resp);
+      //FIXME: missing in the docs
+      case HttpStatusCode.Forbidden: return opKnownFailure("no-allowed", resp);
+      //FIXME: missing in the docs
+      case HttpStatusCode.PreconditionFailed: return 
opKnownFailure("no-enough-balance", resp);
+      //FIXME: missing in the docs
+      case HttpStatusCode.BadRequest: return 
opKnownFailure("incorrect-exchange-rate", resp);
+      case HttpStatusCode.ServiceUnavailable: return 
opKnownFailure("tan-not-supported", resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -449,10 +448,10 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
-      case HttpStatusCode.Conflict: return knownFailure("already-confirmed", 
resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
+      case HttpStatusCode.Conflict: return opKnownFailure("already-confirmed", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -470,11 +469,11 @@ export class TalerCoreBankHttpClient {
       body,
     });
     switch (resp.status) {
-      case HttpStatusCode.NoContent: return httpEmptySuccess()
-      case HttpStatusCode.Forbidden: return 
knownFailure("wrong-tan-or-credential", resp);
-      case HttpStatusCode.NotFound: return knownFailure("not-found", resp);
-      case HttpStatusCode.Conflict: return 
knownFailure("cashout-address-changed", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.Forbidden: return 
opKnownFailure("wrong-tan-or-credential", resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
+      case HttpStatusCode.Conflict: return 
opKnownFailure("cashout-address-changed", resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -494,10 +493,10 @@ export class TalerCoreBankHttpClient {
       method: "GET",
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForCashoutConversionResponse())
-      case HttpStatusCode.BadRequest: return knownFailure("wrong-calculation", 
resp);
-      case HttpStatusCode.NotFound: return knownFailure("not-supported", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForCashoutConversionResponse())
+      case HttpStatusCode.BadRequest: return 
opKnownFailure("wrong-calculation", resp);
+      case HttpStatusCode.NotFound: return opKnownFailure("not-supported", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -514,9 +513,9 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, codecForCashouts())
-      case HttpStatusCode.NoContent: return httpEmptySuccess();
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, codecForCashouts())
+      case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] });
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -533,9 +532,9 @@ export class TalerCoreBankHttpClient {
       },
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForGlobalCashouts())
-      case HttpStatusCode.NoContent: return httpEmptySuccess();
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, codecForGlobalCashouts())
+      case HttpStatusCode.NoContent: return opFixedSuccess({ cashouts: [] });
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -553,9 +552,9 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       //FIXME: missing in docs
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForCashoutStatusResponse())
-      case HttpStatusCode.NotFound: return knownFailure("already-aborted", 
resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForCashoutStatusResponse())
+      case HttpStatusCode.NotFound: return opKnownFailure("already-aborted", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -573,9 +572,9 @@ export class TalerCoreBankHttpClient {
       method: "GET",
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForConversionRatesResponse())
-      case HttpStatusCode.NotFound: return knownFailure("not-supported", resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForConversionRatesResponse())
+      case HttpStatusCode.NotFound: return opKnownFailure("not-supported", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -595,10 +594,10 @@ export class TalerCoreBankHttpClient {
       method: "GET",
     });
     switch (resp.status) {
-      case HttpStatusCode.Ok: return httpSuccess(resp, 
codecForMonitorResponse())
-      case HttpStatusCode.NotFound: return knownFailure("not-supported", resp);
-      case HttpStatusCode.BadRequest: return knownFailure("invalid-input", 
resp);
-      default: return unknownFailure(url, resp)
+      case HttpStatusCode.Ok: return opSuccess(resp, codecForMonitorResponse())
+      case HttpStatusCode.NotFound: return opKnownFailure("not-supported", 
resp);
+      case HttpStatusCode.BadRequest: return opKnownFailure("invalid-input", 
resp);
+      default: return opUnknownFailure(resp, await resp.text())
     }
   }
 
@@ -611,7 +610,7 @@ export class TalerCoreBankHttpClient {
    * 
    */
   getIntegrationAPI(): TalerBankIntegrationHttpClient {
-    const url = new URL(`taler-integration`, this.baseUrl);
+    const url = new URL(`taler-integration/`, this.baseUrl);
     return new TalerBankIntegrationHttpClient(url.href, this.httpLib)
   }
 
@@ -620,7 +619,7 @@ export class TalerCoreBankHttpClient {
    * 
    */
   getWireGatewayAPI(username: string): TalerWireGatewayHttpClient {
-    const url = new URL(`accounts/${username}/taler-wire-gateway`, 
this.baseUrl);
+    const url = new URL(`accounts/${username}/taler-wire-gateway/`, 
this.baseUrl);
     return new TalerWireGatewayHttpClient(url.href, username, this.httpLib)
   }
 
@@ -629,9 +628,16 @@ export class TalerCoreBankHttpClient {
   * 
   */
   getRevenueAPI(username: string): TalerRevenueHttpClient {
-    const url = new URL(`accounts/${username}/taler-revenue`, this.baseUrl);
+    const url = new URL(`accounts/${username}/taler-revenue/`, this.baseUrl);
     return new TalerRevenueHttpClient(url.href, username, this.httpLib,)
   }
 
+  /**
+   * 
https://docs.taler.net/core/api-corebank.html#post--accounts-$USERNAME-token
+  * 
+  */
+  getAuthenticationAPI(username: string): TalerAuthenticationHttpClient {
+    const url = new URL(`accounts/${username}/`, this.baseUrl);
+    return new TalerAuthenticationHttpClient(url.href, username, this.httpLib,)
+  }
 }
-
diff --git a/packages/taler-util/src/http-client/bank-integration.ts 
b/packages/taler-util/src/http-client/bank-integration.ts
index cd6462417..521b6e34c 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -10,7 +10,7 @@ export class TalerBankIntegrationHttpClient {
   httpLib: HttpRequestLibrary;
 
   constructor(
-    private baseUrl: string,
+    readonly baseUrl: string,
     httpClient?: HttpRequestLibrary,
   ) {
     this.httpLib = httpClient ?? createPlatformHttpLib();
diff --git a/packages/taler-util/src/http-client/bank-revenue.ts 
b/packages/taler-util/src/http-client/bank-revenue.ts
index 99ff71457..d594da574 100644
--- a/packages/taler-util/src/http-client/bank-revenue.ts
+++ b/packages/taler-util/src/http-client/bank-revenue.ts
@@ -1,14 +1,13 @@
 import { HttpRequestLibrary, makeBasicAuthHeader, 
readSuccessResponseJsonOrThrow } from "../http-common.js";
 import { createPlatformHttpLib } from "../http.js";
 import { TalerRevenueApi, codecForMerchantIncomingHistory } from "./types.js";
-import { UserAndPassword } from "./utils.js";
 
 export class TalerRevenueHttpClient {
   httpLib: HttpRequestLibrary;
 
   constructor(
-    private baseUrl: string,
-    private username: string,
+    readonly baseUrl: string,
+    readonly username: string,
     httpClient?: HttpRequestLibrary,
   ) {
     this.httpLib = httpClient ?? createPlatformHttpLib();
diff --git a/packages/taler-util/src/http-client/bank-wire.ts 
b/packages/taler-util/src/http-client/bank-wire.ts
index 9f2b859ed..0a032cc61 100644
--- a/packages/taler-util/src/http-client/bank-wire.ts
+++ b/packages/taler-util/src/http-client/bank-wire.ts
@@ -1,7 +1,7 @@
 import { HttpRequestLibrary, makeBasicAuthHeader, 
readSuccessResponseJsonOrThrow } from "../http-common.js";
 import { createPlatformHttpLib } from "../http.js";
-import { TalerWireGatewayApi, codecForAddIncomingResponse, 
codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } 
from "./types.js";
-import { PaginationParams, UserAndPassword, addPaginationParams } from 
"./utils.js";
+import { PaginationParams, TalerWireGatewayApi, codecForAddIncomingResponse, 
codecForIncomingHistory, codecForOutgoingHistory, codecForTransferResponse } 
from "./types.js";
+import { addPaginationParams } from "./utils.js";
 
 /**
  * The API is used by the exchange to trigger transactions and query 
@@ -14,8 +14,8 @@ export class TalerWireGatewayHttpClient {
   httpLib: HttpRequestLibrary;
 
   constructor(
-    private baseUrl: string,
-    private username: string,
+    readonly baseUrl: string,
+    readonly username: string,
     httpClient?: HttpRequestLibrary,
   ) {
     this.httpLib = httpClient ?? createPlatformHttpLib();
diff --git a/packages/taler-util/src/http-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 66ac39f59..5a76981df 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -1,6 +1,58 @@
 import { codecForAmountString } from "../amounts.js";
 import { Codec, buildCodecForObject, buildCodecForUnion, codecForBoolean, 
codecForConstString, codecForEither, codecForList, codecForMap, codecForNumber, 
codecForString, codecOptional } from "../codec.js";
 import { codecForTimestamp } from "../time.js";
+import { TalerErrorDetail } from "../wallet-types.js";
+
+
+export type UserAndPassword = {
+  username: string,
+  password: string,
+}
+
+export type UserAndToken = {
+  username: string,
+  token: AccessToken,
+}
+
+export type PaginationParams = {
+  /**
+   * row identifier as the starting point of the query
+   */
+  offset?: string,
+  /**
+   * max number of element in the result response
+   * always greater than 0
+   */
+  limit?: number,
+  /**
+   * milliseconds the server should wait for at least one result to be shown
+   */
+  timoutMs?: number,
+  /**
+   * order
+   */
+  order: "asc" | "dec"
+}
+
+export type OperationResult<Body, ErrorEnum> =
+  | OperationOk<Body>
+  | OperationFail<ErrorEnum>;
+
+export interface OperationOk<T> {
+  type: "ok",
+  body: T;
+}
+export function isOperationOk<T, E>(c: OperationResult<T, E>): c is 
OperationOk<T> {
+  return c.type === "ok"
+}
+export function isOperationFail<T, E>(c: OperationResult<T, E>): c is 
OperationFail<E> {
+  return c.type === "fail"
+}
+export interface OperationFail<T> {
+  type: "fail",
+  case: T,
+  detail: TalerErrorDetail,
+}
 
 
 ///
@@ -502,14 +554,14 @@ export const codecForAddIncomingResponse =
 
 type EmailAddress = string;
 type PhoneNumber = string;
-type DecimalNumber = string;
+type DecimalNumber = number;
 
 const codecForURL = codecForString
 const codecForLibtoolVersion = codecForString
 const codecForCurrencyName = codecForString
 const codecForPaytoURI = codecForString
 const codecForTalerWithdrawalURI = codecForString
-const codecForDecimalNumber = codecForString
+const codecForDecimalNumber = codecForNumber
 
 enum TanChannel {
   SMS = "sms",
diff --git a/packages/taler-util/src/http-client/utils.ts 
b/packages/taler-util/src/http-client/utils.ts
index f4af5ae03..3be5d6e89 100644
--- a/packages/taler-util/src/http-client/utils.ts
+++ b/packages/taler-util/src/http-client/utils.ts
@@ -1,10 +1,8 @@
 import { base64FromArrayBuffer } from "../base64.js";
-import { HttpResponse, readErrorResponse, readSuccessResponseJsonOrThrow, 
readTalerErrorResponse } from "../http-common.js";
-import { HttpStatusCode } from "../http-status-codes.js";
-import { Codec } from "../index.js";
+import { HttpResponse, readSuccessResponseJsonOrThrow, readTalerErrorResponse 
} from "../http-common.js";
+import { Codec, TalerError, TalerErrorCode } from "../index.js";
 import { stringToBytes } from "../taler-crypto.js";
-import { TalerErrorDetail } from "../wallet-types.js";
-import { AccessToken } from "./types.js";
+import { AccessToken, OperationFail, OperationOk, PaginationParams } from 
"./types.js";
 
 /**
  * Helper function to generate the "Authorization" HTTP header.
@@ -41,91 +39,33 @@ export function addPaginationParams(url: URL, pagination?: 
PaginationParams) {
   url.searchParams.set("delta", String(order * limit))
 }
 
-export type UserAndPassword = {
-  username: string,
-  password: string,
-}
-
-export type UserAndToken = {
-  username: string,
-  token: AccessToken,
-}
 
-export type PaginationParams = {
-  /**
-   * row identifier as the starting point of the query
-   */
-  offset?: string,
-  /**
-   * max number of element in the result response
-   * always greater than 0
-   */
-  limit?: number,
-  /**
-   * milliseconds the server should wait for at least one result to be shown
-   */
-  timoutMs?: number,
-  /**
-   * order
-   */
-  order: "asc" | "dec"
-}
-
-export type HttpResult<Body, ErrorEnum> =
-  | HttpOk<Body>
-  | HttpKnownFail<ErrorEnum>
-  | HttpUnkownFail;
-
-/**
- * 200 < status < 204 
- */
-export interface HttpOk<T> {
-  type: "ok",
-  body: T;
+//////
+// Operation Helper Constructors
+//////
+export async function opSuccess<T>(resp: HttpResponse, codec: Codec<T>): 
Promise<OperationOk<T>> {
+  const body = await readSuccessResponseJsonOrThrow(resp, codec)
+  return { type: "ok" as const, body }
 }
-
-/**
- * 400 < status < 409
- * and error documented 
- */
-export interface HttpKnownFail<T> {
-  type: "fail",
-  case: T,
-  detail: TalerErrorDetail,
+export function opFixedSuccess<T>(body: T): OperationOk<T> {
+  return { type: "ok" as const, body }
 }
-
-/**
- * 400 < status < 599
- * and error NOT documented 
- * undefined behavior on this responses
- */
-export interface HttpUnkownFail {
-  type: "fail-unknown",
-  url: URL;
-  status: HttpStatusCode;
-
-  // read from the body if exist
-  detail?: TalerErrorDetail;
-  body?: string;
+export function opEmptySuccess(): OperationOk<void> {
+  return { type: "ok" as const, body: void 0 }
 }
-
-export async function knownFailure<T extends string>(s: T, resp: 
HttpResponse): Promise<HttpKnownFail<T>> {
+export async function opKnownFailure<T extends string>(s: T, resp: 
HttpResponse): Promise<OperationFail<T>> {
   const detail = await readTalerErrorResponse(resp)
   return { type: "fail", case: s, detail }
 }
-export async function httpSuccess<T>(resp: HttpResponse, codec: Codec<T>): 
Promise<HttpOk<T>> {
-  const body = await readSuccessResponseJsonOrThrow(resp, codec)
-  return { type: "ok" as const, body }
-}
-export function httpEmptySuccess(): HttpOk<void> {
-  return { type: "ok" as const, body: void 0 }
-}
-export async function unknownFailure(url: URL, resp: HttpResponse): 
Promise<HttpUnkownFail> {
-  if (resp.status >= 400 && resp.status < 500) {
-    const detail = await readTalerErrorResponse(resp)
-    return { type: "fail-unknown", url, status: resp.status, detail }
-  } else {
-    const { detail, body } = await readErrorResponse(resp)
-    return { type: "fail-unknown", url, status: resp.status, detail, body }
-  }
+export function opUnknownFailure(resp: HttpResponse, text: string): never {
+  throw TalerError.fromDetail(
+    TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
+    {
+      requestUrl: resp.requestUrl,
+      requestMethod: resp.requestMethod,
+      httpStatusCode: resp.status,
+      errorResponse: text,
+    },
+    `Unexpected HTTP status ${resp.status} in response`,
+  );
 }
diff --git a/packages/taler-util/src/http-common.ts 
b/packages/taler-util/src/http-common.ts
index 817f2367f..da2fbb9da 100644
--- a/packages/taler-util/src/http-common.ts
+++ b/packages/taler-util/src/http-common.ts
@@ -180,37 +180,6 @@ export async function readTalerErrorResponse(
   return errJson;
 }
 
-export async function readErrorResponse(
-  httpResponse: HttpResponse,
-): Promise<{ detail: TalerErrorDetail | undefined, body: string }> {
-  let errString: string;
-  try {
-    errString = await httpResponse.text();
-  } catch (e: any) {
-    throw TalerError.fromDetail(
-      TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
-      {
-        requestUrl: httpResponse.requestUrl,
-        requestMethod: httpResponse.requestMethod,
-        httpStatusCode: httpResponse.status,
-        validationError: e.toString(),
-      },
-      "Couldn't parse JSON format from error response",
-    );
-  }
-  let errJson;
-  try {
-    errJson = JSON.parse(errString)
-  } catch (e) {
-    errJson = undefined
-  }
-
-  const talerErrorCode = errJson && errJson.code;
-  if (typeof talerErrorCode === "number") {
-    return { detail: errJson, body: errString }
-  }
-  return { detail: undefined, body: errString };
-}
 export async function readUnexpectedResponseDetails(
   httpResponse: HttpResponse,
 ): Promise<TalerErrorDetail> {
diff --git a/packages/web-util/src/context/api.ts 
b/packages/web-util/src/context/api.ts
index 81586bd35..23b7e77af 100644
--- a/packages/web-util/src/context/api.ts
+++ b/packages/web-util/src/context/api.ts
@@ -19,17 +19,20 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { TalerBankIntegrationHttpClient, TalerCoreBankHttpClient, 
TalerRevenueHttpClient, TalerWireGatewayHttpClient } from 
"@gnu-taler/taler-util";
 import { ComponentChildren, createContext, h, VNode } from "preact";
 import { useContext } from "preact/hooks";
 import { defaultRequestHandler } from "../utils/request.js";
 
 interface Type {
   request: typeof defaultRequestHandler;
+  bankCore: TalerCoreBankHttpClient,
+  bankIntegration: TalerBankIntegrationHttpClient,
+  bankWire: TalerWireGatewayHttpClient,
+  bankRevenue: TalerRevenueHttpClient,
 }
 
-const Context = createContext<Type>({
-  request: defaultRequestHandler,
-});
+const Context = createContext<Type>(undefined as any);
 
 export const useApiContext = (): Type => useContext(Context);
 export const ApiContextProvider = ({
diff --git a/packages/web-util/src/hooks/useNotifications.ts 
b/packages/web-util/src/hooks/useNotifications.ts
index e9e8a240b..8f9e0e835 100644
--- a/packages/web-util/src/hooks/useNotifications.ts
+++ b/packages/web-util/src/hooks/useNotifications.ts
@@ -8,7 +8,7 @@ export interface ErrorNotification {
   type: "error";
   title: TranslatedString;
   description?: TranslatedString;
-  debug?: string;
+  debug?: any;
 }
 export interface InfoNotification {
   type: "info";
diff --git a/packages/web-util/src/utils/http-impl.browser.ts 
b/packages/web-util/src/utils/http-impl.browser.ts
index 598146149..f19d1fe61 100644
--- a/packages/web-util/src/utils/http-impl.browser.ts
+++ b/packages/web-util/src/utils/http-impl.browser.ts
@@ -104,7 +104,11 @@ export class BrowserHttpLib implements HttpRequestLibrary {
           reject(
             TalerError.fromDetail(
               TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT,
-              {},
+              {
+                requestUrl,
+                requestMethod,
+                timeoutMs: requestTimeout.d_ms === "forever" ? 0 : 
requestTimeout.d_ms
+              },
               `request to ${requestUrl} timed out`,
             ),
           );
diff --git a/packages/web-util/src/utils/http-impl.sw.ts 
b/packages/web-util/src/utils/http-impl.sw.ts
index 05696105c..aab88021f 100644
--- a/packages/web-util/src/utils/http-impl.sw.ts
+++ b/packages/web-util/src/utils/http-impl.sw.ts
@@ -110,7 +110,11 @@ export class ServiceWorkerHttpLib implements 
HttpRequestLibrary {
       if (controller.signal) {
         throw TalerError.fromDetail(
           TalerErrorCode.WALLET_HTTP_REQUEST_GENERIC_TIMEOUT,
-          {},
+          {
+            requestUrl,
+            requestMethod,
+            timeoutMs: requestTimeout.d_ms === "forever" ? 0 : 
requestTimeout.d_ms
+          },
           `request to ${requestUrl} timed out`,
         );
       }

-- 
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]