gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 05/05: integrate bank into the new taler-util API


From: gnunet
Subject: [taler-wallet-core] 05/05: integrate bank into the new taler-util API
Date: Thu, 19 Oct 2023 07:56:27 +0200

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

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

commit 366cccb8fcae6a9971a1e8a9143d821e289339d1
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Oct 19 02:55:57 2023 -0300

    integrate bank into the new taler-util API
---
 .../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 +--
 42 files changed, 1559 insertions(+), 2738 deletions(-)

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;
     }

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