gnunet-svn
[Top][All Lists]
Advanced

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

[taler-merchant] branch master updated: new CRUD APIs for OTP devices an


From: gnunet
Subject: [taler-merchant] branch master updated: new CRUD APIs for OTP devices and merchant accounts (fixes #7929, #7824), one minor test is still failing...
Date: Fri, 01 Sep 2023 14:27:52 +0200

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

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new 35dcd451 new CRUD APIs for OTP devices and merchant accounts (fixes 
#7929, #7824), one minor test is still failing...
35dcd451 is described below

commit 35dcd4514a93ba0f5353ecd1194fc9b515f2aad4
Author: Christian Grothoff <grothoff@gnunet.org>
AuthorDate: Fri Sep 1 14:27:48 2023 +0200

    new CRUD APIs for OTP devices and merchant accounts (fixes #7929, #7824), 
one minor test is still failing...
---
 ...er-merchant-httpd_private-patch-templates-ID.c# | 197 ----
 src/backend/Makefile.am                            |  24 +-
 src/backend/taler-merchant-httpd.c                 | 138 ++-
 .../taler-merchant-httpd_post-using-templates.c    |  11 +-
 ...-merchant-httpd_private-delete-otp-devices-ID.c |  78 ++
 ...-merchant-httpd_private-delete-otp-devices-ID.h |  41 +
 .../taler-merchant-httpd_private-get-accounts-ID.c | 103 +++
 .../taler-merchant-httpd_private-get-accounts-ID.h |  41 +
 .../taler-merchant-httpd_private-get-accounts.c    |  78 ++
 .../taler-merchant-httpd_private-get-accounts.h    |  41 +
 ...er-merchant-httpd_private-get-otp-devices-ID.c} |  53 +-
 ...ler-merchant-httpd_private-get-otp-devices-ID.h |  41 +
 .../taler-merchant-httpd_private-get-otp-devices.c |  80 ++
 .../taler-merchant-httpd_private-get-otp-devices.h |  41 +
 ...taler-merchant-httpd_private-get-templates-ID.c |   9 +-
 ...aler-merchant-httpd_private-patch-accounts-ID.c | 132 +++
 ...aler-merchant-httpd_private-patch-accounts-ID.h |  43 +
 ...ler-merchant-httpd_private-patch-instances-ID.c | 274 ------
 ...r-merchant-httpd_private-patch-otp-devices-ID.c | 122 +++
 ...r-merchant-httpd_private-patch-otp-devices-ID.h |  44 +
 ...ler-merchant-httpd_private-patch-templates-ID.c |  16 +-
 .../taler-merchant-httpd_private-post-account.c    |  13 +-
 .../taler-merchant-httpd_private-post-instances.c  | 149 +---
 .../taler-merchant-httpd_private-post-orders.c     |  69 +-
 .../taler-merchant-httpd_private-post-orders.h     |  20 -
 ...aler-merchant-httpd_private-post-otp-devices.c} | 117 ++-
 ...taler-merchant-httpd_private-post-otp-devices.h |  44 +
 .../taler-merchant-httpd_private-post-templates.c  | 171 ++--
 src/backenddb/Makefile.am                          |  13 +
 src/backenddb/merchant-0001.sql                    |  35 +-
 src/backenddb/merchantdb_helper.c                  |   2 +-
 .../{pg_inactivate_account.c => pg_delete_otp.c}   |  40 +-
 .../{pg_update_account.h => pg_delete_otp.h}       |  27 +-
 ...g_inactivate_account.c => pg_delete_template.c} |  40 +-
 .../{pg_update_account.h => pg_delete_template.h}  |  26 +-
 src/backenddb/pg_inactivate_account.c              |   2 -
 src/backenddb/pg_insert_otp.c                      |  74 ++
 .../{pg_update_account.h => pg_insert_otp.h}       |  25 +-
 src/backenddb/pg_insert_template.c                 |  66 ++
 .../{pg_update_account.h => pg_insert_template.h}  |  28 +-
 src/backenddb/pg_lookup_instances.c                | 121 +--
 src/backenddb/pg_lookup_otp_devices.c              | 133 +++
 ...pg_update_account.h => pg_lookup_otp_devices.h} |  25 +-
 src/backenddb/pg_lookup_template.c                 |  98 ++
 .../{pg_update_account.h => pg_lookup_template.h}  |  26 +-
 src/backenddb/pg_lookup_templates.c                | 135 +++
 .../{pg_update_account.h => pg_lookup_templates.h} |  26 +-
 src/backenddb/pg_select_account.c                  |  79 ++
 .../{pg_update_account.h => pg_select_account.h}   |  24 +-
 src/backenddb/pg_select_accounts.c                 | 158 ++++
 .../{pg_update_account.h => pg_select_accounts.h}  |  24 +-
 src/backenddb/pg_select_otp.c                      |  91 ++
 .../{pg_update_account.h => pg_select_otp.h}       |  27 +-
 src/backenddb/pg_select_otp_serial.c               |  61 ++
 ...{pg_update_account.h => pg_select_otp_serial.h} |  27 +-
 src/backenddb/pg_update_account.c                  |  16 +-
 src/backenddb/pg_update_account.h                  |   8 +-
 src/backenddb/pg_update_instance.c                 |   4 +-
 src/backenddb/pg_update_otp.c                      |  76 ++
 .../{pg_update_account.h => pg_update_otp.h}       |  29 +-
 src/backenddb/pg_update_template.c                 |  85 ++
 .../{pg_update_account.h => pg_update_template.h}  |  30 +-
 src/backenddb/plugin_merchantdb_postgres.c         | 377 +-------
 src/backenddb/test_merchantdb.c                    |  78 +-
 src/include/taler_merchant_service.h               | 993 ++++++++++++++++-----
 src/include/taler_merchant_testing_lib.h           | 196 ++--
 src/include/taler_merchantdb_plugin.h              | 210 ++++-
 src/lib/Makefile.am                                |   8 +
 src/lib/merchant_api_delete_otp_device.c           | 184 ++++
 ...i_get_template.c => merchant_api_get_account.c} |  79 +-
 ..._get_template.c => merchant_api_get_accounts.c} | 144 +--
 src/lib/merchant_api_get_instance.c                |  92 +-
 ...et_template.c => merchant_api_get_otp_device.c} |  68 +-
 ...t_template.c => merchant_api_get_otp_devices.c} | 144 +--
 src/lib/merchant_api_get_template.c                |   9 +-
 ...tch_template.c => merchant_api_patch_account.c} |  72 +-
 src/lib/merchant_api_patch_instance.c              |  40 -
 ..._template.c => merchant_api_patch_otp_device.c} |  72 +-
 src/lib/merchant_api_patch_template.c              |   9 +-
 src/lib/merchant_api_post_account.c                |  62 +-
 src/lib/merchant_api_post_instances.c              |  40 -
 ..._template.c => merchant_api_post_otp_devices.c} | 115 ++-
 src/lib/merchant_api_post_templates.c              |   9 +-
 src/merchant-tools/taler-merchant-benchmark.c      |  22 +-
 src/testing/Makefile.am                            |  13 +-
 src/testing/test_kyc_api.c                         |   7 +-
 src/testing/test_merchant_api.c                    | 125 ++-
 .../exchange-secmod-cs/keys/coin_eur_1/1692810704  |   1 -
 .../exchange-secmod-cs/keys/coin_eur_5/1692810704  |   2 -
 .../keys/coin_eur_ct_1/1692810704                  |   1 -
 .../keys/coin_eur_ct_10/1692810704                 |   1 -
 src/testing/test_merchant_api_twisted.c            |   7 +-
 src/testing/test_merchant_instance_auth.sh         |  17 +-
 src/testing/test_merchant_instance_creation.sh     |   6 +-
 src/testing/test_merchant_instance_purge.sh        |   4 +-
 src/testing/test_merchant_instance_response.sh     |   4 +-
 src/testing/test_merchant_kyc.sh                   |  28 +-
 src/testing/test_merchant_order_autocleanup.sh     |  14 +-
 src/testing/test_merchant_order_creation.sh        |  57 +-
 src/testing/test_merchant_product_creation.sh      |  14 +-
 src/testing/test_merchant_reserve_creation.sh      |  16 +-
 src/testing/test_merchant_transfer_tracking.sh     |  46 +-
 src/testing/test_merchant_wirewatch.sh             |  15 +-
 src/testing/testing_api_cmd_delete_account.c       |  56 +-
 src/testing/testing_api_cmd_delete_otp_device.c    | 181 ++++
 src/testing/testing_api_cmd_get_instance.c         | 229 +----
 src/testing/testing_api_cmd_get_otp_device.c       | 227 +++++
 src/testing/testing_api_cmd_get_otp_devices.c      | 238 +++++
 src/testing/testing_api_cmd_get_template.c         |  20 +-
 src/testing/testing_api_cmd_patch_instance.c       |  69 +-
 ...mplate.c => testing_api_cmd_patch_otp_device.c} | 132 ++-
 src/testing/testing_api_cmd_patch_template.c       |  23 +-
 src/testing/testing_api_cmd_pay_order.c            |  15 +-
 src/testing/testing_api_cmd_post_account.c         |  55 +-
 src/testing/testing_api_cmd_post_instances.c       |  73 +-
 ...plates.c => testing_api_cmd_post_otp_devices.c} | 147 ++-
 src/testing/testing_api_cmd_post_templates.c       |  24 +-
 src/testing/testing_api_cmd_post_using_templates.c |  38 +-
 118 files changed, 5656 insertions(+), 3143 deletions(-)

diff --git a/src/backend/#taler-merchant-httpd_private-patch-templates-ID.c# 
b/src/backend/#taler-merchant-httpd_private-patch-templates-ID.c#
deleted file mode 100644
index 6739c7a9..00000000
--- a/src/backend/#taler-merchant-httpd_private-patch-templates-ID.c#
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
-  This file is part of TALER
-  (C) 2022 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify
-  it under the terms of the GNU Affero General Public License as
-  published by the Free Software Foundation; either version 3,
-  or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public
-  License along with TALER; see the file COPYING.  If not,
-  see <http://www.gnu.org/licenses/>
-*/
-
-/**
- * @file taler-merchant-httpd_private-patch-templates-ID.c
- * @brief implementing PATCH /templates/$ID request handling
- * @author Priscilla HUANG
- */
-#include "platform.h"
-#include "taler-merchant-httpd_private-patch-templates-ID.h"
-#include "taler-merchant-httpd_helper.h"
-#include <taler/taler_json_lib.h>
-
-
-/**
- * How often do we retry the simple INSERT database transaction?
- */
-#define MAX_RETRIES 3
-
-
-/**                    
- * Determine the cause of the PATCH failure in more detail and report.
- *
- * @param connection connection to report on
- * @param instance_id instance we are processing
- * @param template_id ID of the product to patch
- * @param tp template details we failed to set
- */
-static MHD_RESULT
-determine_cause (struct MHD_Connection *connection,
-                 const char *instance_id,
-                 const char *template_id,
-                 const struct TALER_MERCHANTDB_TemplateDetails *tp)
-{
-  struct TALER_MERCHANTDB_TemplateDetails tpx;
-  enum GNUNET_DB_QueryStatus qs;
-
-  qs = TMH_db->lookup_template (TMH_db->cls,
-                                instance_id,
-                                template_id,
-                                &tpx);
-  switch (qs)
-  {
-  case GNUNET_DB_STATUS_HARD_ERROR:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       NULL);
-  case GNUNET_DB_STATUS_SOFT_ERROR:
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                                       "unexpected serialization problem");
-  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_NOT_FOUND,
-                                       
TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
-                                       template_id);
-  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-    break; /* do below */
-  }
-
-  {
-    enum TALER_ErrorCode ec;
-
-    ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
-    TALER_MERCHANTDB_template_details_free (&tpx);
-    GNUNET_break (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE != ec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_CONFLICT,
-                                       ec,
-                                       NULL);
-  }
-}
-
-
-/**
- * PATCH configuration of an existing instance, given its configuration.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] hc context with further information about the request
- * @return MHD result code
- */
-MHD_RESULT
-TMH_private_patch_templates_ID (const struct TMH_RequestHandler *rh,
-                                struct MHD_Connection *connection,
-                                struct TMH_HandlerContext *hc)
-{
-  struct TMH_MerchantInstance *mi = hc->instance;
-  const char *template_id = hc->infix;
-  struct TALER_MERCHANTDB_TemplateDetails tp = {0};
-  enum GNUNET_DB_QueryStatus qs;
-  uint32_t pos_algorithm;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_string ("template_description",
-                             (const char **) &tp.template_description),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                               &pos_algorithm),
-     0),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("pos_key",
-                               (const char **) &tp.pos_key),
-      NULL),
-    GNUNET_JSON_spec_json ("template_contract",
-                           &tp.template_contract),
-    GNUNET_JSON_spec_end ()
-  };
-
-  GNUNET_assert (NULL != mi);
-  GNUNET_assert (NULL != template_id);
-  {
-    enum GNUNET_GenericReturnValue res;
-
-    res = TALER_MHD_parse_json_data (connection,
-                                     hc->request_body,
-                                     spec);
-    if (GNUNET_OK != res)
-      return (GNUNET_NO == res)
-             ? MHD_YES
-             : MHD_NO;
-  }
-
-  tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm;
-  if (! TMH_template_contract_valid (tp.template_contract))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                       "template_contract");
-  }
-
-  qs = TMH_db->update_template (TMH_db->cls,
-                                mi->settings.id,
-                                template_id,
-                                &tp);
-  {
-    MHD_RESULT ret = MHD_NO;
-
-    switch (qs)
-    {
-    case GNUNET_DB_STATUS_HARD_ERROR:
-      GNUNET_break (0);
-      ret = TALER_MHD_reply_with_error (connection,
-                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                        TALER_EC_GENERIC_DB_STORE_FAILED,
-                                        NULL);
-      break;
-    case GNUNET_DB_STATUS_SOFT_ERROR:
-      GNUNET_break (0);
-      ret = TALER_MHD_reply_with_error (connection,
-                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                        
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
-                                        "unexpected serialization problem");
-      break;
-    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-      ret = determine_cause (connection,
-                             mi->settings.id,
-                             template_id,
-                             &tp);
-      break;
-    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-      ret = TALER_MHD_reply_static (connection,
-                                    MHD_HTTP_NO_CONTENT,
-                                    NULL,
-                                    NULL,
-                                    0);
-      break;
-    }
-    GNUNET_JSON_parse_free (spec);
-    return ret;
-  }
-}
-
-
-/* end of taler-merchant-httpd_private-patch-templates-ID.c */
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index fed9c0a2..ef5b67bb 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -44,6 +44,8 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_private-delete-products-ID.h \
   taler-merchant-httpd_private-delete-orders-ID.c \
     taler-merchant-httpd_private-delete-orders-ID.h \
+  taler-merchant-httpd_private-delete-otp-devices-ID.c \
+    taler-merchant-httpd_private-delete-otp-devices-ID.h \
   taler-merchant-httpd_private-delete-reserves-ID.c \
     taler-merchant-httpd_private-delete-reserves-ID.h \
   taler-merchant-httpd_private-delete-templates-ID.c \
@@ -52,6 +54,10 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_private-delete-transfers-ID.h \
   taler-merchant-httpd_private-delete-webhooks-ID.c \
     taler-merchant-httpd_private-delete-webhooks-ID.h \
+  taler-merchant-httpd_private-get-accounts.c \
+    taler-merchant-httpd_private-get-accounts.h \
+  taler-merchant-httpd_private-get-accounts-ID.c \
+    taler-merchant-httpd_private-get-accounts-ID.h \
   taler-merchant-httpd_private-get-instances.c \
     taler-merchant-httpd_private-get-instances.h \
   taler-merchant-httpd_private-get-instances-ID.c \
@@ -66,6 +72,10 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_private-get-orders.h \
   taler-merchant-httpd_private-get-orders-ID.c \
     taler-merchant-httpd_private-get-orders-ID.h \
+  taler-merchant-httpd_private-get-otp-devices.c \
+    taler-merchant-httpd_private-get-otp-devices.h \
+  taler-merchant-httpd_private-get-otp-devices-ID.c \
+    taler-merchant-httpd_private-get-otp-devices-ID.h \
   taler-merchant-httpd_private-get-reserves.c \
     taler-merchant-httpd_private-get-reserves.h \
   taler-merchant-httpd_private-get-reserves-ID.c \
@@ -80,16 +90,20 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_private-get-webhooks.h \
   taler-merchant-httpd_private-get-webhooks-ID.c \
     taler-merchant-httpd_private-get-webhooks-ID.h \
-  taler-merchant-httpd_private-patch-templates-ID.c \
-    taler-merchant-httpd_private-patch-templates-ID.h \
-  taler-merchant-httpd_private-patch-webhooks-ID.c \
-    taler-merchant-httpd_private-patch-webhooks-ID.h \
+  taler-merchant-httpd_private-patch-accounts-ID.c \
+    taler-merchant-httpd_private-patch-accounts-ID.h \
   taler-merchant-httpd_private-patch-instances-ID.c \
     taler-merchant-httpd_private-patch-instances-ID.h \
   taler-merchant-httpd_private-patch-orders-ID-forget.c \
     taler-merchant-httpd_private-patch-orders-ID-forget.h \
+  taler-merchant-httpd_private-patch-otp-devices-ID.c \
+    taler-merchant-httpd_private-patch-otp-devices-ID.h \
   taler-merchant-httpd_private-patch-products-ID.c \
     taler-merchant-httpd_private-patch-products-ID.h \
+  taler-merchant-httpd_private-patch-templates-ID.c \
+    taler-merchant-httpd_private-patch-templates-ID.h \
+  taler-merchant-httpd_private-patch-webhooks-ID.c \
+    taler-merchant-httpd_private-patch-webhooks-ID.h \
   taler-merchant-httpd_private-post-account.c \
     taler-merchant-httpd_private-post-account.h \
   taler-merchant-httpd_private-post-instances.c \
@@ -102,6 +116,8 @@ taler_merchant_httpd_SOURCES = \
     taler-merchant-httpd_private-post-orders.h \
   taler-merchant-httpd_private-post-products.c \
     taler-merchant-httpd_private-post-products.h \
+  taler-merchant-httpd_private-post-otp-devices.c \
+    taler-merchant-httpd_private-post-otp-devices.h \
   taler-merchant-httpd_private-post-products-ID-lock.c \
     taler-merchant-httpd_private-post-products-ID-lock.h \
   taler-merchant-httpd_private-post-reserves.c \
diff --git a/src/backend/taler-merchant-httpd.c 
b/src/backend/taler-merchant-httpd.c
index d8be1edc..9c543958 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -37,14 +37,13 @@
 #include "taler-merchant-httpd_private-delete-instances-ID.h"
 #include "taler-merchant-httpd_private-delete-products-ID.h"
 #include "taler-merchant-httpd_private-delete-orders-ID.h"
+#include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
 #include "taler-merchant-httpd_private-delete-reserves-ID.h"
 #include "taler-merchant-httpd_private-delete-templates-ID.h"
 #include "taler-merchant-httpd_private-delete-transfers-ID.h"
 #include "taler-merchant-httpd_private-delete-webhooks-ID.h"
-#include "taler-merchant-httpd_private-get-webhooks.h"
-#include "taler-merchant-httpd_private-get-webhooks-ID.h"
-#include "taler-merchant-httpd_private-get-templates.h"
-#include "taler-merchant-httpd_private-get-templates-ID.h"
+#include "taler-merchant-httpd_private-get-accounts.h"
+#include "taler-merchant-httpd_private-get-accounts-ID.h"
 #include "taler-merchant-httpd_private-get-instances.h"
 #include "taler-merchant-httpd_private-get-instances-ID.h"
 #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
@@ -52,19 +51,28 @@
 #include "taler-merchant-httpd_private-get-products-ID.h"
 #include "taler-merchant-httpd_private-get-orders.h"
 #include "taler-merchant-httpd_private-get-orders-ID.h"
+#include "taler-merchant-httpd_private-get-otp-devices.h"
+#include "taler-merchant-httpd_private-get-otp-devices-ID.h"
 #include "taler-merchant-httpd_private-get-reserves.h"
 #include "taler-merchant-httpd_private-get-reserves-ID.h"
 #include "taler-merchant-httpd_private-get-rewards-ID.h"
 #include "taler-merchant-httpd_private-get-rewards.h"
+#include "taler-merchant-httpd_private-get-templates.h"
+#include "taler-merchant-httpd_private-get-templates-ID.h"
 #include "taler-merchant-httpd_private-get-transfers.h"
-#include "taler-merchant-httpd_private-patch-webhooks-ID.h"
-#include "taler-merchant-httpd_private-patch-templates-ID.h"
+#include "taler-merchant-httpd_private-get-webhooks.h"
+#include "taler-merchant-httpd_private-get-webhooks-ID.h"
+#include "taler-merchant-httpd_private-patch-accounts-ID.h"
 #include "taler-merchant-httpd_private-patch-instances-ID.h"
 #include "taler-merchant-httpd_private-patch-orders-ID-forget.h"
+#include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
 #include "taler-merchant-httpd_private-patch-products-ID.h"
+#include "taler-merchant-httpd_private-patch-templates-ID.h"
+#include "taler-merchant-httpd_private-patch-webhooks-ID.h"
 #include "taler-merchant-httpd_private-post-account.h"
 #include "taler-merchant-httpd_private-post-instances.h"
 #include "taler-merchant-httpd_private-post-instances-ID-auth.h"
+#include "taler-merchant-httpd_private-post-otp-devices.h"
 #include "taler-merchant-httpd_private-post-orders.h"
 #include "taler-merchant-httpd_private-post-orders-ID-refund.h"
 #include "taler-merchant-httpd_private-post-products.h"
@@ -1041,6 +1049,39 @@ url_handler (void *cls,
       .allow_deleted_instance = true,
       .handler = &TMH_private_get_transfers
     },
+    /* POST /otp-devices: */
+    {
+      .url_prefix = "/otp-devices",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler = &TMH_private_post_otp_devices
+    },
+    /* GET /otp-devices: */
+    {
+      .url_prefix = "/otp-devices",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler = &TMH_private_get_otp_devices
+    },
+    /* GET /otp-devices/$ID/: */
+    {
+      .url_prefix = "/otp-devices/",
+      .method = MHD_HTTP_METHOD_GET,
+      .have_id_segment = true,
+      .handler = &TMH_private_get_otp_devices_ID
+    },
+    /* DELETE /otp-devices/$ID/: */
+    {
+      .url_prefix = "/otp-devices/",
+      .method = MHD_HTTP_METHOD_DELETE,
+      .have_id_segment = true,
+      .handler = &TMH_private_delete_otp_devices_ID
+    },
+    /* PATCH /otp-devices/$ID/: */
+    {
+      .url_prefix = "/otp-devices/",
+      .method = MHD_HTTP_METHOD_PATCH,
+      .have_id_segment = true,
+      .handler = &TMH_private_patch_otp_devices_ID
+    },
     /* POST /templates: */
     {
       .url_prefix = "/templates",
@@ -1133,17 +1174,39 @@ url_handler (void *cls,
          in the code... */
       .max_upload = 1024 * 1024 * 8
     },
-    /* POST /account: */
+    /* POST /accounts: */
     {
-      .url_prefix = "/account",
+      .url_prefix = "/accounts",
       .method = MHD_HTTP_METHOD_POST,
       .handler = &TMH_private_post_account,
       /* allow account details of up to 8 kb, that should be plenty */
       .max_upload = 1024 * 8
     },
-    /* DELETE /account/$PAYTO: */
+    /* PATCH /accounts/$H_WIRE: */
     {
-      .url_prefix = "/account",
+      .url_prefix = "/accounts",
+      .method = MHD_HTTP_METHOD_PATCH,
+      .handler = &TMH_private_patch_accounts_ID,
+      .have_id_segment = true,
+      /* allow account details of up to 8 kb, that should be plenty */
+      .max_upload = 1024 * 8
+    },
+    /* GET /accounts: */
+    {
+      .url_prefix = "/accounts",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler = &TMH_private_get_accounts
+    },
+    /* GET /accounts/$H_WIRE: */
+    {
+      .url_prefix = "/accounts",
+      .method = MHD_HTTP_METHOD_GET,
+      .have_id_segment = true,
+      .handler = &TMH_private_get_accounts_ID
+    },
+    /* DELETE /accounts/$H_WIRE: */
+    {
+      .url_prefix = "/accounts",
       .method = MHD_HTTP_METHOD_DELETE,
       .handler = &TMH_private_delete_account_ID,
       .have_id_segment = true
@@ -1778,6 +1841,31 @@ url_handler (void *cls,
 }
 
 
+/**
+ * Callback invoked with information about a bank account.
+ *
+ * @param cls closure with a `struct TMH_MerchantInstance *`
+ * @param ad details about the account
+ */
+static void
+add_account_cb (void *cls,
+                const struct TALER_MERCHANTDB_AccountDetails *acc)
+{
+  struct TMH_MerchantInstance *mi = cls;
+  struct TMH_WireMethod *wm;
+
+  wm = GNUNET_new (struct TMH_WireMethod);
+  wm->h_wire = acc->h_wire;
+  wm->payto_uri = GNUNET_strdup (acc->payto_uri);
+  wm->wire_salt = acc->salt;
+  wm->wire_method = TALER_payto_get_method (acc->payto_uri);
+  wm->active = acc->active;
+  GNUNET_CONTAINER_DLL_insert (mi->wm_head,
+                               mi->wm_tail,
+                               wm);
+}
+
+
 /**
  * Function called during startup to add all known instances to our
  * hash map in memory for faster lookups when we receive requests.
@@ -1787,20 +1875,17 @@ url_handler (void *cls,
  * @param merchant_priv private key of the instance, NULL if not available
  * @param is detailed configuration settings for the instance
  * @param ias authentication settings for the instance
- * @param accounts_length length of the @a accounts array
- * @param accounts list of accounts of the merchant
  */
 static void
 add_instance_cb (void *cls,
                  const struct TALER_MerchantPublicKeyP *merchant_pub,
                  const struct TALER_MerchantPrivateKeyP *merchant_priv,
                  const struct TALER_MERCHANTDB_InstanceSettings *is,
-                 const struct TALER_MERCHANTDB_InstanceAuthSettings *ias,
-                 unsigned int accounts_length,
-                 const struct TALER_MERCHANTDB_AccountDetails accounts[])
+                 const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
 {
   struct TMH_MerchantInstance *mi;
-
+  enum GNUNET_DB_QueryStatus qs;
+  
   (void) cls;
   mi = TMH_lookup_instance (is->id);
   if (NULL != mi)
@@ -1829,20 +1914,15 @@ add_instance_cb (void *cls,
   else
     mi->deleted = true;
   mi->merchant_pub = *merchant_pub;
-  for (unsigned int i = 0; i<accounts_length; i++)
+  qs = TMH_db->select_accounts (TMH_db->cls,
+                                mi->settings.id,
+                                &add_account_cb,
+                                mi);
+  if (0 > qs)
   {
-    const struct TALER_MERCHANTDB_AccountDetails *acc = &accounts[i];
-    struct TMH_WireMethod *wm;
-
-    wm = GNUNET_new (struct TMH_WireMethod);
-    wm->h_wire = acc->h_wire;
-    wm->payto_uri = GNUNET_strdup (acc->payto_uri);
-    wm->wire_salt = acc->salt;
-    wm->wire_method = TALER_payto_get_method (acc->payto_uri);
-    wm->active = acc->active;
-    GNUNET_CONTAINER_DLL_insert (mi->wm_head,
-                                 mi->wm_tail,
-                                 wm);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Error loading accounts of `%s' from database\n",
+                mi->settings.id);
   }
   GNUNET_assert (GNUNET_OK ==
                  TMH_add_instance (mi));
diff --git a/src/backend/taler-merchant-httpd_post-using-templates.c 
b/src/backend/taler-merchant-httpd_post-using-templates.c
index 74eb0f44..67fc4b1e 100644
--- a/src/backend/taler-merchant-httpd_post-using-templates.c
+++ b/src/backend/taler-merchant-httpd_post-using-templates.c
@@ -73,7 +73,6 @@ TMH_post_using_templates_ID (const struct TMH_RequestHandler 
*rh,
                              struct TMH_HandlerContext *hc)
 {
   struct TMH_MerchantInstance *mi = hc->instance;
-  MHD_RESULT mret;
   const char *template_id = hc->infix;
   const char *summary = NULL;
   const char *fulfillment_url = NULL;
@@ -252,6 +251,9 @@ TMH_post_using_templates_ID (const struct 
TMH_RequestHandler *rh,
     }
     no_summary = (NULL == summary);
     fake_body = GNUNET_JSON_PACK (
+      GNUNET_JSON_pack_allow_null (
+        GNUNET_JSON_pack_string ("otp_id",
+                                 uc->etp.otp_id)),
       GNUNET_JSON_pack_object_steal (
         "order",
         GNUNET_JSON_PACK (
@@ -276,11 +278,8 @@ TMH_post_using_templates_ID (const struct 
TMH_RequestHandler *rh,
   }
 
   uc->ihc.request_body = fake_body;
-  mret = TMH_private_post_orders_with_pos_secrets (
+  return TMH_private_post_orders (
     NULL,                                                  /* not even used */
     connection,
-    &uc->ihc,
-    uc->etp.pos_key,
-    uc->etp.pos_algorithm);
-  return mret;
+    &uc->ihc);
 }
diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c 
b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
new file mode 100644
index 00000000..b147b84f
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.c
@@ -0,0 +1,78 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-delete-otp-devices-ID.c
+ * @brief implement DELETE /otp-devices/$ID
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle a DELETE "/otp-devices/$ID" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_delete_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                   struct MHD_Connection *connection,
+                                   struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  enum GNUNET_DB_QueryStatus qs;
+
+  (void) rh;
+  GNUNET_assert (NULL != mi);
+  GNUNET_assert (NULL != hc->infix);
+  qs = TMH_db->delete_otp (TMH_db->cls,
+                           mi->settings.id,
+                           hc->infix);
+  switch (qs)
+  {
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_STORE_FAILED,
+                                       "delete_otp");
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                                       "delete_otp (soft)");
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       
TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
+                                       hc->infix);
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    return TALER_MHD_reply_static (connection,
+                                   MHD_HTTP_NO_CONTENT,
+                                   NULL,
+                                   NULL,
+                                   0);
+  }
+  GNUNET_assert (0);
+  return MHD_NO;
+}
+
+
+/* end of taler-merchant-httpd_private-delete-otp-devices-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.h 
b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.h
new file mode 100644
index 00000000..cd129d0d
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-delete-otp-devices-ID.h
@@ -0,0 +1,41 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-delete-otp-devices-ID.h
+ * @brief implement DELETE /otp-devices/$ID/
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_DELETE_OTP_DEVICES_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_DELETE_OTP_DEVICES_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a DELETE "/otp-devices/$ID" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_delete_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                   struct MHD_Connection *connection,
+                                   struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-delete-otp-devices-ID.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-accounts-ID.c 
b/src/backend/taler-merchant-httpd_private-get-accounts-ID.c
new file mode 100644
index 00000000..703beeca
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-accounts-ID.c
@@ -0,0 +1,103 @@
+/*
+  This file is part of TALER
+  (C) 2023 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-accounts-ID.c
+ * @brief implement GET /accounts/$ID
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-accounts-ID.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * Handle a GET "/accounts/$ID" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh,
+                             struct MHD_Connection *connection,
+                             struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  const char *h_wire_s = hc->infix;
+  struct TALER_MerchantWireHashP h_wire;
+  struct TALER_MERCHANTDB_AccountDetails tp = { 0 };
+  enum GNUNET_DB_QueryStatus qs;
+
+  GNUNET_assert (NULL != mi);
+  GNUNET_assert (NULL != h_wire_s);
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (h_wire_s,
+                                     strlen (h_wire_s),
+                                     &h_wire,
+                                     sizeof (h_wire)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       
TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED,
+                                       h_wire_s);
+  }
+  qs = TMH_db->select_account (TMH_db->cls,
+                               mi->settings.id,
+                               &h_wire,
+                               &tp);
+  if (0 > qs)
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                       "lookup_account");
+  }
+  if (0 == qs)
+  {
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       
TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN,
+                                       hc->infix);
+  }
+  {
+    MHD_RESULT ret;
+
+    ret = TALER_MHD_REPLY_JSON_PACK (
+      connection,
+      MHD_HTTP_OK,
+      GNUNET_JSON_pack_string ("payto_uri",
+                               tp.payto_uri),
+      GNUNET_JSON_pack_data_auto ("h_wire",
+                                  &tp.h_wire),
+      GNUNET_JSON_pack_data_auto ("salt",
+                                  &tp.salt),
+      GNUNET_JSON_pack_allow_null (
+        GNUNET_JSON_pack_string ("credit_facade_url",
+                                 tp.credit_facade_url)));
+    /* We do not return the credentials, as they may
+       be sensitive */
+    json_decref (tp.credit_facade_credentials);
+    GNUNET_free (tp.payto_uri);
+    GNUNET_free (tp.credit_facade_url);
+    return ret;
+  }
+}
+
+
+/* end of taler-merchant-httpd_private-get-accounts-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-accounts-ID.h 
b/src/backend/taler-merchant-httpd_private-get-accounts-ID.h
new file mode 100644
index 00000000..da5cb729
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-accounts-ID.h
@@ -0,0 +1,41 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-accounts-ID.h
+ * @brief implement GET /accounts/$ID/
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/accounts/$ID" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_get_accounts_ID (const struct TMH_RequestHandler *rh,
+                             struct MHD_Connection *connection,
+                             struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-accounts-ID.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-accounts.c 
b/src/backend/taler-merchant-httpd_private-get-accounts.c
new file mode 100644
index 00000000..92ebb368
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-accounts.c
@@ -0,0 +1,78 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-accounts.c
+ * @brief implement GET /accounts
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-accounts.h"
+
+
+/**
+ * Add account details to our JSON array.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param ad details about the account
+ */
+static void
+add_account (void *cls,
+             const struct TALER_MERCHANTDB_AccountDetails *ad)
+{
+  json_t *pa = cls;
+
+  GNUNET_assert (0 ==
+                 json_array_append_new (
+                   pa,
+                   GNUNET_JSON_PACK (
+                     GNUNET_JSON_pack_string ("payto_uri",
+                                              ad->payto_uri),
+                     GNUNET_JSON_pack_data_auto ("h_wire",
+                                                 &ad->h_wire))));
+}
+
+
+MHD_RESULT
+TMH_private_get_accounts (const struct TMH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          struct TMH_HandlerContext *hc)
+{
+  json_t *pa;
+  enum GNUNET_DB_QueryStatus qs;
+
+  pa = json_array ();
+  GNUNET_assert (NULL != pa);
+  qs = TMH_db->select_accounts (TMH_db->cls,
+                                hc->instance->settings.id,
+                                &add_account,
+                                pa);
+  if (0 > qs)
+  {
+    GNUNET_break (0);
+    json_decref (pa);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                       NULL);
+  }
+  return TALER_MHD_REPLY_JSON_PACK (connection,
+                                    MHD_HTTP_OK,
+                                    GNUNET_JSON_pack_array_steal ("accounts",
+                                                                  pa));
+}
+
+
+/* end of taler-merchant-httpd_private-get-accounts.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-accounts.h 
b/src/backend/taler-merchant-httpd_private-get-accounts.h
new file mode 100644
index 00000000..0e9897cf
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-accounts.h
@@ -0,0 +1,41 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-accounts.h
+ * @brief implement GET /accounts
+ * @author Priscilla HUANG
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_ACCOUNTS_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/accounts" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_get_accounts (const struct TMH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-accounts.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-templates-ID.c 
b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c
similarity index 56%
copy from src/backend/taler-merchant-httpd_private-get-templates-ID.c
copy to src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c
index bdb1de9d..747e30cd 100644
--- a/src/backend/taler-merchant-httpd_private-get-templates-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.c
@@ -14,17 +14,17 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-merchant-httpd_private-get-templates-ID.c
- * @brief implement GET /templates/$ID
- * @author Priscilla HUANG
+ * @file taler-merchant-httpd_private-get-otp-devices-ID.c
+ * @brief implement GET /otp-devices/$ID
+ * @author Christian Grothoff
  */
 #include "platform.h"
-#include "taler-merchant-httpd_private-get-templates-ID.h"
+#include "taler-merchant-httpd_private-get-otp-devices-ID.h"
 #include <taler/taler_json_lib.h>
 
 
 /**
- * Handle a GET "/templates/$ID" request.
+ * Handle a GET "/otp-devices/$ID" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -32,55 +32,52 @@
  * @return MHD result code
  */
 MHD_RESULT
-TMH_private_get_templates_ID (const struct TMH_RequestHandler *rh,
-                              struct MHD_Connection *connection,
-                              struct TMH_HandlerContext *hc)
+TMH_private_get_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                struct MHD_Connection *connection,
+                                struct TMH_HandlerContext *hc)
 {
   struct TMH_MerchantInstance *mi = hc->instance;
-  struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
+  struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 };
   enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_assert (NULL != mi);
-  qs = TMH_db->lookup_template (TMH_db->cls,
-                                mi->settings.id,
-                                hc->infix,
-                                &tp);
+  qs = TMH_db->select_otp (TMH_db->cls,
+                           mi->settings.id,
+                           hc->infix,
+                           &tp);
   if (0 > qs)
   {
     GNUNET_break (0);
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
                                        TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                       "lookup_template");
+                                       "select_otp");
   }
   if (0 == qs)
   {
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_NOT_FOUND,
-                                       
TALER_EC_MERCHANT_GENERIC_TEMPLATE_UNKNOWN,
+                                       
TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
                                        hc->infix);
   }
   {
     MHD_RESULT ret;
 
+    /* Note: we deliberately (by design) do not return the otp_key */
     ret = TALER_MHD_REPLY_JSON_PACK (
       connection,
       MHD_HTTP_OK,
-      GNUNET_JSON_pack_string ("template_description",
-                               tp.template_description),
-      GNUNET_JSON_pack_uint64 ("pos_algorithm",
-                               tp.pos_algorithm),
-      GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string ("pos_key",
-                                 tp.pos_key)),
-      GNUNET_JSON_pack_object_steal ("template_contract",
-                                     tp.template_contract));
-    GNUNET_free (tp.template_description);
-    GNUNET_free (tp.pos_key);
-
+      GNUNET_JSON_pack_string ("device_description",
+                               tp.otp_description),
+      GNUNET_JSON_pack_uint64 ("otp_algorithm",
+                               tp.otp_algorithm),
+      GNUNET_JSON_pack_uint64 ("otp_ctr",
+                               tp.otp_ctr));
+    GNUNET_free (tp.otp_description);
+    GNUNET_free (tp.otp_key);
     return ret;
   }
 }
 
 
-/* end of taler-merchant-httpd_private-get-templates-ID.c */
+/* end of taler-merchant-httpd_private-get-otp-devices-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.h 
b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.h
new file mode 100644
index 00000000..78834f67
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-otp-devices-ID.h
@@ -0,0 +1,41 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-otp-devices-ID.h
+ * @brief implement GET /otp-devices/$ID/
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/otp-devices/$ID" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_get_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                struct MHD_Connection *connection,
+                                struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-otp-devices-ID.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices.c 
b/src/backend/taler-merchant-httpd_private-get-otp-devices.c
new file mode 100644
index 00000000..df86842b
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-otp-devices.c
@@ -0,0 +1,80 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-otp-devices.c
+ * @brief implement GET /otp-devices
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-get-otp-devices.h"
+
+
+/**
+ * Add OTP device details to our JSON array.
+ *
+ * @param cls a `json_t *` JSON array to build
+ * @param template_id ID of the template
+ * @param template_description human-readable description for the template
+ */
+static void
+add_otp (void *cls,
+         const char *otp_id,
+         const char *otp_description)
+{
+  json_t *pa = cls;
+
+  GNUNET_assert (0 ==
+                 json_array_append_new (
+                   pa,
+                   GNUNET_JSON_PACK (
+                     GNUNET_JSON_pack_string ("otp_device_id",
+                                              otp_id),
+                     GNUNET_JSON_pack_string ("device_description",
+                                              otp_description))));
+}
+
+
+MHD_RESULT
+TMH_private_get_otp_devices (const struct TMH_RequestHandler *rh,
+                             struct MHD_Connection *connection,
+                             struct TMH_HandlerContext *hc)
+{
+  json_t *pa;
+  enum GNUNET_DB_QueryStatus qs;
+
+  pa = json_array ();
+  GNUNET_assert (NULL != pa);
+  qs = TMH_db->lookup_otp_devices (TMH_db->cls,
+                                   hc->instance->settings.id,
+                                   &add_otp,
+                                   pa);
+  if (0 > qs)
+  {
+    GNUNET_break (0);
+    json_decref (pa);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                       NULL);
+  }
+  return TALER_MHD_REPLY_JSON_PACK (connection,
+                                    MHD_HTTP_OK,
+                                    GNUNET_JSON_pack_array_steal 
("otp_devices",
+                                                                  pa));
+}
+
+
+/* end of taler-merchant-httpd_private-get-otp-devices.c */
diff --git a/src/backend/taler-merchant-httpd_private-get-otp-devices.h 
b/src/backend/taler-merchant-httpd_private-get-otp-devices.h
new file mode 100644
index 00000000..a97ca179
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-get-otp-devices.h
@@ -0,0 +1,41 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-merchant-httpd_private-get-otp-devices.h
+ * @brief implement GET /otp-devices
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_GET_OTP_DEVICES_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Handle a GET "/otp-devices" request.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_get_otp_devices (const struct TMH_RequestHandler *rh,
+                             struct MHD_Connection *connection,
+                             struct TMH_HandlerContext *hc);
+
+/* end of taler-merchant-httpd_private-get-otp-devices.h */
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-get-templates-ID.c 
b/src/backend/taler-merchant-httpd_private-get-templates-ID.c
index bdb1de9d..e9dfc00f 100644
--- a/src/backend/taler-merchant-httpd_private-get-templates-ID.c
+++ b/src/backend/taler-merchant-httpd_private-get-templates-ID.c
@@ -68,16 +68,13 @@ TMH_private_get_templates_ID (const struct 
TMH_RequestHandler *rh,
       MHD_HTTP_OK,
       GNUNET_JSON_pack_string ("template_description",
                                tp.template_description),
-      GNUNET_JSON_pack_uint64 ("pos_algorithm",
-                               tp.pos_algorithm),
       GNUNET_JSON_pack_allow_null (
-        GNUNET_JSON_pack_string ("pos_key",
-                                 tp.pos_key)),
+        GNUNET_JSON_pack_string ("otp_id",
+                                 tp.otp_id)),
       GNUNET_JSON_pack_object_steal ("template_contract",
                                      tp.template_contract));
     GNUNET_free (tp.template_description);
-    GNUNET_free (tp.pos_key);
-
+    GNUNET_free (tp.otp_id);
     return ret;
   }
 }
diff --git a/src/backend/taler-merchant-httpd_private-patch-accounts-ID.c 
b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.c
new file mode 100644
index 00000000..04fe4ce5
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.c
@@ -0,0 +1,132 @@
+/*
+  This file is part of TALER
+  (C) 2023 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-merchant-httpd_private-patch-accounts-ID.c
+ * @brief implementing PATCH /accounts/$ID request handling
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-patch-accounts-ID.h"
+#include "taler-merchant-httpd_helper.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * PATCH configuration of an existing instance, given its configuration.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_patch_accounts_ID (const struct TMH_RequestHandler *rh,
+                                struct MHD_Connection *connection,
+                                struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  const char *h_wire_s = hc->infix;
+  enum GNUNET_DB_QueryStatus qs;
+  const json_t *cfc;
+  const char *cfu;
+  struct TALER_MerchantWireHashP h_wire;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_string ("credit_facade_url",
+                               (const char **) &cfu),
+      NULL),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_object_const ("credit_facade_credentials",
+                                     &cfc),
+      NULL),
+    GNUNET_JSON_spec_end ()
+  };
+
+  GNUNET_assert (NULL != mi);
+  GNUNET_assert (NULL != h_wire_s);
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (h_wire_s,
+                                     strlen (h_wire_s),
+                                     &h_wire,
+                                     sizeof (h_wire)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       
TALER_EC_MERCHANT_GENERIC_H_WIRE_MALFORMED,
+                                       h_wire_s);
+  }
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     hc->request_body,
+                                     spec);
+    if (GNUNET_OK != res)
+      return (GNUNET_NO == res)
+             ? MHD_YES
+             : MHD_NO;
+  }
+  
+  qs = TMH_db->update_account (TMH_db->cls,
+                               mi->settings.id,
+                               &h_wire,
+                               cfu,
+                               cfc);
+  {
+    MHD_RESULT ret = MHD_NO;
+
+    switch (qs)
+    {
+    case GNUNET_DB_STATUS_HARD_ERROR:
+      GNUNET_break (0);
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        TALER_EC_GENERIC_DB_STORE_FAILED,
+                                        "update_account");
+      break;
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                                        "unexpected serialization problem");
+      break;
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         
TALER_EC_MERCHANT_GENERIC_ACCOUNT_UNKNOWN,
+                                         h_wire_s);
+      break;
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      ret = TALER_MHD_reply_static (connection,
+                                    MHD_HTTP_NO_CONTENT,
+                                    NULL,
+                                    NULL,
+                                    0);
+      break;
+    }
+    GNUNET_JSON_parse_free (spec);
+    return ret;
+  }
+}
+
+
+/* end of taler-merchant-httpd_private-patch-accounts-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-patch-accounts-ID.h 
b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.h
new file mode 100644
index 00000000..752fb958
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-accounts-ID.h
@@ -0,0 +1,43 @@
+/*
+  This file is part of TALER
+  (C) 2023 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-merchant-httpd_private-patch-accounts-ID.h
+ * @brief implementing PATCH /accounts request handling
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_ACCOUNTS_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_ACCOUNTS_ID_H
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * PATCH configuration of an existing instance, given its configuration.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_patch_accounts_ID (const struct TMH_RequestHandler *rh,
+                               struct MHD_Connection *connection,
+                               struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c 
b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c
index bc6e3aae..027d5869 100644
--- a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c
+++ b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c
@@ -63,14 +63,11 @@ patch_instances_ID (struct TMH_MerchantInstance *mi,
                     struct TMH_HandlerContext *hc)
 {
   struct TALER_MERCHANTDB_InstanceSettings is;
-  const json_t *accounts;
   const char *name;
   const char *uts = "business";
   struct TMH_WireMethod *wm_head = NULL;
   struct TMH_WireMethod *wm_tail = NULL;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_array_const ("accounts",
-                                  &accounts),
     GNUNET_JSON_spec_string ("name",
                              &name),
     GNUNET_JSON_spec_mark_optional (
@@ -102,7 +99,6 @@ patch_instances_ID (struct TMH_MerchantInstance *mi,
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_DB_QueryStatus qs;
-  bool committed = false;
 
   GNUNET_assert (NULL != mi);
   memset (&is,
@@ -162,14 +158,6 @@ patch_instances_ID (struct TMH_MerchantInstance *mi,
                                        "jurisdiction");
   }
 
-  if (! TMH_accounts_array_valid (accounts))
-  {
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
-                                       NULL);
-  }
   for (unsigned int retry = 0; retry<MAX_RETRIES; retry++)
   {
     /* Cleanup after earlier loops */
@@ -236,258 +224,13 @@ patch_instances_ID (struct TMH_MerchantInstance *mi,
           goto giveup;
       }
     }
-
-    /* Check for changes in accounts */
-    {
-      unsigned int len = json_array_size (accounts);
-      struct TMH_WireMethod *matches[GNUNET_NZL (len)];
-      bool updated[GNUNET_NZL (len)];
-
-      memset (matches,
-              0,
-              sizeof (matches));
-      memset (updated,
-              0,
-              sizeof (updated));
-      for (struct TMH_WireMethod *wm = mi->wm_head;
-           NULL != wm;
-           wm = wm->next)
-      {
-        bool matched = false;
-        for (unsigned int i = 0; i<len; i++)
-        {
-          json_t *account = json_array_get (accounts,
-                                            i);
-          enum GNUNET_GenericReturnValue ret;
-
-          ret = TMH_cmp_wire_account (account,
-                                      wm);
-          switch (ret)
-          {
-          case GNUNET_SYSERR:
-            continue;
-          case GNUNET_NO:
-            matched = true;
-            /* our own existing payto URIs should be unique, that is no
-               duplicates in the list, so we cannot match twice */
-            GNUNET_assert (NULL == matches[i]);
-            matches[i] = wm;
-            updated[i] = true;
-            break;
-          case GNUNET_YES:
-            matched = true;
-            /* our own existing payto URIs should be unique, that is no
-               duplicates in the list, so we cannot match twice */
-            GNUNET_assert (NULL == matches[i]);
-            matches[i] = wm;
-            break;
-          }
-        }
-
-        /* delete unmatched (= removed) accounts */
-        if ( (! matched) &&
-             (wm->active) )
-        {
-          /* Account was REMOVED */
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Existing account `%s' not found, deactivating it.\n",
-                      wm->payto_uri);
-          wm->deleting = true;
-          qs = TMH_db->inactivate_account (TMH_db->cls,
-                                           mi->settings.id,
-                                           &wm->h_wire);
-          if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-          {
-            TMH_db->rollback (TMH_db->cls);
-            if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-              goto retry;
-            else
-              goto giveup;
-          }
-        }
-      } /* for (wm) */
-
-      /* handle updates */
-      for (unsigned int i = 0; i<len; i++)
-      {
-        struct TMH_WireMethod *wm = matches[i];
-
-        if (! updated[i])
-          continue;
-        GNUNET_assert (NULL != wm);
-        {
-          struct TALER_MERCHANTDB_AccountDetails ad = {
-            .payto_uri = wm->payto_uri,
-            .h_wire = wm->h_wire,
-            .salt = wm->wire_salt,
-            .credit_facade_url = wm->credit_facade_url,
-            .credit_facade_credentials = wm->credit_facade_credentials,
-            .active = true
-          };
-
-          qs = TMH_db->update_account (TMH_db->cls,
-                                       mi->settings.id,
-                                       &ad);
-          if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-          {
-            TMH_db->rollback (TMH_db->cls);
-            if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-              goto retry;
-            else
-              goto giveup;
-          }
-        } /* ad scope */
-      } /* for possible updates */
-
-      /* Find _new_ accounts or accounts to only enable */
-      for (unsigned int i = 0; i<len; i++)
-      {
-        json_t *account = json_array_get (accounts,
-                                          i);
-        struct TMH_WireMethod *wm = matches[i];
-
-        if (NULL != wm)
-        {
-          if (updated[i])
-            continue; /* handled above */
-          if (! wm->active)
-          {
-            qs = TMH_db->activate_account (TMH_db->cls,
-                                           mi->settings.id,
-                                           &wm->h_wire);
-            if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-            {
-              TMH_db->rollback (TMH_db->cls);
-              if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-                goto retry;
-              else
-                goto giveup;
-            }
-          }
-          wm->enabling = true;
-          continue;
-        }
-
-        {
-          const char *credit_facade_url = NULL;
-          const json_t *credit_facade_credentials = NULL;
-          const char *uri;
-          struct GNUNET_JSON_Specification ispec[] = {
-            GNUNET_JSON_spec_string ("payto_uri",
-                                     &uri),
-            GNUNET_JSON_spec_mark_optional (
-              GNUNET_JSON_spec_string ("credit_facade_url",
-                                       &credit_facade_url),
-              NULL),
-            GNUNET_JSON_spec_mark_optional (
-              GNUNET_JSON_spec_object_const ("credit_facade_credentials",
-                                             &credit_facade_credentials),
-              NULL),
-            GNUNET_JSON_spec_end ()
-          };
-          enum GNUNET_GenericReturnValue res;
-
-          res = TALER_MHD_parse_json_data (connection,
-                                           account,
-                                           ispec);
-          if (GNUNET_OK != res)
-            return (GNUNET_NO == res)
-              ? MHD_YES
-              : MHD_NO;
-          GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                      "Adding NEW account `%s'\n",
-                      uri);
-          wm = TMH_setup_wire_account (uri,
-                                       credit_facade_url,
-                                       credit_facade_credentials);
-          GNUNET_assert (NULL != wm); /* checked payto_uri validity earlier */
-          GNUNET_CONTAINER_DLL_insert (wm_head,
-                                       wm_tail,
-                                       wm);
-        } /* ispec scope */
-
-        {
-          struct TALER_MERCHANTDB_AccountDetails ad = {
-            .payto_uri = wm->payto_uri,
-            .h_wire = wm->h_wire,
-            .salt = wm->wire_salt,
-            .credit_facade_url = wm->credit_facade_url,
-            .credit_facade_credentials = wm->credit_facade_credentials,
-            .active = true
-          };
-
-          qs = TMH_db->insert_account (TMH_db->cls,
-                                       mi->settings.id,
-                                       &ad);
-          if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-          {
-            TMH_db->rollback (TMH_db->cls);
-            if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-              goto retry;
-            else
-              goto giveup;
-          }
-        }   /* ad variable scope */
-      } /* for (i) to find new accounts */
-    } /* scope for checking for account changes */
-
-    {
-      struct GNUNET_DB_EventHeaderP es = {
-        .size = htons (sizeof (es)),
-        .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
-      };
-
-      TMH_db->event_notify (TMH_db->cls,
-                            &es,
-                            NULL,
-                            0);
-    }
     qs = TMH_db->commit (TMH_db->cls);
 retry:
     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
       continue;
-    if (qs >= 0)
-      committed = true;
     break;
   } /* for(... MAX_RETRIES) */
 giveup:
-  /* Deactivate existing wire methods that were removed above */
-  for (struct TMH_WireMethod *wm = mi->wm_head;
-       NULL != wm;
-       wm = wm->next)
-  {
-    /* We did not flip the 'active' bits earlier because the
-       DB transaction could still fail. Now it is time to update our
-       runtime state. */
-    GNUNET_assert (! (wm->deleting & wm->enabling));
-    if (committed)
-    {
-      if (wm->deleting)
-        wm->active = false;
-      if (wm->enabling)
-        wm->active = true;
-    }
-    wm->deleting = false;
-    wm->enabling = false;
-  }
-  if (! committed)
-  {
-    struct TMH_WireMethod *wm;
-
-    while (NULL != (wm = wm_head))
-    {
-      GNUNET_CONTAINER_DLL_remove (wm_head,
-                                   wm_tail,
-                                   wm);
-      free_wm (wm);
-    }
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       TALER_EC_GENERIC_DB_COMMIT_FAILED,
-                                       NULL);
-  }
-
   /* Update our 'settings' */
   GNUNET_free (mi->settings.name);
   GNUNET_free (mi->settings.email);
@@ -508,23 +251,6 @@ giveup:
   if (NULL != is.logo)
     mi->settings.logo = GNUNET_strdup (is.logo);
 
-  /* Add 'new' wire methods to our list */
-  {
-    struct TMH_WireMethod *wm;
-
-    /* Note: this _could_ be done more efficiently if
-       someone wrote a GNUNET_CONTAINER_DLL_merge()... */
-    while (NULL != (wm = wm_head))
-    {
-      GNUNET_CONTAINER_DLL_remove (wm_head,
-                                   wm_tail,
-                                   wm);
-      GNUNET_CONTAINER_DLL_insert (mi->wm_head,
-                                   mi->wm_tail,
-                                   wm);
-    }
-  }
-
   GNUNET_JSON_parse_free (spec);
   TMH_reload_instances (mi->settings.id);
   return TALER_MHD_reply_static (connection,
diff --git a/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.c 
b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.c
new file mode 100644
index 00000000..f0fc8b0b
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.c
@@ -0,0 +1,122 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-merchant-httpd_private-patch-otp-devices-ID.c
+ * @brief implementing PATCH /otp-devices/$ID request handling
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
+#include "taler-merchant-httpd_helper.h"
+#include <taler/taler_json_lib.h>
+
+
+/**
+ * PATCH OTP device of an existing instance.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  struct TMH_HandlerContext *hc)
+{
+  struct TMH_MerchantInstance *mi = hc->instance;
+  const char *device_id = hc->infix;
+  struct TALER_MERCHANTDB_OtpDeviceDetails tp = {0};
+  enum GNUNET_DB_QueryStatus qs;
+  uint32_t otp_algorithm;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_string ("otp_description",
+                             (const char **) &tp.otp_description),
+    GNUNET_JSON_spec_uint32 ("otp_algorithm",
+                             &otp_algorithm),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_uint64 ("otp_ctr",
+                               &tp.otp_ctr),
+      NULL),
+    GNUNET_JSON_spec_string ("otp_key",
+                             (const char **) &tp.otp_key),
+    GNUNET_JSON_spec_end ()
+  };
+
+  GNUNET_assert (NULL != mi);
+  GNUNET_assert (NULL != device_id);
+  {
+    enum GNUNET_GenericReturnValue res;
+
+    res = TALER_MHD_parse_json_data (connection,
+                                     hc->request_body,
+                                     spec);
+    if (GNUNET_OK != res)
+      return (GNUNET_NO == res)
+             ? MHD_YES
+             : MHD_NO;
+  }
+
+  tp.otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) otp_algorithm;
+
+  qs = TMH_db->update_otp (TMH_db->cls,
+                           mi->settings.id,
+                           device_id,
+                           &tp);
+  {
+    MHD_RESULT ret = MHD_NO;
+
+    switch (qs)
+    {
+    case GNUNET_DB_STATUS_HARD_ERROR:
+      GNUNET_break (0);
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        TALER_EC_GENERIC_DB_STORE_FAILED,
+                                        "update_pos");
+      break;
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                        
TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+                                        "unexpected serialization problem");
+      break;
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      ret = TALER_MHD_reply_with_error (connection,
+                                        MHD_HTTP_NOT_FOUND,
+                                        
TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
+                                        device_id);
+      break;
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      ret = TALER_MHD_reply_static (connection,
+                                    MHD_HTTP_NO_CONTENT,
+                                    NULL,
+                                    NULL,
+                                    0);
+      break;
+    }
+    GNUNET_JSON_parse_free (spec);
+    return ret;
+  }
+}
+
+
+/* end of taler-merchant-httpd_private-patch-otp-devices-ID.c */
diff --git a/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.h 
b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.h
new file mode 100644
index 00000000..eef1dd0a
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-patch-otp-devices-ID.h
@@ -0,0 +1,44 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-merchant-httpd_private-patch-otp-devices-ID.h
+ * @brief implementing PATCH /otp-devices/$ID request handling
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_PATCH_OTP_DEVICES_ID_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_PATCH_OTP_DEVICES_ID_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * PATCH configuration of an existing instance, given its configuration.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_patch_otp_devices_ID (const struct TMH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c 
b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
index b5938368..68e0a478 100644
--- a/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
+++ b/src/backend/taler-merchant-httpd_private-patch-templates-ID.c
@@ -28,12 +28,6 @@
 #include <taler/taler_json_lib.h>
 
 
-/**
- * How often do we retry the simple INSERT database transaction?
- */
-#define MAX_RETRIES 3
-
-
 /**
  * Determine the cause of the PATCH failure in more detail and report.
  *
@@ -109,17 +103,12 @@ TMH_private_patch_templates_ID (const struct 
TMH_RequestHandler *rh,
   const char *template_id = hc->infix;
   struct TALER_MERCHANTDB_TemplateDetails tp = {0};
   enum GNUNET_DB_QueryStatus qs;
-  uint32_t pos_algorithm;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_string ("template_description",
                              (const char **) &tp.template_description),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                               &pos_algorithm),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("pos_key",
-                               (const char **) &tp.pos_key),
+      GNUNET_JSON_spec_string ("otp_id",
+                               (const char **) &tp.otp_id),
       NULL),
     GNUNET_JSON_spec_json ("template_contract",
                            &tp.template_contract),
@@ -140,7 +129,6 @@ TMH_private_patch_templates_ID (const struct 
TMH_RequestHandler *rh,
              : MHD_NO;
   }
 
-  tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm;
   if (! TMH_template_contract_valid (tp.template_contract))
   {
     GNUNET_break_op (0);
diff --git a/src/backend/taler-merchant-httpd_private-post-account.c 
b/src/backend/taler-merchant-httpd_private-post-account.c
index f9be253c..73abfe48 100644
--- a/src/backend/taler-merchant-httpd_private-post-account.c
+++ b/src/backend/taler-merchant-httpd_private-post-account.c
@@ -19,7 +19,7 @@
 
 /**
  * @file taler-merchant-httpd_private-post-account.c
- * @brief implementing POST /private/account request handling
+ * @brief implementing POST /private/accounts request handling
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -173,11 +173,12 @@ TMH_private_post_account (const struct TMH_RequestHandler 
*rh,
   /* Note: we may not need to do this, as we notified
      about the account change above. But also hardly hurts. */
   TMH_reload_instances (mi->settings.id);
-  return TALER_MHD_reply_static (connection,
-                                 MHD_HTTP_NO_CONTENT,
-                                 NULL,
-                                 NULL,
-                                 0);
+  return TALER_MHD_REPLY_JSON_PACK (connection,
+                                    MHD_HTTP_OK,
+                                    GNUNET_JSON_pack_data_auto ("salt",
+                                                                
&wm->wire_salt),
+                                    GNUNET_JSON_pack_data_auto ("h_wire",
+                                                                &wm->h_wire));
 }
 
 
diff --git a/src/backend/taler-merchant-httpd_private-post-instances.c 
b/src/backend/taler-merchant-httpd_private-post-instances.c
index 6c9727e9..398a846d 100644
--- a/src/backend/taler-merchant-httpd_private-post-instances.c
+++ b/src/backend/taler-merchant-httpd_private-post-instances.c
@@ -36,56 +36,6 @@
 #define MAX_RETRIES 3
 
 
-/**
- * Check if the array of @a payto_uris contains exactly the same
- * URIs as those already in @a mi (possibly in a different order).
- *
- * @param mi a merchant instance with accounts
- * @param accounts a JSON array with accounts (presumably)
- * @return true if they are 'equal', false if not or of payto_uris is not an 
array
- */
-static bool
-accounts_equal (const struct TMH_MerchantInstance *mi,
-                const json_t *accounts)
-{
-  if (! json_is_array (accounts))
-    return false;
-  {
-    unsigned int len = json_array_size (accounts);
-    enum GNUNET_GenericReturnValue matches[GNUNET_NZL (len)];
-
-    for (unsigned int i = 0; i<len; i++)
-      matches[i] = GNUNET_SYSERR;
-    for (struct TMH_WireMethod *wm = mi->wm_head;
-         NULL != wm;
-         wm = wm->next)
-    {
-      for (unsigned int i = 0; i<len; i++)
-      {
-        json_t *account = json_array_get (accounts,
-                                          i);
-        enum GNUNET_GenericReturnValue ret;
-
-        ret = TMH_cmp_wire_account (account,
-                                    wm);
-        if (GNUNET_SYSERR == ret)
-          continue;
-        if (GNUNET_SYSERR != matches[i])
-        {
-          GNUNET_break (0);
-          return false; /* duplicate entry!? */
-        }
-        matches[i] = ret;
-      }
-    }
-    for (unsigned int i = 0; i<len; i++)
-      if (GNUNET_YES != matches[i])
-        return false;
-  }
-  return true;
-}
-
-
 /**
  * Generate an instance, given its configuration.
  *
@@ -101,15 +51,12 @@ TMH_private_post_instances (const struct 
TMH_RequestHandler *rh,
 {
   struct TALER_MERCHANTDB_InstanceSettings is;
   struct TALER_MERCHANTDB_InstanceAuthSettings ias;
-  const json_t *accounts;
   const char *auth_token = NULL;
   const char *uts = "business";
   struct TMH_WireMethod *wm_head = NULL;
   struct TMH_WireMethod *wm_tail = NULL;
   const json_t *jauth;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_array_const ("accounts",
-                                  &accounts),
     GNUNET_JSON_spec_string ("id",
                              (const char **) &is.id),
     GNUNET_JSON_spec_string ("name",
@@ -179,16 +126,6 @@ TMH_private_post_instances (const struct 
TMH_RequestHandler *rh,
       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
   }
 
-  /* check accounts for well-formedness */
-  if (! TMH_accounts_array_valid (accounts))
-  {
-    GNUNET_break_op (0);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PAYTO_URI_MALFORMED,
-                                       NULL);
-  }
-
   /* check 'id' well-formed */
   {
     static bool once;
@@ -292,9 +229,7 @@ TMH_private_post_instances (const struct TMH_RequestHandler 
*rh,
                                       is.default_wire_transfer_delay)) &&
            (GNUNET_TIME_relative_cmp (mi->settings.default_pay_delay,
                                       ==,
-                                      is.default_pay_delay)) &&
-           (accounts_equal (mi,
-                            accounts)) )
+                                      is.default_pay_delay)) )
       {
         return TALER_MHD_reply_static (connection,
                                        MHD_HTTP_NO_CONTENT,
@@ -312,50 +247,6 @@ TMH_private_post_instances (const struct 
TMH_RequestHandler *rh,
     }
   }
 
-  /* convert provided payto URIs into internal data structure with salts */
-  {
-    unsigned int len = json_array_size (accounts);
-
-    for (unsigned int i = 0; i<len; i++)
-    {
-      json_t *account = json_array_get (accounts,
-                                        i);
-      const char *credit_facade_url = NULL;
-      const json_t *credit_facade_credentials = NULL;
-      const char *uri;
-      struct TMH_WireMethod *wm;
-      struct GNUNET_JSON_Specification ispec[] = {
-        GNUNET_JSON_spec_string ("payto_uri",
-                                 &uri),
-        GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("credit_facade_url",
-                                   &credit_facade_url),
-          NULL),
-        GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_object_const ("credit_facade_credentials",
-                                         &credit_facade_credentials),
-          NULL),
-        GNUNET_JSON_spec_end ()
-      };
-      enum GNUNET_GenericReturnValue res;
-
-      res = TALER_MHD_parse_json_data (connection,
-                                       account,
-                                       ispec);
-      if (GNUNET_OK != res)
-        return (GNUNET_NO == res)
-          ? MHD_YES
-          : MHD_NO;
-      wm = TMH_setup_wire_account (uri,
-                                   credit_facade_url,
-                                   credit_facade_credentials);
-      GNUNET_assert (NULL != wm);
-      GNUNET_CONTAINER_DLL_insert (wm_head,
-                                   wm_tail,
-                                   wm);
-    }
-  }
-
   /* handle authentication token setup */
   if (NULL == auth_token)
   {
@@ -431,44 +322,6 @@ TMH_private_post_instances (const struct 
TMH_RequestHandler *rh,
         TMH_instance_decref (mi);
         return ret;
       }
-      for (struct TMH_WireMethod *wm = wm_head;
-           NULL != wm;
-           wm = wm->next)
-      {
-        struct TALER_MERCHANTDB_AccountDetails ad = {
-          .payto_uri = wm->payto_uri,
-          .salt = wm->wire_salt,
-          .h_wire = wm->h_wire,
-          .credit_facade_url = wm->credit_facade_url,
-          .credit_facade_credentials = wm->credit_facade_credentials,
-          .active = wm->active
-        };
-
-        qs = TMH_db->insert_account (TMH_db->cls,
-                                     mi->settings.id,
-                                     &ad);
-        if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-          break;
-      }
-      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-      {
-        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-        TMH_db->rollback (TMH_db->cls);
-        if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-          break;
-        goto retry;
-      }
-      {
-        struct GNUNET_DB_EventHeaderP es = {
-          .size = htons (sizeof (es)),
-          .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
-        };
-
-        TMH_db->event_notify (TMH_db->cls,
-                              &es,
-                              NULL,
-                              0);
-      }
       qs = TMH_db->commit (TMH_db->cls);
       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
         qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c 
b/src/backend/taler-merchant-httpd_private-post-orders.c
index 1c888508..34fac6a0 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.c
+++ b/src/backend/taler-merchant-httpd_private-post-orders.c
@@ -1760,11 +1760,17 @@ merge_inventory (struct OrderContext *oc)
 }
 
 
+/**
+ * Parse the basics of the client request.
+ *
+ * @param[in,out] oc order context to process
+ */
 static void
 parse_order_request (struct OrderContext *oc)
 {
   const json_t *ip = NULL;
   const json_t *uuid = NULL;
+  const char *otp_id = NULL;
   bool create_token = true; /* default */
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_json ("order",
@@ -1789,6 +1795,10 @@ parse_order_request (struct OrderContext *oc)
       GNUNET_JSON_spec_bool ("create_token",
                              &create_token),
       NULL),
+    GNUNET_JSON_spec_mark_optional (
+      GNUNET_JSON_spec_string ("otp_id",
+                               &otp_id),
+      NULL),
     GNUNET_JSON_spec_end ()
   };
   enum GNUNET_GenericReturnValue ret;
@@ -1808,6 +1818,43 @@ parse_order_request (struct OrderContext *oc)
               GNUNET_TIME_relative2s (oc->refund_delay,
                                       false));
   TMH_db->expire_locks (TMH_db->cls);
+  if (NULL != otp_id)
+  {
+    struct TALER_MERCHANTDB_OtpDeviceDetails td;
+    enum GNUNET_DB_QueryStatus qs;
+    
+    qs = TMH_db->select_otp (TMH_db->cls,
+                             oc->hc->instance->settings.id,
+                             otp_id,
+                             &td);
+    switch (qs)
+    {
+    case GNUNET_DB_STATUS_HARD_ERROR:
+      GNUNET_break (0);
+      reply_with_error (oc,
+                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                        TALER_EC_GENERIC_DB_FETCH_FAILED,
+                        "select_otp");
+      return;
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+      GNUNET_break (0);
+      reply_with_error (oc,
+                        MHD_HTTP_INTERNAL_SERVER_ERROR,
+                        TALER_EC_GENERIC_DB_SOFT_FAILURE,
+                        "select_otp");
+      return;
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      reply_with_error (oc,
+                        MHD_HTTP_NOT_FOUND,
+                        TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
+                        otp_id);
+      break;
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      break;
+    }
+    oc->pos_key = td.otp_key;
+    oc->pos_algorithm = td.otp_algorithm;
+  }
   if (create_token)
   {
     GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
@@ -1912,12 +1959,10 @@ parse_order_request (struct OrderContext *oc)
 
 
 MHD_RESULT
-TMH_private_post_orders_with_pos_secrets (
+TMH_private_post_orders (
   const struct TMH_RequestHandler *rh,
   struct MHD_Connection *connection,
-  struct TMH_HandlerContext *hc,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm pos_algorithm)
+  struct TMH_HandlerContext *hc)
 {
   struct OrderContext *oc = hc->ctx;
 
@@ -1928,8 +1973,6 @@ TMH_private_post_orders_with_pos_secrets (
     hc->cc = &clean_order;
     oc->connection = connection;
     oc->hc = hc;
-    oc->pos_key = pos_key;
-    oc->pos_algorithm = pos_algorithm;
   }
   while (1)
   {
@@ -1976,18 +2019,4 @@ TMH_private_post_orders_with_pos_secrets (
 }
 
 
-MHD_RESULT
-TMH_private_post_orders (
-  const struct TMH_RequestHandler *rh,
-  struct MHD_Connection *connection,
-  struct TMH_HandlerContext *hc)
-{
-  return TMH_private_post_orders_with_pos_secrets (rh,
-                                                   connection,
-                                                   hc,
-                                                   NULL,
-                                                   TALER_MCA_NONE);
-}
-
-
 /* end of taler-merchant-httpd_private-post-orders.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-orders.h 
b/src/backend/taler-merchant-httpd_private-post-orders.h
index cbbb59c0..f1127bec 100644
--- a/src/backend/taler-merchant-httpd_private-post-orders.h
+++ b/src/backend/taler-merchant-httpd_private-post-orders.h
@@ -46,25 +46,5 @@ TMH_private_post_orders (const struct TMH_RequestHandler *rh,
                          struct MHD_Connection *connection,
                          struct TMH_HandlerContext *hc);
 
-/**
- * Generate an order.  We add the fields 'exchanges', 'merchant_pub', and
- * 'H_wire' to the order gotten from the frontend, as well as possibly other
- * fields if the frontend did not provide them. Returns the order_id.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] hc context with further information about the request
- * @param pos_key key identifying the POS, can be NULL
- * @param pos_algorithm algorithm for computing the POS confirmation
- * @return MHD result code
- */
-MHD_RESULT
-TMH_private_post_orders_with_pos_secrets (
-  const struct TMH_RequestHandler *rh,
-  struct MHD_Connection *connection,
-  struct TMH_HandlerContext *hc,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm pos_algorithm);
-
 
 #endif
diff --git a/src/backend/taler-merchant-httpd_private-post-templates.c 
b/src/backend/taler-merchant-httpd_private-post-otp-devices.c
similarity index 57%
copy from src/backend/taler-merchant-httpd_private-post-templates.c
copy to src/backend/taler-merchant-httpd_private-post-otp-devices.c
index 276e225f..5521ce97 100644
--- a/src/backend/taler-merchant-httpd_private-post-templates.c
+++ b/src/backend/taler-merchant-httpd_private-post-otp-devices.c
@@ -18,12 +18,12 @@
 */
 
 /**
- * @file taler-merchant-httpd_private-post-templates.c
- * @brief implementing POST /templates request handling
- * @author Priscilla HUANG
+ * @file taler-merchant-httpd_private-post-otp-devices.c
+ * @brief implementing POST /otp-devices request handling
+ * @author Christian Grothoff
  */
 #include "platform.h"
-#include "taler-merchant-httpd_private-post-templates.h"
+#include "taler-merchant-httpd_private-post-otp-devices.h"
 #include "taler-merchant-httpd_helper.h"
 #include <taler/taler_json_lib.h>
 
@@ -35,54 +35,48 @@
 
 
 /**
- * Check if the two templates are identical.
+ * Check if the two otp-devices are identical.
  *
- * @param t1 template to compare
- * @param t2 other template to compare
+ * @param t1 device to compare
+ * @param t2 other device to compare
  * @return true if they are 'equal', false if not or of payto_uris is not an 
array
  */
 static bool
-templates_equal (const struct TALER_MERCHANTDB_TemplateDetails *t1,
-                 const struct TALER_MERCHANTDB_TemplateDetails *t2)
+otp_devices_equal (const struct TALER_MERCHANTDB_OtpDeviceDetails *t1,
+                   const struct TALER_MERCHANTDB_OtpDeviceDetails *t2)
 {
-  return ( (0 == strcmp (t1->template_description,
-                         t2->template_description)) &&
-           ( ( (NULL == t1->pos_key) &&
-               (NULL == t2->pos_key) ) ||
-             ( (NULL != t1->pos_key) &&
-               (NULL != t2->pos_key) &&
-               (0 == strcmp (t1->pos_key,
-                             t2->pos_key))) ) &&
-           (1 == json_equal (t1->template_contract,
-                             t2->template_contract)) );
+  return ( (0 == strcmp (t1->otp_description,
+                         t2->otp_description)) &&
+           (0 == strcmp (t1->otp_key,
+                         t2->otp_key) ) &&
+           (t1->otp_ctr == t2->otp_ctr) &&
+           (t1->otp_algorithm == t2->otp_algorithm) );
 }
 
 
 MHD_RESULT
-TMH_private_post_templates (const struct TMH_RequestHandler *rh,
-                            struct MHD_Connection *connection,
-                            struct TMH_HandlerContext *hc)
+TMH_private_post_otp_devices (const struct TMH_RequestHandler *rh,
+                              struct MHD_Connection *connection,
+                              struct TMH_HandlerContext *hc)
 {
   struct TMH_MerchantInstance *mi = hc->instance;
-  struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
-  const char *template_id;
+  struct TALER_MERCHANTDB_OtpDeviceDetails tp = { 0 };
+  const char *device_id;
   enum GNUNET_DB_QueryStatus qs;
-  uint32_t pos_algorithm = 0;
+  uint32_t otp_algorithm;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_string ("template_id",
-                             &template_id),
-    GNUNET_JSON_spec_string ("template_description",
-                             (const char **) &tp.template_description),
+    GNUNET_JSON_spec_string ("otp_device_id",
+                             &device_id),
+    GNUNET_JSON_spec_string ("otp_device_description",
+                             (const char **) &tp.otp_description),
+    GNUNET_JSON_spec_uint32 ("otp_algorithm",
+                             &otp_algorithm),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                               &pos_algorithm),
+      GNUNET_JSON_spec_uint64 ("otp_ctr",
+                               &tp.otp_ctr),
       NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("pos_key",
-                               (const char **) &tp.pos_key),
-      NULL),
-    GNUNET_JSON_spec_json ("template_contract",
-                           &tp.template_contract),
+    GNUNET_JSON_spec_string ("otp_key",
+                             (const char **) &tp.otp_key),
     GNUNET_JSON_spec_end ()
   };
 
@@ -101,29 +95,17 @@ TMH_private_post_templates (const struct 
TMH_RequestHandler *rh,
              : MHD_NO;
     }
   }
-  tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm;
-  if (! TMH_template_contract_valid (tp.template_contract))
-  {
-    GNUNET_break_op (0);
-    json_dumpf (tp.template_contract,
-                stderr,
-                JSON_INDENT (2));
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_BAD_REQUEST,
-                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                       "template_contract");
-  }
+  tp.otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) otp_algorithm;
 
   /* finally, interact with DB until no serialization error */
   for (unsigned int i = 0; i<MAX_RETRIES; i++)
   {
-    /* Test if a template of this id is known */
-    struct TALER_MERCHANTDB_TemplateDetails etp;
+    /* Test if a OTP device of this id is known */
+    struct TALER_MERCHANTDB_OtpDeviceDetails etp;
 
     if (GNUNET_OK !=
         TMH_db->start (TMH_db->cls,
-                       "/post templates"))
+                       "/post otp-devices"))
     {
       GNUNET_break (0);
       GNUNET_JSON_parse_free (spec);
@@ -132,10 +114,10 @@ TMH_private_post_templates (const struct 
TMH_RequestHandler *rh,
                                          TALER_EC_GENERIC_DB_START_FAILED,
                                          NULL);
     }
-    qs = TMH_db->lookup_template (TMH_db->cls,
-                                  mi->settings.id,
-                                  template_id,
-                                  &etp);
+    qs = TMH_db->select_otp (TMH_db->cls,
+                             mi->settings.id,
+                             device_id,
+                             &etp);
     switch (qs)
     {
     case GNUNET_DB_STATUS_HARD_ERROR:
@@ -158,9 +140,10 @@ TMH_private_post_templates (const struct 
TMH_RequestHandler *rh,
       {
         bool eq;
 
-        eq = templates_equal (&tp,
-                              &etp);
-        TALER_MERCHANTDB_template_details_free (&etp);
+        eq = otp_devices_equal (&tp,
+                                &etp);
+        GNUNET_free (etp.otp_description);
+        GNUNET_free (etp.otp_key);
         TMH_db->rollback (TMH_db->cls);
         GNUNET_JSON_parse_free (spec);
         return eq
@@ -171,15 +154,15 @@ TMH_private_post_templates (const struct 
TMH_RequestHandler *rh,
                                     0)
           : TALER_MHD_reply_with_error (connection,
                                         MHD_HTTP_CONFLICT,
-                                        
TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
-                                        template_id);
+                                        
TALER_EC_MERCHANT_PRIVATE_POST_OTP_DEVICES_CONFLICT_OTP_DEVICE_EXISTS,
+                                        device_id);
       }
     } /* end switch (qs) */
 
-    qs = TMH_db->insert_template (TMH_db->cls,
-                                  mi->settings.id,
-                                  template_id,
-                                  &tp);
+    qs = TMH_db->insert_otp (TMH_db->cls,
+                             mi->settings.id,
+                             device_id,
+                             &tp);
     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     {
       TMH_db->rollback (TMH_db->cls);
@@ -215,4 +198,4 @@ retry:
 }
 
 
-/* end of taler-merchant-httpd_private-post-templates.c */
+/* end of taler-merchant-httpd_private-post-otp-devices.c */
diff --git a/src/backend/taler-merchant-httpd_private-post-otp-devices.h 
b/src/backend/taler-merchant-httpd_private-post-otp-devices.h
new file mode 100644
index 00000000..96564d08
--- /dev/null
+++ b/src/backend/taler-merchant-httpd_private-post-otp-devices.h
@@ -0,0 +1,44 @@
+/*
+  This file is part of TALER
+  (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as
+  published by the Free Software Foundation; either version 3,
+  or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file taler-merchant-httpd_private-post-otp-devices.h
+ * @brief implementing POST /otp-devices request handling
+ * @author Christian Grothoff
+ */
+#ifndef TALER_MERCHANT_HTTPD_PRIVATE_POST_OTP_DEVICES_H
+#define TALER_MERCHANT_HTTPD_PRIVATE_POST_OTP_DEVICES_H
+
+#include "taler-merchant-httpd.h"
+
+
+/**
+ * Generate an OTP device.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] hc context with further information about the request
+ * @return MHD result code
+ */
+MHD_RESULT
+TMH_private_post_otp_devices (const struct TMH_RequestHandler *rh,
+                              struct MHD_Connection *connection,
+                              struct TMH_HandlerContext *hc);
+
+#endif
diff --git a/src/backend/taler-merchant-httpd_private-post-templates.c 
b/src/backend/taler-merchant-httpd_private-post-templates.c
index 276e225f..4a5d8133 100644
--- a/src/backend/taler-merchant-httpd_private-post-templates.c
+++ b/src/backend/taler-merchant-httpd_private-post-templates.c
@@ -28,12 +28,6 @@
 #include <taler/taler_json_lib.h>
 
 
-/**
- * How often do we retry the simple INSERT database transaction?
- */
-#define MAX_RETRIES 3
-
-
 /**
  * Check if the two templates are identical.
  *
@@ -47,12 +41,12 @@ templates_equal (const struct 
TALER_MERCHANTDB_TemplateDetails *t1,
 {
   return ( (0 == strcmp (t1->template_description,
                          t2->template_description)) &&
-           ( ( (NULL == t1->pos_key) &&
-               (NULL == t2->pos_key) ) ||
-             ( (NULL != t1->pos_key) &&
-               (NULL != t2->pos_key) &&
-               (0 == strcmp (t1->pos_key,
-                             t2->pos_key))) ) &&
+           ( ( (NULL == t1->otp_id) &&
+               (NULL == t2->otp_id) ) ||
+             ( (NULL != t1->otp_id) &&
+               (NULL != t2->otp_id) &&
+               (0 == strcmp (t1->otp_id,
+                             t2->otp_id))) ) &&
            (1 == json_equal (t1->template_contract,
                              t2->template_contract)) );
 }
@@ -67,24 +61,20 @@ TMH_private_post_templates (const struct TMH_RequestHandler 
*rh,
   struct TALER_MERCHANTDB_TemplateDetails tp = { 0 };
   const char *template_id;
   enum GNUNET_DB_QueryStatus qs;
-  uint32_t pos_algorithm = 0;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_string ("template_id",
                              &template_id),
     GNUNET_JSON_spec_string ("template_description",
                              (const char **) &tp.template_description),
     GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                               &pos_algorithm),
-      NULL),
-    GNUNET_JSON_spec_mark_optional (
-      GNUNET_JSON_spec_string ("pos_key",
-                               (const char **) &tp.pos_key),
+      GNUNET_JSON_spec_string ("otp_id",
+                               (const char **) &tp.otp_id),
       NULL),
     GNUNET_JSON_spec_json ("template_contract",
                            &tp.template_contract),
     GNUNET_JSON_spec_end ()
   };
+  uint64_t otp_serial = 0;
 
   GNUNET_assert (NULL != mi);
   {
@@ -101,7 +91,6 @@ TMH_private_post_templates (const struct TMH_RequestHandler 
*rh,
              : MHD_NO;
     }
   }
-  tp.pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos_algorithm;
   if (! TMH_template_contract_valid (tp.template_contract))
   {
     GNUNET_break_op (0);
@@ -115,23 +104,63 @@ TMH_private_post_templates (const struct 
TMH_RequestHandler *rh,
                                        "template_contract");
   }
 
-  /* finally, interact with DB until no serialization error */
-  for (unsigned int i = 0; i<MAX_RETRIES; i++)
+  if (NULL != tp.otp_id)
   {
-    /* Test if a template of this id is known */
-    struct TALER_MERCHANTDB_TemplateDetails etp;
-
-    if (GNUNET_OK !=
-        TMH_db->start (TMH_db->cls,
-                       "/post templates"))
+    qs = TMH_db->select_otp_serial (TMH_db->cls,
+                                    mi->settings.id,
+                                    tp.otp_id,
+                                    &otp_serial);
+    switch (qs)
     {
+    case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
       GNUNET_break (0);
       GNUNET_JSON_parse_free (spec);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         TALER_EC_GENERIC_DB_START_FAILED,
+                                         TALER_EC_GENERIC_DB_STORE_FAILED,
+                                         "select_otp_serial");
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_NOT_FOUND,
+                                         
TALER_EC_MERCHANT_GENERIC_OTP_DEVICE_UNKNOWN,
                                          NULL);
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      break;
     }
+  }
+
+  qs = TMH_db->insert_template (TMH_db->cls,
+                                mi->settings.id,
+                                template_id,
+                                otp_serial,
+                                &tp);
+  switch (qs)
+  {
+  case GNUNET_DB_STATUS_HARD_ERROR:
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    GNUNET_break (0);
+    GNUNET_JSON_parse_free (spec);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_GENERIC_DB_STORE_FAILED,
+                                       NULL);
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    GNUNET_JSON_parse_free (spec);
+    return TALER_MHD_reply_static (connection,
+                                   MHD_HTTP_NO_CONTENT,
+                                   NULL,
+                                   NULL,
+                                   0);
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    break;
+  }
+
+  {
+    /* Test if a template of this id is known */
+    struct TALER_MERCHANTDB_TemplateDetails etp;
+
     qs = TMH_db->lookup_template (TMH_db->cls,
                                   mi->settings.id,
                                   template_id,
@@ -139,79 +168,45 @@ TMH_private_post_templates (const struct 
TMH_RequestHandler *rh,
     switch (qs)
     {
     case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
       /* Clean up and fail hard */
       GNUNET_break (0);
-      TMH_db->rollback (TMH_db->cls);
       GNUNET_JSON_parse_free (spec);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
                                          NULL);
-    case GNUNET_DB_STATUS_SOFT_ERROR:
-      /* restart transaction */
-      goto retry;
     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
-      /* Good, we can proceed! */
-      break;
+      GNUNET_break (0);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                         "logic error");
     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-      /* idempotency check: is etp == tp? */
-      {
-        bool eq;
-
-        eq = templates_equal (&tp,
-                              &etp);
-        TALER_MERCHANTDB_template_details_free (&etp);
-        TMH_db->rollback (TMH_db->cls);
-        GNUNET_JSON_parse_free (spec);
-        return eq
-          ? TALER_MHD_reply_static (connection,
-                                    MHD_HTTP_NO_CONTENT,
-                                    NULL,
-                                    NULL,
-                                    0)
-          : TALER_MHD_reply_with_error (connection,
-                                        MHD_HTTP_CONFLICT,
-                                        
TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
-                                        template_id);
-      }
-    } /* end switch (qs) */
-
-    qs = TMH_db->insert_template (TMH_db->cls,
-                                  mi->settings.id,
-                                  template_id,
-                                  &tp);
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-    {
-      TMH_db->rollback (TMH_db->cls);
       break;
     }
-    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    /* idempotency check: is etp == tp? */
     {
-      qs = TMH_db->commit (TMH_db->cls);
-      if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
-        break;
+      bool eq;
+      
+      eq = templates_equal (&tp,
+                            &etp);
+      TALER_MERCHANTDB_template_details_free (&etp);
+      TMH_db->rollback (TMH_db->cls);
+      GNUNET_JSON_parse_free (spec);
+      return eq
+        ? TALER_MHD_reply_static (connection,
+                                  MHD_HTTP_NO_CONTENT,
+                                  NULL,
+                                  NULL,
+                                  0)
+        : TALER_MHD_reply_with_error (connection,
+                                      MHD_HTTP_CONFLICT,
+                                      
TALER_EC_MERCHANT_PRIVATE_POST_TEMPLATES_CONFLICT_TEMPLATE_EXISTS,
+                                      template_id);
     }
-retry:
-    GNUNET_assert (GNUNET_DB_STATUS_SOFT_ERROR == qs);
-    TMH_db->rollback (TMH_db->cls);
-  } /* for RETRIES loop */
-  GNUNET_JSON_parse_free (spec);
-  if (qs < 0)
-  {
-    GNUNET_break (0);
-    return TALER_MHD_reply_with_error (
-      connection,
-      MHD_HTTP_INTERNAL_SERVER_ERROR,
-      (GNUNET_DB_STATUS_SOFT_ERROR == qs)
-      ? TALER_EC_GENERIC_DB_SOFT_FAILURE
-      : TALER_EC_GENERIC_DB_COMMIT_FAILED,
-      NULL);
   }
-  return TALER_MHD_reply_static (connection,
-                                 MHD_HTTP_NO_CONTENT,
-                                 NULL,
-                                 NULL,
-                                 0);
 }
 
 
diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am
index 3d7d7827..6081f722 100644
--- a/src/backenddb/Makefile.am
+++ b/src/backenddb/Makefile.am
@@ -84,6 +84,19 @@ libtaler_plugin_merchantdb_postgres_la_SOURCES = \
   pg_update_instance_auth.h pg_update_instance_auth.c \
   pg_inactivate_account.h pg_inactivate_account.c \
   pg_activate_account.h pg_activate_account.c \
+  pg_insert_otp.h pg_insert_otp.c \
+  pg_delete_otp.h pg_delete_otp.c \
+  pg_update_otp.h pg_update_otp.c \
+  pg_select_otp.h pg_select_otp.c \
+  pg_select_otp_serial.h pg_select_otp_serial.c \
+  pg_lookup_otp_devices.h pg_lookup_otp_devices.c \
+  pg_select_account.h pg_select_account.c \
+  pg_select_accounts.h pg_select_accounts.c \
+  pg_delete_template.h pg_delete_template.c \
+  pg_insert_template.h pg_insert_template.c \
+  pg_update_template.h pg_update_template.c \
+  pg_lookup_templates.h pg_lookup_templates.c \
+  pg_lookup_template.h pg_lookup_template.c \
   pg_lookup_products.h pg_lookup_products.c \
   pg_lookup_product.h pg_lookup_product.c \
   pg_delete_product.h pg_delete_product.c \
diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql
index bbc97828..4d286db6 100644
--- a/src/backenddb/merchant-0001.sql
+++ b/src/backenddb/merchant-0001.sql
@@ -291,6 +291,7 @@ CREATE TABLE IF NOT EXISTS merchant_contract_terms
     REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
   ,order_id TEXT NOT NULL
   ,contract_terms BYTEA NOT NULL
+  ,wallet_data BYTEA DEFAULT NULL
   ,h_contract_terms BYTEA NOT NULL CHECK (LENGTH(h_contract_terms)=64)
   ,creation_time INT8 NOT NULL
   ,pay_deadline INT8 NOT NULL
@@ -313,6 +314,8 @@ COMMENT ON COLUMN merchant_contract_terms.merchant_serial
   IS 'Identifies the instance offering the contract';
 COMMENT ON COLUMN merchant_contract_terms.contract_terms
   IS 'These contract terms include the wallet nonce';
+COMMENT ON COLUMN merchant_contract_terms.wallet_data
+  IS 'Data provided by the wallet when paying for the contract (subcontract 
selection, blinded tokens, etc.)';
 COMMENT ON COLUMN merchant_contract_terms.h_contract_terms
   IS 'Hash over contract_terms';
 COMMENT ON COLUMN merchant_contract_terms.refund_deadline
@@ -643,14 +646,37 @@ COMMENT ON COLUMN merchant_kyc.exchange_url
   IS 'Which exchange base URL is this KYC status valid for';
 
 
+CREATE TABLE IF NOT EXISTS merchant_otp_devices
+  (otp_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
+  ,merchant_serial BIGINT NOT NULL
+    REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
+  ,otp_id TEXT NOT NULL
+  ,otp_description TEXT NOT NULL
+  ,otp_key TEXT DEFAULT NULL
+  ,otp_algorithm INT NOT NULL DEFAULT (0)
+  ,otp_ctr INT8 NOT NULL DEFAULT (0)
+  ,UNIQUE (merchant_serial, otp_id)
+  );
+COMMENT ON TABLE merchant_otp_devices
+  IS 'OTP device owned by a merchant';
+COMMENT ON COLUMN merchant_otp_devices.otp_description
+  IS 'Human-readable OTP device description';
+COMMENT ON COLUMN merchant_otp_devices.otp_key
+  IS 'A base64-encoded key of the point-of-sale. It will be use by the OTP 
device';
+COMMENT ON COLUMN merchant_otp_devices.otp_algorithm
+  IS 'algorithm to used to generate the confirmation code. It is linked with 
the otp_key and otp_ctr';
+COMMENT ON COLUMN merchant_otp_devices.otp_ctr
+  IS 'counter for counter-based OTP generators';
+
+
 CREATE TABLE IF NOT EXISTS merchant_template
   (template_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
   ,merchant_serial BIGINT NOT NULL
     REFERENCES merchant_instances (merchant_serial) ON DELETE CASCADE
   ,template_id TEXT NOT NULL
   ,template_description TEXT NOT NULL
-  ,pos_key TEXT DEFAULT NULL
-  ,pos_algorithm INT NOT NULL DEFAULT (0)
+  ,otp_device_id BIGINT
+    REFERENCES merchant_otp_devices (otp_serial) ON DELETE SET NULL
   ,template_contract TEXT NOT NULL -- in JSON format
   ,UNIQUE (merchant_serial, template_id)
   );
@@ -658,14 +684,11 @@ COMMENT ON TABLE merchant_template
   IS 'template used by the merchant (may be incomplete, frontend can 
override)';
 COMMENT ON COLUMN merchant_template.template_description
   IS 'Human-readable template description';
-COMMENT ON COLUMN merchant_template.pos_key
-  IS 'A base64-encoded key of the point-of-sale. It will be use by the TOTP';
-COMMENT ON COLUMN merchant_template.pos_algorithm
-  IS 'algorithm to used to generate the confirmation code. It is link with the 
pos_key';
 COMMENT ON COLUMN merchant_template.template_contract
   IS 'The template contract will contains some additional information.';
 
 
+
 CREATE TABLE IF NOT EXISTS merchant_webhook
   (webhook_serial BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
   ,merchant_serial BIGINT NOT NULL
diff --git a/src/backenddb/merchantdb_helper.c 
b/src/backenddb/merchantdb_helper.c
index ee52cccc..4ae75020 100644
--- a/src/backenddb/merchantdb_helper.c
+++ b/src/backenddb/merchantdb_helper.c
@@ -42,7 +42,7 @@ TALER_MERCHANTDB_template_details_free (
   struct TALER_MERCHANTDB_TemplateDetails *tp)
 {
   GNUNET_free (tp->template_description);
-  GNUNET_free (tp->pos_key);
+  GNUNET_free (tp->otp_id);
   json_decref (tp->template_contract);
 }
 
diff --git a/src/backenddb/pg_inactivate_account.c 
b/src/backenddb/pg_delete_otp.c
similarity index 55%
copy from src/backenddb/pg_inactivate_account.c
copy to src/backenddb/pg_delete_otp.c
index 67c39462..7d32b3fb 100644
--- a/src/backenddb/pg_inactivate_account.c
+++ b/src/backenddb/pg_delete_otp.c
@@ -14,42 +14,42 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_inactivate_account.c
- * @brief Implementation of the inactivate_account function for Postgres
- * @author Iván Ávalos
+ * @file backenddb/pg_delete_otp.c
+ * @brief Implementation of the delete_otp function for Postgres
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <taler/taler_error_codes.h>
 #include <taler/taler_dbevents.h>
 #include <taler/taler_pq_lib.h>
-#include "pg_inactivate_account.h"
+#include "pg_delete_otp.h"
 #include "pg_helper.h"
 
+
 enum GNUNET_DB_QueryStatus
-TMH_PG_inactivate_account (void *cls,
-                           const char *merchant_id,
-                           const struct TALER_MerchantWireHashP *h_wire)
+TMH_PG_delete_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (merchant_id),
-    GNUNET_PQ_query_param_auto_from_type (h_wire),
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (otp_id),
     GNUNET_PQ_query_param_end
   };
 
   check_connection (pg);
-  /* the merchant instance is implied from the random salt
-     that is part of the h_wire calculation */
   PREPARE (pg,
-           "inactivate_account",
-           "UPDATE merchant_accounts SET"
-           " active=FALSE"
-           " WHERE h_wire=$2"
-           "  AND merchant_serial="
-           "   (SELECT merchant_serial"
-           "      FROM merchant_instances"
-           "      WHERE merchant_id=$1)");
+           "delete_otp",
+           "DELETE"
+           " FROM merchant_otp_devices"
+           " WHERE merchant_otp_devices.merchant_serial="
+           "     (SELECT merchant_serial "
+           "        FROM merchant_instances"
+           "        WHERE merchant_id=$1)"
+           "   AND merchant_otp_devices.template_id=$2");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "inactivate_account",
+                                             "delete_otp",
                                              params);
 }
+
diff --git a/src/backenddb/pg_update_account.h b/src/backenddb/pg_delete_otp.h
similarity index 61%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_delete_otp.h
index 52b476d9..40e67e8a 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_delete_otp.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_delete_otp.h
+ * @brief implementation of the delete_otp function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_DELETE_OTP_H
+#define PG_DELETE_OTP_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,17 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Delete information about an OTP device.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
- * @return database result code
+ * @param instance_id instance to delete OTP device of
+ * @param otp_id otp device to delete
+ * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+ *           if template unknown.
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_delete_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id);
 
 #endif
diff --git a/src/backenddb/pg_inactivate_account.c 
b/src/backenddb/pg_delete_template.c
similarity index 55%
copy from src/backenddb/pg_inactivate_account.c
copy to src/backenddb/pg_delete_template.c
index 67c39462..15531c6b 100644
--- a/src/backenddb/pg_inactivate_account.c
+++ b/src/backenddb/pg_delete_template.c
@@ -14,42 +14,42 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_inactivate_account.c
- * @brief Implementation of the inactivate_account function for Postgres
- * @author Iván Ávalos
+ * @file backenddb/pg_delete_template.c
+ * @brief Implementation of the delete_template function for Postgres
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <taler/taler_error_codes.h>
 #include <taler/taler_dbevents.h>
 #include <taler/taler_pq_lib.h>
-#include "pg_inactivate_account.h"
+#include "pg_delete_template.h"
 #include "pg_helper.h"
 
+
 enum GNUNET_DB_QueryStatus
-TMH_PG_inactivate_account (void *cls,
-                           const char *merchant_id,
-                           const struct TALER_MerchantWireHashP *h_wire)
+TMH_PG_delete_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (merchant_id),
-    GNUNET_PQ_query_param_auto_from_type (h_wire),
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (template_id),
     GNUNET_PQ_query_param_end
   };
 
   check_connection (pg);
-  /* the merchant instance is implied from the random salt
-     that is part of the h_wire calculation */
   PREPARE (pg,
-           "inactivate_account",
-           "UPDATE merchant_accounts SET"
-           " active=FALSE"
-           " WHERE h_wire=$2"
-           "  AND merchant_serial="
-           "   (SELECT merchant_serial"
-           "      FROM merchant_instances"
-           "      WHERE merchant_id=$1)");
+           "delete_template",
+           "DELETE"
+           " FROM merchant_template"
+           " WHERE merchant_template.merchant_serial="
+           "     (SELECT merchant_serial "
+           "        FROM merchant_instances"
+           "        WHERE merchant_id=$1)"
+           "   AND merchant_template.template_id=$2");
   return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "inactivate_account",
+                                             "delete_template",
                                              params);
 }
+
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_delete_template.h
similarity index 60%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_delete_template.h
index 52b476d9..ed0e0cf0 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_delete_template.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_delete_template.h
+ * @brief implementation of the delete_template function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_DELETE_TEMPLATE_H
+#define PG_DELETE_TEMPLATE_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,18 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Delete information about a template.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
- * @return database result code
+ * @param instance_id instance to delete template of
+ * @param template_id template to delete
+ * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+ *           if template unknown.
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
+TMH_PG_delete_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id);
 
 
 #endif
diff --git a/src/backenddb/pg_inactivate_account.c 
b/src/backenddb/pg_inactivate_account.c
index 67c39462..7e0d10ae 100644
--- a/src/backenddb/pg_inactivate_account.c
+++ b/src/backenddb/pg_inactivate_account.c
@@ -38,8 +38,6 @@ TMH_PG_inactivate_account (void *cls,
   };
 
   check_connection (pg);
-  /* the merchant instance is implied from the random salt
-     that is part of the h_wire calculation */
   PREPARE (pg,
            "inactivate_account",
            "UPDATE merchant_accounts SET"
diff --git a/src/backenddb/pg_insert_otp.c b/src/backenddb/pg_insert_otp.c
new file mode 100644
index 00000000..feae2230
--- /dev/null
+++ b/src/backenddb/pg_insert_otp.c
@@ -0,0 +1,74 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_otp.c
+ * @brief Implementation of the insert_otp function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_otp.h"
+#include "pg_helper.h"
+
+
+/**
+ * Insert details about a particular OTP device.
+ *
+ * @param cls closure
+ * @param instance_id instance to insert OTP device for
+ * @param otp_id otp identifier of OTP device to insert
+ * @param td the OTP device details to insert
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id,
+                   const struct TALER_MERCHANTDB_OtpDeviceDetails *td)
+{
+  struct PostgresClosure *pg = cls;
+  uint32_t pos32 = (uint32_t) td->otp_algorithm;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (otp_id),
+    GNUNET_PQ_query_param_string (td->otp_description),
+    GNUNET_PQ_query_param_string (td->otp_key),
+    GNUNET_PQ_query_param_uint32 (&pos32),
+    GNUNET_PQ_query_param_uint64 (&td->otp_ctr),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "insert_otp",
+           "INSERT INTO merchant_otp_devices"
+           "(merchant_serial"
+           ",otp_id"
+           ",otp_description"
+           ",otp_key"
+           ",otp_algorithm"
+           ",otp_ctr"
+           ")"
+           " SELECT merchant_serial,"
+           " $2, $3, $4, $5, $6"
+           " FROM merchant_instances"
+           " WHERE merchant_id=$1");
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_otp",
+                                             params);
+}
diff --git a/src/backenddb/pg_update_account.h b/src/backenddb/pg_insert_otp.h
similarity index 60%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_insert_otp.h
index 52b476d9..70267d37 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_insert_otp.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_insert_otp.h
+ * @brief implementation of the insert_otp function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_INSERT_OTP_H
+#define PG_INSERT_OTP_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,19 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Insert details about a particular OTP device.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
+ * @param instance_id instance to insert OTP device for
+ * @param otp_id otp identifier of OTP device to insert
+ * @param td the OTP device details to insert
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
+TMH_PG_insert_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id,
+                   const struct TALER_MERCHANTDB_OtpDeviceDetails *td);
 
 
 #endif
diff --git a/src/backenddb/pg_insert_template.c 
b/src/backenddb/pg_insert_template.c
new file mode 100644
index 00000000..5fc76a1d
--- /dev/null
+++ b/src/backenddb/pg_insert_template.c
@@ -0,0 +1,66 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_insert_template.c
+ * @brief Implementation of the insert_template function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_insert_template.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_insert_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id,
+                        uint64_t otp_serial_id,
+                        const struct TALER_MERCHANTDB_TemplateDetails *td)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (template_id),
+    GNUNET_PQ_query_param_string (td->template_description),
+    (0 == otp_serial_id)
+    ? GNUNET_PQ_query_param_null ()
+    : GNUNET_PQ_query_param_uint64 (&otp_serial_id),
+    TALER_PQ_query_param_json (td->template_contract),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "insert_template",
+           "INSERT INTO merchant_template"
+           "(merchant_serial"
+           ",template_id"
+           ",template_description"
+           ",otp_device_id"
+           ",template_contract"
+           ")"
+           " SELECT merchant_serial,"
+           " $2, $3, $4, $5"
+           " FROM merchant_instances"
+           " WHERE merchant_id=$1");
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "insert_template",
+                                             params);
+}
+
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_insert_template.h
similarity index 55%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_insert_template.h
index 52b476d9..fb9c3700 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_insert_template.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_insert_template.h
+ * @brief implementation of the insert_template function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_INSERT_TEMPLATE_H
+#define PG_INSERT_TEMPLATE_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,20 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Insert details about a particular template.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
+ * @param instance_id instance to insert template for
+ * @param template_id template identifier of template to insert
+ * @param otp_serial_id 0 if no OTP device is associated
+ * @param td the template details to insert
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_insert_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id,
+                        uint64_t otp_serial_id,
+                        const struct TALER_MERCHANTDB_TemplateDetails *td);
 
 #endif
diff --git a/src/backenddb/pg_lookup_instances.c 
b/src/backenddb/pg_lookup_instances.c
index 15137518..323b1957 100644
--- a/src/backenddb/pg_lookup_instances.c
+++ b/src/backenddb/pg_lookup_instances.c
@@ -92,17 +92,6 @@ prepare (struct PostgresClosure *pg)
            " merchant_priv"
            " FROM merchant_keys"
            " WHERE merchant_serial=$1");
-  PREPARE (pg,
-           "lookup_accounts",
-           "SELECT"
-           " h_wire"
-           ",salt"
-           ",payto_uri"
-           ",credit_facade_url"
-           ",credit_facade_credentials"
-           ",active"
-           " FROM merchant_accounts"
-           " WHERE merchant_serial=$1");
   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
 }
 
@@ -112,13 +101,9 @@ prepare (struct PostgresClosure *pg)
  * Find the private key if possible, and invoke the callback.
  *
  * @param lic context we are handling
- * @param num_accounts length of @a accounts array
- * @param accounts information about accounts of the instance in @a lic
  */
 static void
-call_with_accounts (struct LookupInstancesContext *lic,
-                    unsigned int num_accounts,
-                    const struct TALER_MERCHANTDB_AccountDetails accounts[])
+call_cb (struct LookupInstancesContext *lic)
 {
   struct PostgresClosure *pg = lic->pg;
   enum GNUNET_DB_QueryStatus qs;
@@ -157,91 +142,7 @@ call_with_accounts (struct LookupInstancesContext *lic,
            &lic->merchant_pub,
            (0 == qs) ? NULL : &merchant_priv,
            &lic->is,
-           &lic->ias,
-           num_accounts,
-           accounts);
-}
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results about accounts.
- *
- * @param cls of type `struct LookupInstancesContext *`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-lookup_accounts_cb (void *cls,
-                    PGresult *result,
-                    unsigned int num_results)
-{
-  struct LookupInstancesContext *lic = cls;
-  char *paytos[GNUNET_NZL (num_results)];
-  char *facade_urls[GNUNET_NZL (num_results)];
-  json_t *credentials[GNUNET_NZL (num_results)];
-  struct TALER_MERCHANTDB_AccountDetails accounts[GNUNET_NZL (num_results)];
-
-  memset (facade_urls,
-          0,
-          sizeof (facade_urls));
-  memset (credentials,
-          0,
-          sizeof (credentials));
-  /* Note: this memset is completely superfluous, but gcc-11 (and gcc-12) have
-     a bug creating a warning without it! See #7585 */
-  memset (accounts,
-          0,
-          sizeof (accounts));
-  for (unsigned int i = 0; i < num_results; i++)
-  {
-    struct TALER_MERCHANTDB_AccountDetails *account = &accounts[i];
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("h_wire",
-                                            &account->h_wire),
-      GNUNET_PQ_result_spec_auto_from_type ("salt",
-                                            &account->salt),
-      GNUNET_PQ_result_spec_string ("payto_uri",
-                                    &paytos[i]),
-      GNUNET_PQ_result_spec_allow_null (
-        GNUNET_PQ_result_spec_string ("credit_facade_url",
-                                      &facade_urls[i]),
-        NULL),
-      GNUNET_PQ_result_spec_allow_null (
-        TALER_PQ_result_spec_json ("credit_facade_credentials",
-                                   &credentials[i]),
-        NULL),
-      GNUNET_PQ_result_spec_bool ("active",
-                                  &account->active),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
-    {
-      GNUNET_break (0);
-      lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
-      for (unsigned int j = 0; j < i; j++)
-      {
-        GNUNET_free (paytos[j]);
-        GNUNET_free (facade_urls[j]);
-        json_decref (credentials[j]);
-      }
-      return;
-    }
-    account->payto_uri = paytos[i];
-  }
-  call_with_accounts (lic,
-                      num_results,
-                      accounts);
-  for (unsigned int i = 0; i < num_results; i++)
-  {
-    GNUNET_free (paytos[i]);
-    GNUNET_free (facade_urls[i]);
-    json_decref (credentials[i]);
-  }
+           &lic->ias);
 }
 
 
@@ -316,10 +217,6 @@ lookup_instances_cb (void *cls,
         NULL),
       GNUNET_PQ_result_spec_end
     };
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_uint64 (&lic->instance_serial),
-      GNUNET_PQ_query_param_end
-    };
 
     memset (&lic->ias.auth_salt,
             0,
@@ -337,19 +234,7 @@ lookup_instances_cb (void *cls,
       return;
     }
     lic->is.ut = (enum TALER_KYCLOGIC_KycUserType) ut32;
-    lic->qs = GNUNET_PQ_eval_prepared_multi_select (lic->pg->conn,
-                                                    "lookup_accounts",
-                                                    params,
-                                                    &lookup_accounts_cb,
-                                                    lic);
-    if (0 > lic->qs)
-    {
-      /* lookup_accounts_cb() did not run, still notify about the
-         account-less instance! */
-      call_with_accounts (lic,
-                          0,
-                          NULL);
-    }
+    call_cb (lic);
     GNUNET_PQ_cleanup_result (rs);
     if (0 > lic->qs)
       break;
diff --git a/src/backenddb/pg_lookup_otp_devices.c 
b/src/backenddb/pg_lookup_otp_devices.c
new file mode 100644
index 00000000..b4a2b569
--- /dev/null
+++ b/src/backenddb/pg_lookup_otp_devices.c
@@ -0,0 +1,133 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_lookup_otp_devices.c
+ * @brief Implementation of the lookup_otp_devices function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_lookup_otp_devices.h"
+#include "pg_helper.h"
+
+
+/**
+ * Context used for TMH_PG_lookup_otp_devices().
+ */
+struct LookupOtpDeviceContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_OtpDeviceCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Did database result extraction fail?
+   */
+  bool extract_failed;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results about otp_device.
+ *
+ * @param[in,out] cls of type `struct LookupOtpDeviceContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_otp_devices_cb (void *cls,
+                       PGresult *result,
+                       unsigned int num_results)
+{
+  struct LookupOtpDeviceContext *tlc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    char *otp_device_id;
+    char *otp_device_description;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("otp_id",
+                                    &otp_device_id),
+      GNUNET_PQ_result_spec_string ("otp_description",
+                                    &otp_device_description),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      tlc->extract_failed = true;
+      return;
+    }
+    tlc->cb (tlc->cb_cls,
+             otp_device_id,
+             otp_device_description);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_otp_devices (void *cls,
+                           const char *instance_id,
+                           TALER_MERCHANTDB_OtpDeviceCallback cb,
+                           void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct LookupOtpDeviceContext tlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    /* Can be overwritten by the lookup_otp_device_cb */
+    .extract_failed = false,
+  };
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "lookup_otp_devices",
+           "SELECT"
+           " otp_id"
+           ",otp_description"
+           " FROM merchant_otp_devices"
+           " JOIN merchant_instances"
+           "   USING (merchant_serial)"
+           " WHERE merchant_instances.merchant_id=$1");
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_otp_devices",
+                                             params,
+                                             &lookup_otp_devices_cb,
+                                             &tlc);
+  /* If there was an error inside lookup_otp_device_cb, return a hard error. */
+  if (tlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_lookup_otp_devices.h
similarity index 58%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_lookup_otp_devices.h
index 52b476d9..bb58b70f 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_lookup_otp_devices.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_lookup_otp_devices.h
+ * @brief implementation of the lookup_otp_devices function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_LOOKUP_OTP_DEVICES_H
+#define PG_LOOKUP_OTP_DEVICES_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,19 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Lookup all of the OTP devices the given instance has configured.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
+ * @param instance_id instance to lookup OTP devices for
+ * @param cb function to call on all OTP devices found
+ * @param cb_cls closure for @a cb
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
+TMH_PG_lookup_otp_devices (void *cls,
+                           const char *instance_id,
+                           TALER_MERCHANTDB_OtpDeviceCallback cb,
+                           void *cb_cls);
 
 
 #endif
diff --git a/src/backenddb/pg_lookup_template.c 
b/src/backenddb/pg_lookup_template.c
new file mode 100644
index 00000000..a0326bc8
--- /dev/null
+++ b/src/backenddb/pg_lookup_template.c
@@ -0,0 +1,98 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_lookup_template.c
+ * @brief Implementation of the lookup_template function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_lookup_template.h"
+#include "pg_helper.h"
+
+
+/**
+ * Lookup details about a particular template.
+ *
+ * @param cls closure
+ * @param instance_id instance to lookup template for
+ * @param template_id template to lookup
+ * @param[out] td set to the template details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
+ * @return database result code
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id,
+                        struct TALER_MERCHANTDB_TemplateDetails *td)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (template_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "lookup_template",
+           "SELECT"
+           " mt.template_description"
+           ",mod.otp_id"
+           ",mt.template_contract"
+           " FROM merchant_template mt"
+           " JOIN merchant_instances mi"
+           "   ON (mi.merchant_serial = mt.merchant_serial)"
+           " LEFT JOIN merchant_otp_devices mod"
+           "   ON (mod.otp_serial = mt.otp_device_id)"
+           " WHERE mi.merchant_id=$1"
+           "   AND mt.template_id=$2");
+  if (NULL == td)
+  {
+    struct GNUNET_PQ_ResultSpec rs_null[] = {
+      GNUNET_PQ_result_spec_end
+    };
+
+    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                     "lookup_template",
+                                                     params,
+                                                     rs_null);
+  }
+  else
+  {
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("template_description",
+                                    &td->template_description),
+      GNUNET_PQ_result_spec_allow_null (
+        GNUNET_PQ_result_spec_string ("otp_id",
+                                      &td->otp_id),
+        NULL),
+      TALER_PQ_result_spec_json ("template_contract",
+                                 &td->template_contract),
+      GNUNET_PQ_result_spec_end
+    };
+
+    td->otp_id = NULL;
+    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                     "lookup_template",
+                                                     params,
+                                                     rs);
+  }
+}
+
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_lookup_template.h
similarity index 56%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_lookup_template.h
index 52b476d9..44e01b08 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_lookup_template.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_lookup_template.h
+ * @brief implementation of the lookup_template function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_LOOKUP_TEMPLATE_H
+#define PG_LOOKUP_TEMPLATE_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,20 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Lookup details about a particular template.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
+ * @param instance_id instance to lookup template for
+ * @param template_id template to lookup
+ * @param[out] td set to the template details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
+TMH_PG_lookup_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id,
+                        struct TALER_MERCHANTDB_TemplateDetails *td);
 
 
 #endif
diff --git a/src/backenddb/pg_lookup_templates.c 
b/src/backenddb/pg_lookup_templates.c
new file mode 100644
index 00000000..59240994
--- /dev/null
+++ b/src/backenddb/pg_lookup_templates.c
@@ -0,0 +1,135 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_lookup_templates.c
+ * @brief Implementation of the lookup_templates function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_lookup_templates.h"
+#include "pg_helper.h"
+
+
+/**
+ * Context used for TMH_PG_lookup_templates().
+ */
+struct LookupTemplateContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_TemplatesCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Did database result extraction fail?
+   */
+  bool extract_failed;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results about template.
+ *
+ * @param[in,out] cls of type `struct LookupTemplateContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+lookup_templates_cb (void *cls,
+                     PGresult *result,
+                     unsigned int num_results)
+{
+  struct LookupTemplateContext *tlc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    char *template_id;
+    char *template_description;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("template_id",
+                                    &template_id),
+      GNUNET_PQ_result_spec_string ("template_description",
+                                    &template_description),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      tlc->extract_failed = true;
+      return;
+    }
+    tlc->cb (tlc->cb_cls,
+             template_id,
+             template_description);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_lookup_templates (void *cls,
+                         const char *instance_id,
+                         TALER_MERCHANTDB_TemplatesCallback cb,
+                         void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct LookupTemplateContext tlc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    /* Can be overwritten by the lookup_template_cb */
+    .extract_failed = false,
+  };
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "lookup_templates",
+           "SELECT"
+           " template_id"
+           ",template_description"
+           " FROM merchant_template"
+           " JOIN merchant_instances"
+           "   USING (merchant_serial)"
+           " WHERE merchant_instances.merchant_id=$1");
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "lookup_templates",
+                                             params,
+                                             &lookup_templates_cb,
+                                             &tlc);
+  /* If there was an error inside lookup_template_cb, return a hard error. */
+  if (tlc.extract_failed)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_lookup_templates.h
similarity index 59%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_lookup_templates.h
index 52b476d9..eee5be7e 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_lookup_templates.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_lookup_templates.h
+ * @brief implementation of the lookup_templates function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_LOOKUP_TEMPLATES_H
+#define PG_LOOKUP_TEMPLATES_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,18 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Lookup all of the templates the given instance has configured.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
+ * @param instance_id instance to lookup template for
+ * @param cb function to call on all template found
+ * @param cb_cls closure for @a cb
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_lookup_templates (void *cls,
+                         const char *instance_id,
+                         TALER_MERCHANTDB_TemplatesCallback cb,
+                         void *cb_cls);
 
 #endif
diff --git a/src/backenddb/pg_select_account.c 
b/src/backenddb/pg_select_account.c
new file mode 100644
index 00000000..abf7e9a8
--- /dev/null
+++ b/src/backenddb/pg_select_account.c
@@ -0,0 +1,79 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_select_account.c
+ * @brief Implementation of the select_account function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_select_accounts.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_account (void *cls,
+                       const char *id,
+                       const struct TALER_MerchantWireHashP *h_wire,
+                       struct TALER_MERCHANTDB_AccountDetails *ad)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (id),
+    GNUNET_PQ_query_param_auto_from_type (h_wire),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("salt",
+                                          &ad->salt),
+    GNUNET_PQ_result_spec_string ("payto_uri",
+                                  &ad->payto_uri),
+    GNUNET_PQ_result_spec_allow_null (
+      GNUNET_PQ_result_spec_string ("credit_facade_url",
+                                    &ad->credit_facade_url),
+      NULL),
+    GNUNET_PQ_result_spec_allow_null (
+      TALER_PQ_result_spec_json ("credit_facade_credentials",
+                                 &ad->credit_facade_credentials),
+      NULL),
+    GNUNET_PQ_result_spec_bool ("active",
+                                &ad->active),
+    GNUNET_PQ_result_spec_end
+  };
+
+  ad->h_wire = *h_wire;
+  check_connection (pg);
+  PREPARE (pg,
+           "select_account",
+           "SELECT"
+           " salt"
+           ",payto_uri"
+           ",credit_facade_url"
+           ",credit_facade_credentials"
+           ",active"
+           " FROM merchant_accounts"
+           " WHERE merchant_serial="
+           "  (SELECT merchant_serial "
+           "    FROM merchant_instances"
+           "    WHERE merchant_id=$1) AND "
+           " h_wire=$2;");
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "select_account",
+                                                   params,
+                                                   rs);
+}
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_select_account.h
similarity index 63%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_select_account.h
index 52b476d9..d74eaf1e 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_select_account.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_select_account.h
+ * @brief implementation of the select_account function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_SELECT_ACCOUNT_H
+#define PG_SELECT_ACCOUNT_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,18 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Obtain information about an instance's accounts.
  *
  * @param cls closure
  * @param id identifier of the instance
- * @param account_details details about the account
+ * @param h_wire wire hash of the account
+ * @param[out] ad account details returned
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_select_account (void *cls,
+                       const char *id,
+                       const struct TALER_MerchantWireHashP *h_wire,
+                       struct TALER_MERCHANTDB_AccountDetails *ad);
 
 #endif
diff --git a/src/backenddb/pg_select_accounts.c 
b/src/backenddb/pg_select_accounts.c
new file mode 100644
index 00000000..cf15a765
--- /dev/null
+++ b/src/backenddb/pg_select_accounts.c
@@ -0,0 +1,158 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_select_accounts.c
+ * @brief Implementation of the select_accounts function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_select_accounts.h"
+#include "pg_helper.h"
+
+
+/**
+ * Context for select_accounts().
+ */
+struct SelectAccountsContext
+{
+  /**
+   * Function to call with the results.
+   */
+  TALER_MERCHANTDB_AccountCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Database context.
+   */
+  struct PostgresClosure *pg;
+
+  /**
+   * Set to the return value on errors.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results about accounts.
+ *
+ * @param cls of type `struct SelectAccountsContext *`
+ * @param result the postgres result
+ * @param num_results the number of results in @a result
+ */
+static void
+select_account_cb (void *cls,
+                   PGresult *result,
+                   unsigned int num_results)
+{
+  struct SelectAccountsContext *lic = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
+  {
+    char *payto;
+    char *facade_url = NULL;
+    json_t *credential = NULL;
+    struct TALER_MERCHANTDB_AccountDetails acc;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+                                            &acc.h_wire),
+      GNUNET_PQ_result_spec_auto_from_type ("salt",
+                                            &acc.salt),
+      GNUNET_PQ_result_spec_string ("payto_uri",
+                                    &payto),
+      GNUNET_PQ_result_spec_allow_null (
+        GNUNET_PQ_result_spec_string ("credit_facade_url",
+                                      &facade_url),
+        NULL),
+      GNUNET_PQ_result_spec_allow_null (
+        TALER_PQ_result_spec_json ("credit_facade_credentials",
+                                   &credential),
+        NULL),
+      GNUNET_PQ_result_spec_bool ("active",
+                                  &acc.active),
+      GNUNET_PQ_result_spec_end
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
+    {
+      GNUNET_break (0);
+      lic->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+    acc.payto_uri = payto;
+    acc.credit_facade_url = facade_url;
+    acc.credit_facade_credentials = credential;
+    lic->cb (lic->cb_cls,
+             &acc);
+    GNUNET_PQ_cleanup_result (rs);
+  }
+}
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_accounts (void *cls,
+                        const char *id,
+                        TALER_MERCHANTDB_AccountCallback cb,
+                        void *cb_cls)
+{
+  struct PostgresClosure *pg = cls;
+  struct SelectAccountsContext lic = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .pg = pg
+  };
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (id),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  check_connection (pg);
+  PREPARE (pg,
+           "select_accounts",
+           "SELECT"
+           " h_wire"
+           ",salt"
+           ",payto_uri"
+           ",credit_facade_url"
+           ",credit_facade_credentials"
+           ",active"
+           " FROM merchant_accounts"
+           " WHERE merchant_serial="
+           "  (SELECT merchant_serial "
+           "    FROM merchant_instances"
+           "    WHERE merchant_id=$1);");
+  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
+                                             "select_accounts",
+                                             params,
+                                             &select_account_cb,
+                                             &lic);
+  if (0 > lic.qs)
+    return lic.qs;
+  return qs;
+}
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_select_accounts.h
similarity index 64%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_select_accounts.h
index 52b476d9..935fada6 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_select_accounts.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_select_accounts.h
+ * @brief implementation of the select_accounts function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_SELECT_ACCOUNTS_H
+#define PG_SELECT_ACCOUNTS_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,18 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Obtain information about an instance's accounts.
  *
  * @param cls closure
  * @param id identifier of the instance
- * @param account_details details about the account
+ * @param cb function to call on each account
+ * @param cb_cls closure for @a cb
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_select_accounts (void *cls,
+                        const char *id,
+                        TALER_MERCHANTDB_AccountCallback cb,
+                        void *cb_cls);
 
 #endif
diff --git a/src/backenddb/pg_select_otp.c b/src/backenddb/pg_select_otp.c
new file mode 100644
index 00000000..f105488a
--- /dev/null
+++ b/src/backenddb/pg_select_otp.c
@@ -0,0 +1,91 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_select_otp.c
+ * @brief Implementation of the select_otp function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_select_otp.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id,
+                   struct TALER_MERCHANTDB_OtpDeviceDetails *td)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (otp_id),
+    GNUNET_PQ_query_param_end
+  };
+
+  PREPARE (pg,
+           "select_otp",
+           "SELECT"
+           " otp_description"
+           ",otp_ctr"
+           ",otp_key"
+           ",otp_algorithm"
+           " FROM merchant_otp_devices"
+           " JOIN merchant_instances"
+           "   USING (merchant_serial)"
+           " WHERE merchant_instances.merchant_id=$1"
+           "   AND merchant_otp_devices.otp_id=$2");
+  if (NULL == td)
+  {
+    struct GNUNET_PQ_ResultSpec rs_null[] = {
+      GNUNET_PQ_result_spec_end
+    };
+
+    check_connection (pg);
+    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                     "select_otp",
+                                                     params,
+                                                     rs_null);
+  }
+  else
+  {
+    uint32_t pos32;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_string ("otp_description",
+                                    &td->otp_description),
+      GNUNET_PQ_result_spec_uint64 ("otp_ctr",
+                                    &td->otp_ctr),
+      GNUNET_PQ_result_spec_string ("otp_key",
+                                    &td->otp_key),
+      GNUNET_PQ_result_spec_uint32 ("otp_algorithm",
+                                    &pos32),
+      GNUNET_PQ_result_spec_end
+    };
+    enum GNUNET_DB_QueryStatus qs;
+
+    check_connection (pg);
+    qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "select_otp",
+                                                   params,
+                                                   rs);
+    td->otp_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32;
+    return qs;
+  }
+}
+
diff --git a/src/backenddb/pg_update_account.h b/src/backenddb/pg_select_otp.h
similarity index 58%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_select_otp.h
index 52b476d9..e015cf6b 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_select_otp.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_select_otp.h
+ * @brief implementation of the select_otp function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_SELECT_OTP_H
+#define PG_SELECT_OTP_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,19 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Lookup details about an OTP device.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
+ * @param instance_id instance to lookup template for
+ * @param otp_id OTP device to lookup
+ * @param[out] td set to the OTP device details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_select_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id,
+                   struct TALER_MERCHANTDB_OtpDeviceDetails *td);
 
 #endif
diff --git a/src/backenddb/pg_select_otp_serial.c 
b/src/backenddb/pg_select_otp_serial.c
new file mode 100644
index 00000000..c2011aad
--- /dev/null
+++ b/src/backenddb/pg_select_otp_serial.c
@@ -0,0 +1,61 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_select_otp_serial.c
+ * @brief Implementation of the select_otp_serial function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_select_otp_serial.h"
+#include "pg_helper.h"
+
+
+enum GNUNET_DB_QueryStatus
+TMH_PG_select_otp_serial (void *cls,
+                          const char *instance_id,
+                          const char *otp_id,
+                          uint64_t *serial)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (otp_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint64 ("otp_serial",
+                                  serial),
+    GNUNET_PQ_result_spec_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "select_otp_serial",
+           "SELECT"
+           " otp_serial"
+           " FROM merchant_otp_devices"
+           " JOIN merchant_instances"
+           "   USING (merchant_serial)"
+           " WHERE merchant_instances.merchant_id=$1"
+           "   AND merchant_otp_devices.otp_id=$2");
+  return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
+                                                   "select_otp_serial",
+                                                   params,
+                                                   rs);
+}
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_select_otp_serial.h
similarity index 58%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_select_otp_serial.h
index 52b476d9..46d128ae 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_select_otp_serial.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_select_otp_serial.h
+ * @brief implementation of the select_otp_serial function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_SELECT_OTP_SERIAL_H
+#define PG_SELECT_OTP_SERIAL_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,17 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Lookup serial number of an OTP device.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
- * @return database result code
+ * @param instance_id instance to lookup template for
+ * @param otp_id OTP device to lookup
+ * @param[out] serial set to the OTP device serial number   * @return database 
result code
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_select_otp_serial (void *cls,
+                          const char *instance_id,
+                          const char *otp_id,
+                          uint64_t *serial);
 
 #endif
diff --git a/src/backenddb/pg_update_account.c 
b/src/backenddb/pg_update_account.c
index 3e36596d..7458c095 100644
--- a/src/backenddb/pg_update_account.c
+++ b/src/backenddb/pg_update_account.c
@@ -30,19 +30,20 @@ enum GNUNET_DB_QueryStatus
 TMH_PG_update_account (
   void *cls,
   const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details)
+  const struct TALER_MerchantWireHashP *h_wire,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (id),
-    GNUNET_PQ_query_param_auto_from_type (&account_details->h_wire),
-    NULL ==account_details->credit_facade_url
+    GNUNET_PQ_query_param_auto_from_type (h_wire),
+    NULL == credit_facade_url
     ? GNUNET_PQ_query_param_null ()
-    : GNUNET_PQ_query_param_string (account_details->credit_facade_url),
-    NULL == account_details->credit_facade_credentials
+    : GNUNET_PQ_query_param_string (credit_facade_url),
+    NULL == credit_facade_credentials
     ? GNUNET_PQ_query_param_null ()
-    : TALER_PQ_query_param_json (account_details->credit_facade_credentials),
-    GNUNET_PQ_query_param_bool (account_details->active),
+    : TALER_PQ_query_param_json (credit_facade_credentials),
     GNUNET_PQ_query_param_end
   };
 
@@ -52,7 +53,6 @@ TMH_PG_update_account (
            "UPDATE merchant_accounts SET"
            " credit_facade_url=$3"
            ",credit_facade_credentials=COALESCE($4,credit_facade_credentials)"
-           ",active=$5"
            " WHERE h_wire=$2"
            "   AND merchant_serial="
            "   (SELECT merchant_serial"
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_update_account.h
index 52b476d9..794b99d8 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_update_account.h
@@ -31,14 +31,18 @@
  *
  * @param cls closure
  * @param id identifier of the instance
- * @param account_details details about the account
+ * @param h_wire which account to update
+ * @param credit_facade_url new facade URL, can be NULL
+ * @param credit_facade_credentials new credentials, can be NULL to keep 
previous credentials
  * @return database result code
  */
 enum GNUNET_DB_QueryStatus
 TMH_PG_update_account (
   void *cls,
   const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
+  const struct TALER_MerchantWireHashP *h_wire,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials);
 
 
 #endif
diff --git a/src/backenddb/pg_update_instance.c 
b/src/backenddb/pg_update_instance.c
index 0f44c833..228e2031 100644
--- a/src/backenddb/pg_update_instance.c
+++ b/src/backenddb/pg_update_instance.c
@@ -26,6 +26,7 @@
 #include "pg_update_instance.h"
 #include "pg_helper.h"
 
+
 enum GNUNET_DB_QueryStatus
 TMH_PG_update_instance (void *cls,
                         const struct TALER_MERCHANTDB_InstanceSettings *is)
@@ -40,7 +41,8 @@ TMH_PG_update_instance (void *cls,
     GNUNET_PQ_query_param_bool (is->use_stefan),
     GNUNET_PQ_query_param_relative_time (
       &is->default_wire_transfer_delay),
-    GNUNET_PQ_query_param_relative_time (&is->default_pay_delay),
+    GNUNET_PQ_query_param_relative_time (
+      &is->default_pay_delay),
     (NULL == is->website)
     ? GNUNET_PQ_query_param_null ()
     : GNUNET_PQ_query_param_string (is->website),
diff --git a/src/backenddb/pg_update_otp.c b/src/backenddb/pg_update_otp.c
new file mode 100644
index 00000000..8f6c7176
--- /dev/null
+++ b/src/backenddb/pg_update_otp.c
@@ -0,0 +1,76 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_update_otp.c
+ * @brief Implementation of the update_otp function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_update_otp.h"
+#include "pg_helper.h"
+
+
+/**
+ * Update details about a particular OTP device.
+ *
+ * @param cls closure
+ * @param instance_id instance to update OTP device for
+ * @param otp_id OTP device to update
+ * @param td update to the OTP device details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+ *         does not yet exist.
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_update_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id,
+                   const struct TALER_MERCHANTDB_OtpDeviceDetails *td)
+{
+  struct PostgresClosure *pg = cls;
+  uint32_t pos32 = (uint32_t) td->otp_algorithm;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (otp_id),
+    GNUNET_PQ_query_param_string (td->otp_description),
+    GNUNET_PQ_query_param_uint32 (&pos32),
+    GNUNET_PQ_query_param_uint64 (&td->otp_ctr),
+    GNUNET_PQ_query_param_string (td->otp_key),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "update_otp",
+           "UPDATE merchant_otp_devices SET"
+           " otp_description=$3"
+           ",otp_algorithm=$4"
+           ",otp_ctr=$5"
+           ",otp_key=$6"
+           " WHERE merchant_serial="
+           "   (SELECT merchant_serial"
+           "      FROM merchant_instances"
+           "      WHERE merchant_id=$1)"
+           "   AND otp_id=$2");
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "update_otp",
+                                             params);
+}
+
+
diff --git a/src/backenddb/pg_update_account.h b/src/backenddb/pg_update_otp.h
similarity index 52%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_update_otp.h
index 52b476d9..7568608b 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_update_otp.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_update_otp.h
+ * @brief implementation of the update_otp function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_UPDATE_OTP_H
+#define PG_UPDATE_OTP_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,21 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Update details about a particular OTP device.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
- * @return database result code
+ * @param instance_id instance to update OTP device for
+ * @param otp_id OTP device to update
+ * @param td update to the OTP device details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+ *         does not yet exist.
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
+TMH_PG_update_otp (void *cls,
+                   const char *instance_id,
+                   const char *otp_id,
+                   const struct TALER_MERCHANTDB_OtpDeviceDetails *td);
 
 
 #endif
diff --git a/src/backenddb/pg_update_template.c 
b/src/backenddb/pg_update_template.c
new file mode 100644
index 00000000..9f7f37c3
--- /dev/null
+++ b/src/backenddb/pg_update_template.c
@@ -0,0 +1,85 @@
+/*
+   This file is part of TALER
+   Copyright (C) 2022 Taler Systems SA
+
+   TALER is free software; you can redistribute it and/or modify it under the
+   terms of the GNU General Public License as published by the Free Software
+   Foundation; either version 3, or (at your option) any later version.
+
+   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
FOR
+   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along with
+   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+/**
+ * @file backenddb/pg_update_template.c
+ * @brief Implementation of the update_template function for Postgres
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_error_codes.h>
+#include <taler/taler_dbevents.h>
+#include <taler/taler_pq_lib.h>
+#include "pg_update_template.h"
+#include "pg_helper.h"
+
+
+/**
+ * Update details about a particular template.
+ *
+ * @param cls closure
+ * @param instance_id instance to update template for
+ * @param template_id template to update
+ * @param td update to the template details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+ *         does not yet exist.
+ */
+enum GNUNET_DB_QueryStatus
+TMH_PG_update_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id,
+                        const struct TALER_MERCHANTDB_TemplateDetails *td)
+{
+  struct PostgresClosure *pg = cls;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_string (instance_id),
+    GNUNET_PQ_query_param_string (template_id),
+    GNUNET_PQ_query_param_string (td->template_description),
+    (NULL == td->otp_id)
+    ? GNUNET_PQ_query_param_null ()
+    : GNUNET_PQ_query_param_string (td->otp_id),
+    TALER_PQ_query_param_json (td->template_contract),
+    GNUNET_PQ_query_param_end
+  };
+
+  check_connection (pg);
+  PREPARE (pg,
+           "update_template",
+           "WITH mid AS ("
+           "  SELECT merchant_serial"
+           "    FROM merchant_instances"
+           "   WHERE merchant_id=$1)"
+           ",otp AS ("
+           "   SELECT otp_serial"
+           "     FROM merchant_otp_devices"
+           "     JOIN mid USING (merchant_serial)"
+           "    WHERE otp_id=$4)"
+           "UPDATE merchant_template SET"
+           " template_description=$3"
+           ",otp_device_id="
+           "  COALESCE((SELECT otp_serial"
+           "            FROM otp), NULL)"
+           ",template_contract=$5"
+           " WHERE merchant_serial="
+           "   (SELECT merchant_serial"
+           "      FROM mid)"
+           "   AND template_id=$2");
+  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
+                                             "update_template",
+                                             params);
+}
+
+
diff --git a/src/backenddb/pg_update_account.h 
b/src/backenddb/pg_update_template.h
similarity index 51%
copy from src/backenddb/pg_update_account.h
copy to src/backenddb/pg_update_template.h
index 52b476d9..26b932a2 100644
--- a/src/backenddb/pg_update_account.h
+++ b/src/backenddb/pg_update_template.h
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2023 Taler Systems SA
+   Copyright (C) 2022 Taler Systems SA
 
    TALER is free software; you can redistribute it and/or modify it under the
    terms of the GNU General Public License as published by the Free Software
@@ -14,12 +14,12 @@
    TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 /**
- * @file backenddb/pg_update_account.h
- * @brief implementation of the update_account function for Postgres
+ * @file backenddb/pg_update_template.h
+ * @brief implementation of the update_template function for Postgres
  * @author Christian Grothoff
  */
-#ifndef PG_UPDATE_ACCOUNT_H
-#define PG_UPDATE_ACCOUNT_H
+#ifndef PG_UPDATE_TEMPLATE_H
+#define PG_UPDATE_TEMPLATE_H
 
 #include <taler/taler_util.h>
 #include <taler/taler_json_lib.h>
@@ -27,18 +27,20 @@
 
 
 /**
- * Update information about an instance's account in our database.
+ * Update details about a particular template.
  *
  * @param cls closure
- * @param id identifier of the instance
- * @param account_details details about the account
- * @return database result code
+ * @param instance_id instance to update template for
+ * @param template_id template to update
+ * @param td update to the template details on success, can be NULL
+ *             (in that case we only want to check if the template exists)
+ * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+ *         does not yet exist.
  */
 enum GNUNET_DB_QueryStatus
-TMH_PG_update_account (
-  void *cls,
-  const char *id,
-  const struct TALER_MERCHANTDB_AccountDetails *account_details);
-
+TMH_PG_update_template (void *cls,
+                        const char *instance_id,
+                        const char *template_id,
+                        const struct TALER_MERCHANTDB_TemplateDetails *td);
 
 #endif
diff --git a/src/backenddb/plugin_merchantdb_postgres.c 
b/src/backenddb/plugin_merchantdb_postgres.c
index 70a3e644..c4074c98 100644
--- a/src/backenddb/plugin_merchantdb_postgres.c
+++ b/src/backenddb/plugin_merchantdb_postgres.c
@@ -31,6 +31,11 @@
 #include <taler/taler_mhd_lib.h>
 #include "taler_merchantdb_plugin.h"
 #include "pg_helper.h"
+#include "pg_insert_otp.h"
+#include "pg_delete_otp.h"
+#include "pg_update_otp.h"
+#include "pg_select_otp.h"
+#include "pg_select_otp_serial.h"
 #include "pg_insert_account.h"
 #include "pg_update_account.h"
 #include "pg_lookup_instances.h"
@@ -43,6 +48,7 @@
 #include "pg_insert_exchange_account.h"
 #include "pg_lookup_reserves.h"
 #include "pg_lookup_instance_auth.h"
+#include "pg_lookup_otp_devices.h"
 #include "pg_update_transfer_status.h"
 #include "pg_insert_instance.h"
 #include "pg_account_kyc_set_status.h"
@@ -72,6 +78,11 @@
 #include "pg_insert_contract_terms.h"
 #include "pg_update_contract_terms.h"
 #include "pg_delete_contract_terms.h"
+#include "pg_delete_template.h"
+#include "pg_insert_template.h"
+#include "pg_update_template.h"
+#include "pg_lookup_templates.h"
+#include "pg_lookup_template.h"
 #include "pg_lookup_deposits.h"
 #include "pg_insert_exchange_signkey.h"
 #include "pg_insert_deposit.h"
@@ -86,6 +97,8 @@
 #include "pg_select_exchange_keys.h"
 #include "pg_insert_deposit_to_transfer.h"
 #include "pg_increase_refund.h"
+#include "pg_select_account.h"
+#include "pg_select_accounts.h"
 #include "pg_insert_transfer.h"
 #include "pg_insert_transfer_details.h"
 #include "pg_store_wire_fee_by_exchange.h"
@@ -2308,283 +2321,6 @@ postgres_insert_pickup_blind_signature (
 }
 
 
-/**
- * Delete information about a template.
- *
- * @param cls closure
- * @param instance_id instance to delete template of
- * @param template_id template to delete
- * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
- *           if template unknown.
- */
-static enum GNUNET_DB_QueryStatus
-postgres_delete_template (void *cls,
-                          const char *instance_id,
-                          const char *template_id)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (template_id),
-    GNUNET_PQ_query_param_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "delete_template",
-                                             params);
-}
-
-
-/**
- * Insert details about a particular template.
- *
- * @param cls closure
- * @param instance_id instance to insert template for
- * @param template_id template identifier of template to insert
- * @param td the template details to insert
- * @return database result code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_template (void *cls,
-                          const char *instance_id,
-                          const char *template_id,
-                          const struct TALER_MERCHANTDB_TemplateDetails *td)
-{
-  struct PostgresClosure *pg = cls;
-  uint32_t pos32 = (uint32_t) td->pos_algorithm;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (template_id),
-    GNUNET_PQ_query_param_string (td->template_description),
-    (NULL == td->pos_key)
-    ? GNUNET_PQ_query_param_null ()
-    : GNUNET_PQ_query_param_string (td->pos_key),
-    GNUNET_PQ_query_param_uint32 (&pos32),
-    TALER_PQ_query_param_json (td->template_contract),
-    GNUNET_PQ_query_param_end
-  };
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "insert_template",
-                                             params);
-}
-
-
-/**
- * Update details about a particular template.
- *
- * @param cls closure
- * @param instance_id instance to update template for
- * @param template_id template to update
- * @param td update to the template details on success, can be NULL
- *             (in that case we only want to check if the template exists)
- * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
- *         does not yet exist.
- */
-static enum GNUNET_DB_QueryStatus
-postgres_update_template (void *cls,
-                          const char *instance_id,
-                          const char *template_id,
-                          const struct TALER_MERCHANTDB_TemplateDetails *td)
-{
-  struct PostgresClosure *pg = cls;
-  uint32_t pos32 = (uint32_t) td->pos_algorithm;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (template_id),
-    GNUNET_PQ_query_param_string (td->template_description),
-    (NULL == td->pos_key)
-    ? GNUNET_PQ_query_param_null ()
-    : GNUNET_PQ_query_param_string (td->pos_key),
-    GNUNET_PQ_query_param_uint32 (&pos32),
-    TALER_PQ_query_param_json (td->template_contract),
-    GNUNET_PQ_query_param_end
-  };
-
-
-  check_connection (pg);
-  return GNUNET_PQ_eval_prepared_non_select (pg->conn,
-                                             "update_template",
-                                             params);
-}
-
-
-/**
- * Context used for postgres_lookup_template().
- */
-struct LookupTemplateContext
-{
-  /**
-   * Function to call with the results.
-   */
-  TALER_MERCHANTDB_TemplatesCallback cb;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Did database result extraction fail?
-   */
-  bool extract_failed;
-};
-
-
-/**
- * Function to be called with the results of a SELECT statement
- * that has returned @a num_results results about template.
- *
- * @param[in,out] cls of type `struct LookupTemplateContext *`
- * @param result the postgres result
- * @param num_results the number of results in @a result
- */
-static void
-lookup_templates_cb (void *cls,
-                     PGresult *result,
-                     unsigned int num_results)
-{
-  struct LookupTemplateContext *tlc = cls;
-
-  for (unsigned int i = 0; i < num_results; i++)
-  {
-    char *template_id;
-    char *template_description;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_string ("template_id",
-                                    &template_id),
-      GNUNET_PQ_result_spec_string ("template_description",
-                                    &template_description),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
-    {
-      GNUNET_break (0);
-      tlc->extract_failed = true;
-      return;
-    }
-    tlc->cb (tlc->cb_cls,
-             template_id,
-             template_description);
-    GNUNET_PQ_cleanup_result (rs);
-  }
-}
-
-
-/**
- * Lookup all of the templates the given instance has configured.
- *
- * @param cls closure
- * @param instance_id instance to lookup template for
- * @param cb function to call on all template found
- * @param cb_cls closure for @a cb
- * @return database result code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_lookup_templates (void *cls,
-                           const char *instance_id,
-                           TALER_MERCHANTDB_TemplatesCallback cb,
-                           void *cb_cls)
-{
-  struct PostgresClosure *pg = cls;
-  struct LookupTemplateContext tlc = {
-    .cb = cb,
-    .cb_cls = cb_cls,
-    /* Can be overwritten by the lookup_template_cb */
-    .extract_failed = false,
-  };
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_end
-  };
-  enum GNUNET_DB_QueryStatus qs;
-
-  check_connection (pg);
-  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
-                                             "lookup_templates",
-                                             params,
-                                             &lookup_templates_cb,
-                                             &tlc);
-  /* If there was an error inside lookup_template_cb, return a hard error. */
-  if (tlc.extract_failed)
-    return GNUNET_DB_STATUS_HARD_ERROR;
-  return qs;
-}
-
-
-/**
- * Lookup details about a particular template.
- *
- * @param cls closure
- * @param instance_id instance to lookup template for
- * @param template_id template to lookup
- * @param[out] td set to the template details on success, can be NULL
- *             (in that case we only want to check if the template exists)
- * @return database result code
- */
-static enum GNUNET_DB_QueryStatus
-postgres_lookup_template (void *cls,
-                          const char *instance_id,
-                          const char *template_id,
-                          struct TALER_MERCHANTDB_TemplateDetails *td)
-{
-  struct PostgresClosure *pg = cls;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_string (instance_id),
-    GNUNET_PQ_query_param_string (template_id),
-    GNUNET_PQ_query_param_end
-  };
-
-  if (NULL == td)
-  {
-    struct GNUNET_PQ_ResultSpec rs_null[] = {
-      GNUNET_PQ_result_spec_end
-    };
-
-    check_connection (pg);
-    return GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                     "lookup_template",
-                                                     params,
-                                                     rs_null);
-  }
-  else
-  {
-    uint32_t pos32;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_string ("template_description",
-                                    &td->template_description),
-      GNUNET_PQ_result_spec_allow_null (
-        GNUNET_PQ_result_spec_string ("pos_key",
-                                      &td->pos_key),
-        NULL),
-      GNUNET_PQ_result_spec_allow_null (
-        GNUNET_PQ_result_spec_uint32 ("pos_algorithm",
-                                      &pos32),
-        NULL),
-      TALER_PQ_result_spec_json ("template_contract",
-                                 &td->template_contract),
-      GNUNET_PQ_result_spec_end
-    };
-    enum GNUNET_DB_QueryStatus qs;
-
-    check_connection (pg);
-    td->pos_key = NULL;
-    qs = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
-                                                   "lookup_template",
-                                                   params,
-                                                   rs);
-    td->pos_algorithm = (enum TALER_MerchantConfirmationAlgorithm) pos32;
-    return qs;
-  }
-}
-
-
 /**
  * Delete information about a webhook.
  *
@@ -3747,62 +3483,6 @@ postgres_connect (void *cls)
                             " pickup_serial, $2, $3"
                             " FROM merchant_reward_pickups"
                             " WHERE pickup_id=$1"),
-    /* for postgres_lookup_templates() */
-    GNUNET_PQ_make_prepare ("lookup_templates",
-                            "SELECT"
-                            " template_id"
-                            ",template_description"
-                            " FROM merchant_template"
-                            " JOIN merchant_instances"
-                            "   USING (merchant_serial)"
-                            " WHERE merchant_instances.merchant_id=$1"),
-    /* for postgres_lookup_template() */
-    GNUNET_PQ_make_prepare ("lookup_template",
-                            "SELECT"
-                            " template_description"
-                            ",pos_key"
-                            ",pos_algorithm"
-                            ",template_contract"
-                            " FROM merchant_template"
-                            " JOIN merchant_instances"
-                            "   USING (merchant_serial)"
-                            " WHERE merchant_instances.merchant_id=$1"
-                            "   AND merchant_template.template_id=$2"),
-    /* for postgres_delete_template() */
-    GNUNET_PQ_make_prepare ("delete_template",
-                            "DELETE"
-                            " FROM merchant_template"
-                            " WHERE merchant_template.merchant_serial="
-                            "     (SELECT merchant_serial "
-                            "        FROM merchant_instances"
-                            "        WHERE merchant_id=$1)"
-                            "   AND merchant_template.template_id=$2"),
-    /* for postgres_insert_template() */
-    GNUNET_PQ_make_prepare ("insert_template",
-                            "INSERT INTO merchant_template"
-                            "(merchant_serial"
-                            ",template_id"
-                            ",template_description"
-                            ",pos_key"
-                            ",pos_algorithm"
-                            ",template_contract"
-                            ")"
-                            " SELECT merchant_serial,"
-                            " $2, $3, $4, $5, $6"
-                            " FROM merchant_instances"
-                            " WHERE merchant_id=$1"),
-    /* for postgres_update_template() */
-    GNUNET_PQ_make_prepare ("update_template",
-                            "UPDATE merchant_template SET"
-                            " template_description=$3"
-                            ",pos_key=$4"
-                            ",pos_algorithm=$5"
-                            ",template_contract=$6"
-                            " WHERE merchant_serial="
-                            "   (SELECT merchant_serial"
-                            "      FROM merchant_instances"
-                            "      WHERE merchant_id=$1)"
-                            "   AND template_id=$2"),
     /* for postgres_lookup_webhooks() */
     GNUNET_PQ_make_prepare ("lookup_webhooks",
                             "SELECT"
@@ -4010,6 +3690,18 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     = &TMH_PG_insert_instance;
   plugin->insert_account
     = &TMH_PG_insert_account;
+  plugin->lookup_otp_devices
+    = &TMH_PG_lookup_otp_devices;
+  plugin->delete_template
+    = &TMH_PG_delete_template;
+  plugin->insert_template
+    = &TMH_PG_insert_template;
+  plugin->update_template
+    = &TMH_PG_update_template;
+  plugin->lookup_templates
+    = &TMH_PG_lookup_templates;
+  plugin->lookup_template
+    = &TMH_PG_lookup_template;
   plugin->update_account
     = &TMH_PG_update_account;
   plugin->account_kyc_set_status
@@ -4040,6 +3732,16 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     = &TMH_PG_insert_product;
   plugin->update_product
     = &TMH_PG_update_product;
+  plugin->insert_otp
+    = &TMH_PG_insert_otp;
+  plugin->delete_otp
+    = &TMH_PG_delete_otp;
+  plugin->update_otp
+    = &TMH_PG_update_otp;
+  plugin->select_otp
+    = &TMH_PG_select_otp;
+  plugin->select_otp_serial
+    = &TMH_PG_select_otp_serial;
   plugin->lock_product
     = &TMH_PG_lock_product;
   plugin->expire_locks
@@ -4117,6 +3819,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     = &TMH_PG_update_wirewatch_progress;
   plugin->select_wirewatch_accounts
     = &TMH_PG_select_wirewatch_accounts;
+  plugin->select_account
+    = &TMH_PG_select_account;
+  plugin->select_accounts
+    = &TMH_PG_select_accounts;
   plugin->lookup_reserves
     = &TMH_PG_lookup_reserves;
   plugin->lookup_pending_reserves = &postgres_lookup_pending_reserves;
@@ -4151,11 +3857,6 @@ libtaler_plugin_merchantdb_postgres_init (void *cls)
     = &TMH_PG_authorize_reward;
   plugin->insert_pickup
     = &TMH_PG_insert_pickup;
-  plugin->lookup_templates = &postgres_lookup_templates;
-  plugin->lookup_template = &postgres_lookup_template;
-  plugin->delete_template = &postgres_delete_template;
-  plugin->insert_template = &postgres_insert_template;
-  plugin->update_template = &postgres_update_template;
   plugin->lookup_webhooks = &postgres_lookup_webhooks;
   plugin->lookup_webhook = &postgres_lookup_webhook;
   plugin->delete_webhook = &postgres_delete_webhook;
diff --git a/src/backenddb/test_merchantdb.c b/src/backenddb/test_merchantdb.c
index 58470786..214bd5f8 100644
--- a/src/backenddb/test_merchantdb.c
+++ b/src/backenddb/test_merchantdb.c
@@ -259,6 +259,19 @@ check_accounts_equal (const struct 
TALER_MERCHANTDB_AccountDetails *a,
 }
 
 
+// FIXME: use this!
+void
+lookup_accounts_cb (void *cls,
+                    const struct TALER_MERCHANTDB_AccountDetails *account)
+{
+  const struct TALER_MERCHANTDB_AccountDetails *want = cls;
+
+  GNUNET_assert (0 ==
+                 check_accounts_equal (want,
+                                       account));
+}
+
+
 /**
  * Called after testing 'lookup_instances'.
  *
@@ -267,54 +280,33 @@ check_accounts_equal (const struct 
TALER_MERCHANTDB_AccountDetails *a,
  * @param merchant_priv private key of the instance, NULL if not available
  * @param is general instance settings
  * @param ias instance authentication settings
- * @param accounts_length length of the @a accounts array
- * @param accounts list of accounts of the merchant
 */
 static void
 lookup_instances_cb (void *cls,
                      const struct TALER_MerchantPublicKeyP *merchant_pub,
                      const struct TALER_MerchantPrivateKeyP *merchant_priv,
                      const struct TALER_MERCHANTDB_InstanceSettings *is,
-                     const struct TALER_MERCHANTDB_InstanceAuthSettings *ias,
-                     unsigned int accounts_length,
-                     const struct TALER_MERCHANTDB_AccountDetails accounts[])
+                     const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
 {
   struct TestLookupInstances_Closure *cmp = cls;
+
   if (NULL == cmp)
     return;
   cmp->results_length += 1;
   /* Look through the closure and test each instance for equality */
   for (unsigned int i = 0; cmp->instances_to_cmp_length > i; ++i)
   {
-    int accounts_matching[GNUNET_NZL (accounts_length)];
-    bool accounts_match = true;
     if (0 != check_instances_equal (cmp->instances_to_cmp[i].instance,
                                     is))
       continue;
-    if (accounts_length != cmp->instances_to_cmp[i].accounts_length)
-      continue;
-    /* Count matches between the accounts found and accounts in cls */
-    for (unsigned int j = 0; accounts_length > j; ++j)
-      accounts_matching[j] = 0;
-    for (unsigned int j = 0; accounts_length > j; ++j)
-    {
-      for (unsigned int k = 0; accounts_length > k; ++k)
-      {
-        if (0 == check_accounts_equal (&cmp->instances_to_cmp[i].accounts[j],
-                                       &accounts[k]))
-          accounts_matching[j] += 1;
-      }
-    }
-    /* Each account from the lookup should match with one and only one from 
cls */
-    for (unsigned int j = 0; accounts_length > j; ++j)
-      if (1 != accounts_matching[j])
-        accounts_match = false;
-    if (true == accounts_match)
-      cmp->results_matching[i] += 1;
+    cmp->results_matching[i] += 1;
   }
 }
 
 
+
+
+
 /**
  * Tests @e insert_instance.
  *
@@ -6866,8 +6858,7 @@ make_template (const char *id,
 {
   template->id = id;
   template->template.template_description = "This is a test template";
-  template->template.pos_key = NULL;
-  template->template.pos_algorithm = 0;
+  template->template.otp_id = NULL;
   template->template.template_contract = json_array ();
   GNUNET_assert (NULL != template->template.template_contract);
 }
@@ -6881,7 +6872,7 @@ make_template (const char *id,
 static void
 free_template_data (struct TemplateData *template)
 {
-  GNUNET_free (template->template.pos_key);
+  GNUNET_free (template->template.otp_id);
   json_decref (template->template.template_contract);
 }
 
@@ -6899,9 +6890,10 @@ check_templates_equal (const struct 
TALER_MERCHANTDB_TemplateDetails *a,
 {
   if ((0 != strcmp (a->template_description,
                     b->template_description)) ||
-      ( (NULL == a->pos_key) && (NULL != b->pos_key)) ||
-      ( (NULL != a->pos_key) && (NULL == b->pos_key)) ||
-      ( (NULL != a->pos_key) && (0 != strcmp (a->pos_key, b->pos_key))) ||
+      ( (NULL == a->otp_id) && (NULL != b->otp_id)) ||
+      ( (NULL != a->otp_id) && (NULL == b->otp_id)) ||
+      ( (NULL != a->otp_id) && (0 != strcmp (a->otp_id,
+                                             b->otp_id))) ||
       (1 != json_equal (a->template_contract,
                         b->template_contract)))
     return 1;
@@ -6967,6 +6959,7 @@ test_lookup_template (const struct InstanceData *instance,
                       const struct TemplateData *template)
 {
   struct TALER_MERCHANTDB_TemplateDetails lookup_result;
+
   if (0 > plugin->lookup_template (plugin->cls,
                                    instance->instance.id,
                                    template->id,
@@ -7212,8 +7205,23 @@ run_test_templates (struct TestTemplates_Closure *cls)
   /* Test template update */
   cls->templates[0].template.template_description =
     "This is a test template that has been updated!";
-  GNUNET_free (cls->templates[0].template.pos_key);
-  cls->templates[0].template.pos_key = GNUNET_strdup ("pos_key");
+  GNUNET_free (cls->templates[0].template.otp_id);
+  cls->templates[0].template.otp_id = GNUNET_strdup ("otp_id");
+  {
+    /* ensure OTP device exists */
+    struct TALER_MERCHANTDB_OtpDeviceDetails td = {
+      .otp_description = "my otp",
+      .otp_key = "my key",
+      .otp_algorithm = 1,
+      .otp_ctr = 42
+    };
+    GNUNET_assert (0 <=
+                   plugin->insert_otp (plugin->cls,
+                                       cls->instance.instance.id,
+                                       "otp_id",
+                                       &td));
+  }
+
   GNUNET_assert (0 ==
                  json_array_append_new (
                    cls->templates[0].template.template_contract,
diff --git a/src/include/taler_merchant_service.h 
b/src/include/taler_merchant_service.h
index c41080be..e92ea3b8 100644
--- a/src/include/taler_merchant_service.h
+++ b/src/include/taler_merchant_service.h
@@ -523,38 +523,12 @@ typedef void
   const struct TALER_MERCHANT_HttpResponse *hr);
 
 
-/**
- * Information about an account of the merchant.
- */
-struct TALER_MERCHANT_AccountConfig
-{
-  /**
-   * Payto URI of the account.
-   */
-  const char *payto_uri;
-
-  /**
-   * Optional credit facade for the account.
-   * Can be NULL.
-   */
-  const char *credit_facade_url;
-
-  /**
-   * Credit facade credentials for the account.
-   * Can be NULL.
-   */
-  json_t *credit_facade_credentials;
-
-};
-
 /**
  * Setup an new instance in the backend.
  *
  * @param ctx the context
  * @param backend_url HTTP base URL for the backend
  * @param instance_id identity of the instance to get information about
- * @param accounts_length how many bank accounts this instance has
- * @param accounts the bank accounts of the merchant instance
  * @param name name of the merchant instance
  * @param ut user type of the merchant instance
  * @param address physical address of the merchant instance
@@ -573,8 +547,6 @@ TALER_MERCHANT_instances_post (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
   const char *instance_id,
-  unsigned int accounts_length,
-  const struct TALER_MERCHANT_AccountConfig accounts[],
   const char *name,
   enum TALER_KYCLOGIC_KycUserType ut,
   const json_t *address,
@@ -598,126 +570,6 @@ TALER_MERCHANT_instances_post_cancel (
   struct TALER_MERCHANT_InstancesPostHandle *iph);
 
 
-/**
- * Handle for a POST /instances/$ID/account operation.
- */
-struct TALER_MERCHANT_AccountPostHandle;
-
-
-/**
- * Response for a POST /instances/$ID/account operation.
- */
-struct TALER_MERCHANT_AccountPostResponse
-{
-  /**
-   * HTTP response data
-   */
-  struct TALER_MERCHANT_HttpResponse hr;
-};
-
-
-/**
- * Function called with the result of the POST /instances/$ID/account 
operation.
- *
- * @param cls closure
- * @param par response data
- */
-typedef void
-(*TALER_MERCHANT_AccountPostCallback)(
-  void *cls,
-  const struct TALER_MERCHANT_AccountPostResponse *par);
-
-
-/**
- * Setup an new account for an instance in the backend.
- *
- * @param ctx the context
- * @param backend_url HTTP base URL for the backend
- * @param account the bank accounts to add to the merchant instance
- * @param cb function to call with the response
- * @param cb_cls closure for @a config_cb
- * @return the instances handle; NULL upon error
- */
-struct TALER_MERCHANT_AccountPostHandle *
-TALER_MERCHANT_account_post (
-  struct GNUNET_CURL_Context *ctx,
-  const char *backend_url,
-  const struct TALER_MERCHANT_AccountConfig *account,
-  TALER_MERCHANT_AccountPostCallback cb,
-  void *cb_cls);
-
-
-/**
- * Cancel /account request.  Must not be called by clients after
- * the callback was invoked.
- *
- * @param pah request to cancel.
- */
-void
-TALER_MERCHANT_account_post_cancel (
-  struct TALER_MERCHANT_AccountPostHandle *pah);
-
-
-/**
- * Handle for a DELETE /instances/$ID/account/$H_WIRE operation.
- */
-struct TALER_MERCHANT_AccountDeleteHandle;
-
-
-/**
- * Response for a DELETE /instances/$ID/account operation.
- */
-struct TALER_MERCHANT_AccountDeleteResponse
-{
-  /**
-   * HTTP response data
-   */
-  struct TALER_MERCHANT_HttpResponse hr;
-};
-
-
-/**
- * Function called with the result of the DELETE 
/instances/$ID/account/$H_WIRE operation.
- *
- * @param cls closure
- * @param par response data
- */
-typedef void
-(*TALER_MERCHANT_AccountDeleteCallback)(
-  void *cls,
-  const struct TALER_MERCHANT_AccountDeleteResponse *par);
-
-
-/**
- * Remove bank account from an instance in the backend.
- *
- * @param ctx the context
- * @param backend_url HTTP base URL for the backend
- * @param h_wire wire hash of the bank accounts to delete
- * @param cb function to call with the response
- * @param cb_cls closure for @a config_cb
- * @return the instances handle; NULL upon error
- */
-struct TALER_MERCHANT_AccountDeleteHandle *
-TALER_MERCHANT_account_delete (
-  struct GNUNET_CURL_Context *ctx,
-  const char *backend_url,
-  const struct TALER_MerchantWireHashP *h_wire,
-  TALER_MERCHANT_AccountDeleteCallback cb,
-  void *cb_cls);
-
-
-/**
- * Cancel /account request.  Must not be called by clients after
- * the callback was invoked.
- *
- * @param pah request to cancel.
- */
-void
-TALER_MERCHANT_account_delete_cancel (
-  struct TALER_MERCHANT_AccountDeleteHandle *pah);
-
-
 /**
  * Handle for a PATCH /instances/$ID operation.
  */
@@ -744,8 +596,6 @@ typedef void
  *                    or base URL of an instance if @a instance_id is NULL)
  * @param instance_id identity of the instance to modify information about; 
NULL
  *                    if the instance is identified as part of the @a 
backend_url
- * @param accounts_length length of the @a accounts array
- * @param accounts the bank accounts of the merchant instance
  * @param name name of the merchant instance
  * @param ut user type of the merchant instance
  * @param address physical address of the merchant instance
@@ -763,8 +613,6 @@ TALER_MERCHANT_instance_patch (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
   const char *instance_id,
-  unsigned int accounts_length,
-  const struct TALER_MERCHANT_AccountConfig accounts[static accounts_length],
   const char *name,
   enum TALER_KYCLOGIC_KycUserType ut,
   const json_t *address,
@@ -847,39 +695,6 @@ TALER_MERCHANT_instance_auth_post_cancel (
 struct TALER_MERCHANT_InstanceGetHandle;
 
 
-/**
- * Details about a merchant's bank account.
- */
-struct TALER_MERCHANT_Account
-{
-  /**
-   * salt used to compute h_wire
-   */
-  struct TALER_WireSaltP salt;
-
-  /**
-   * payto:// URI of the account.
-   */
-  const char *payto_uri;
-
-  /**
-   * Credit facade URL of the account.
-   */
-  const char *credit_facade_url;
-
-  /**
-   * Hash of @e payto_uri and @e salt.
-   */
-  struct TALER_MerchantWireHashP h_wire;
-
-  /**
-   * true if the account is active,
-   * false if it is historic.
-   */
-  bool active;
-};
-
-
 /**
  * Details about an instance.
  */
@@ -943,16 +758,6 @@ struct TALER_MERCHANT_InstanceGetResponse
      */
     struct
     {
-      /**
-       * Length of the @e accounts array.
-       */
-      unsigned int accounts_length;
-
-      /**
-       * bank accounts of the merchant instance
-       */
-      const struct TALER_MERCHANT_Account *accounts;
-
       /**
        * Details about the instance.
        */
@@ -1093,6 +898,420 @@ TALER_MERCHANT_instance_delete_cancel (
   TALER_MERCHANT_instance_delete_cancel (arg)
 
 
+/* *************** Accounts **************** */
+
+/**
+ * Handle for a POST /instances/$ID/accounts operation.
+ */
+struct TALER_MERCHANT_AccountsPostHandle;
+
+
+/**
+ * Response for a POST /instances/$ID/account operation.
+ */
+struct TALER_MERCHANT_AccountsPostResponse
+{
+  /**
+   * HTTP response data
+   */
+  struct TALER_MERCHANT_HttpResponse hr;
+
+  /**
+   * Details depending on HTTP status.
+   */
+  union {
+
+    /**
+     * Details returned on #MHD_HTTP_OK.
+     */
+    struct {
+
+      /**
+       * Hash of @e payto_uri and @e salt.
+       */
+      struct TALER_MerchantWireHashP h_wire;
+
+      /**
+       * salt used to compute h_wire
+       */
+      struct TALER_WireSaltP salt;
+    } ok;
+
+  } details;
+};
+
+
+/**
+ * Function called with the result of the POST /instances/$ID/accounts 
operation.
+ *
+ * @param cls closure
+ * @param par response data
+ */
+typedef void
+(*TALER_MERCHANT_AccountsPostCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_AccountsPostResponse *par);
+
+
+/**
+ * Setup an new account for an instance in the backend.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param payto_uri URI of the bank account as per RFC 8905
+ * @param credit_facade_url credit facade for the account, can be NULL
+ * @param credit_facade_credentials credentials for credit facade, can be NULL
+ * @param account the bank accounts to add to the merchant instance
+ * @param cb function to call with the response
+ * @param cb_cls closure for @a config_cb
+ * @return the instances handle; NULL upon error
+ */
+struct TALER_MERCHANT_AccountsPostHandle *
+TALER_MERCHANT_accounts_post (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *payto_uri,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials,
+  TALER_MERCHANT_AccountsPostCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel POST /accounts request.  Must not be called by clients after
+ * the callback was invoked.
+ *
+ * @param pah request to cancel.
+ */
+void
+TALER_MERCHANT_accounts_post_cancel (
+  struct TALER_MERCHANT_AccountsPostHandle *pah);
+
+
+/**
+ * Handle for a GET /accounts/$ID operation.
+ */
+struct TALER_MERCHANT_AccountGetHandle;
+
+
+/**
+ * Details about a merchant's bank account.
+ */
+struct TALER_MERCHANT_AccountDetails
+{
+  /**
+   * salt used to compute h_wire
+   */
+  struct TALER_WireSaltP salt;
+
+  /**
+   * payto:// URI of the account.
+   */
+  const char *payto_uri;
+
+  /**
+   * Credit facade URL of the account.
+   */
+  const char *credit_facade_url;
+
+  /**
+   * Hash of @e payto_uri and @e salt.
+   */
+  struct TALER_MerchantWireHashP h_wire;
+
+  /**
+   * true if the account is active,
+   * false if it is historic.
+   */
+  bool active;
+};
+
+
+/**
+ * Response returned with details about an account.
+ */
+struct TALER_MERCHANT_AccountGetResponse
+{
+  /**
+   * HTTP response data
+   */
+  struct TALER_MERCHANT_HttpResponse hr;
+
+  union
+  {
+
+    /**
+     * Data returned on #MHD_HTTP_OK.
+     */
+    struct
+    {
+
+      /**
+       * bank accounts of the merchant instance
+       */
+      struct TALER_MERCHANT_AccountDetails ad;
+
+    } ok;
+  }
+  details;
+};
+
+
+/**
+ * Function called with the result of the GET /instances/$ID/accounts/$H_WIRE 
operation.
+ *
+ * @param cls closure
+ * @param igr response details
+ */
+typedef void
+(*TALER_MERCHANT_AccountGetCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_AccountGetResponse *igr);
+
+
+/**
+ * Get the details on one of the accounts of an instance. Will connect to the
+ * merchant backend and obtain information about the account.  The respective
+ * information will be passed to the @a cb once available.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param instance_id identity of the instance to get information about
+ * @param h_wire hash of the wire details
+ * @param cb function to call with the
+ *        backend's instances information
+ * @param cb_cls closure for @a config_cb
+ * @return the instances handle; NULL upon error
+ */
+struct TALER_MERCHANT_AccountGetHandle *
+TALER_MERCHANT_account_get (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *instance_id,
+  const struct TALER_MerchantWireHashP *h_wire,
+  TALER_MERCHANT_AccountGetCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel GET /accounts/$H_WIRE request.  Must not be called by clients after
+ * the callback was invoked.
+ *
+ * @param igh request to cancel.
+ */
+void
+TALER_MERCHANT_account_get_cancel (
+  struct TALER_MERCHANT_AccountGetHandle *igh);
+
+
+/**
+ * Handle for a GET /accounts operation.
+ */
+struct TALER_MERCHANT_AccountsGetHandle;
+
+/**
+ * Individual account (minimal information
+ * returned via GET /accounts).
+ */
+struct TALER_MERCHANT_AccountEntry
+{
+  /**
+   * account payto URI.
+   */
+  const char *payto_uri;
+
+  /**
+   * Hash of @e payto_uri and salt.
+   */
+  struct TALER_MerchantWireHashP h_wire;
+
+};
+
+
+/**
+ * Response to a GET /accounts operation.
+ */
+struct TALER_MERCHANT_AccountsGetResponse
+{
+  /**
+   * HTTP response details
+   */
+  struct TALER_MERCHANT_HttpResponse hr;
+
+  /**
+   * Details depending on status.
+   */
+  union
+  {
+    /**
+     * Details if status is #MHD_HTTP_OK.
+     */
+    struct
+    {
+      /**
+       * length of the @e accounts array
+       */
+      unsigned int accounts_length;
+
+      /**
+       * array of accounts the requested instance offers
+       */
+      const struct TALER_MERCHANT_AccountEntry *accounts;
+    } ok;
+  } details;
+};
+
+
+/**
+ * Function called with the result of the GET /accounts operation.
+ *
+ * @param cls closure
+ * @param tgr response details
+ */
+typedef void
+(*TALER_MERCHANT_AccountsGetCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_AccountsGetResponse *tgr);
+
+
+/**
+ * Make a GET /accounts request.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param cb function to call with the backend information
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_AccountsGetHandle *
+TALER_MERCHANT_accounts_get (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  TALER_MERCHANT_AccountsGetCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel GET /accounts operation.
+ *
+ * @param tgh operation to cancel
+ */
+void
+TALER_MERCHANT_accounts_get_cancel (
+  struct TALER_MERCHANT_AccountsGetHandle *tgh);
+
+
+/**
+ * Handle for a PATCH /account operation.
+ */
+struct TALER_MERCHANT_AccountPatchHandle;
+
+
+/**
+ * Function called with the result of the PATCH /account operation.
+ *
+ * @param cls closure
+ * @param hr HTTP response details
+ */
+typedef void
+(*TALER_MERCHANT_AccountPatchCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_HttpResponse *hr);
+
+
+/**
+ * Make a PATCH /accounts/$H_WIRE request to update account details. Cannot be 
used to change the payto URI or the salt.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param h_wire identifies the account to patch
+ * @param credit_facade_url credit facade for the account, can be NULL
+ * @param credit_facade_credentials credentials for credit facade, can be NULL
+ * @param cb function to call with the backend's result
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_AccountPatchHandle *
+TALER_MERCHANT_account_patch (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const struct TALER_MerchantWireHashP *h_wire,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials,
+  TALER_MERCHANT_AccountPatchCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel PATCH /accounts/$H_WIRE operation.
+ *
+ * @param[in] tph operation to cancel
+ */
+void
+TALER_MERCHANT_account_patch_cancel (
+  struct TALER_MERCHANT_AccountPatchHandle *tph);
+
+
+/**
+ * Handle for a DELETE /instances/$ID/account/$H_WIRE operation.
+ */
+struct TALER_MERCHANT_AccountDeleteHandle;
+
+
+/**
+ * Response for a DELETE /instances/$ID/account operation.
+ */
+struct TALER_MERCHANT_AccountDeleteResponse
+{
+  /**
+   * HTTP response data
+   */
+  struct TALER_MERCHANT_HttpResponse hr;
+};
+
+
+/**
+ * Function called with the result of the DELETE 
/instances/$ID/account/$H_WIRE operation.
+ *
+ * @param cls closure
+ * @param par response data
+ */
+typedef void
+(*TALER_MERCHANT_AccountDeleteCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_AccountDeleteResponse *par);
+
+
+/**
+ * Remove bank account from an instance in the backend.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param h_wire wire hash of the bank accounts to delete
+ * @param cb function to call with the response
+ * @param cb_cls closure for @a config_cb
+ * @return the instances handle; NULL upon error
+ */
+struct TALER_MERCHANT_AccountDeleteHandle *
+TALER_MERCHANT_account_delete (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const struct TALER_MerchantWireHashP *h_wire,
+  TALER_MERCHANT_AccountDeleteCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel /account request.  Must not be called by clients after
+ * the callback was invoked.
+ *
+ * @param pah request to cancel.
+ */
+void
+TALER_MERCHANT_account_delete_cancel (
+  struct TALER_MERCHANT_AccountDeleteHandle *pah);
+
+
 /* ********************* /products *********************** */
 
 
@@ -4922,6 +5141,364 @@ TALER_MERCHANT_kyc_get_cancel (
   struct TALER_MERCHANT_KycGetHandle *kyc);
 
 
+/* ********************* /otp-devices *********************** */
+
+
+/**
+ * Handle for a GET /otp-devices operation.
+ */
+struct TALER_MERCHANT_OtpDevicesGetHandle;
+
+/**
+ * Individual OTP device (minimal information
+ * returned via GET /otp-devices).
+ */
+struct TALER_MERCHANT_OtpDeviceEntry
+{
+  /**
+   * OTP device identifier.
+   */
+  const char *otp_device_id;
+
+  /**
+   * OTP device description.
+   */
+  const char *device_description;
+
+};
+
+
+/**
+ * Response to a GET /otp-devices operation.
+ */
+struct TALER_MERCHANT_OtpDevicesGetResponse
+{
+  /**
+   * HTTP response details
+   */
+  struct TALER_MERCHANT_HttpResponse hr;
+
+  /**
+   * Details depending on status.
+   */
+  union
+  {
+    /**
+     * Details if status is #MHD_HTTP_OK.
+     */
+    struct
+    {
+      /**
+       * length of the @e otp_devices array
+       */
+      unsigned int otp_devices_length;
+
+      /**
+       * array of otp_devices the requested instance offers
+       */
+      const struct TALER_MERCHANT_OtpDeviceEntry *otp_devices;
+    } ok;
+  } details;
+};
+
+
+/**
+ * Function called with the result of the GET /otp-devices operation.
+ *
+ * @param cls closure
+ * @param tgr response details
+ */
+typedef void
+(*TALER_MERCHANT_OtpDevicesGetCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_OtpDevicesGetResponse *tgr);
+
+
+/**
+ * Make a GET /otp-devices request.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param cb function to call with the backend information
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_OtpDevicesGetHandle *
+TALER_MERCHANT_otp_devices_get (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  TALER_MERCHANT_OtpDevicesGetCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel GET /otp-devices operation.
+ *
+ * @param tgh operation to cancel
+ */
+void
+TALER_MERCHANT_otp_devices_get_cancel (
+  struct TALER_MERCHANT_OtpDevicesGetHandle *tgh);
+
+
+/**
+ * Handle for a GET /otp-device/$ID operation. Gets details
+ * about a single otp_device. Do not confused with a
+ * `struct TALER_MERCHANT_OtpDevicesGetHandle`, which
+ * obtains a list of all otp_devices.
+ */
+struct TALER_MERCHANT_OtpDeviceGetHandle;
+
+
+/**
+ * Details in a response to a GET /otp-devices request.
+ */
+struct TALER_MERCHANT_OtpDeviceGetResponse
+{
+  /**
+   * HTTP response details.
+   */
+  struct TALER_MERCHANT_HttpResponse hr;
+
+  /**
+   * Response details depending on the HTTP status.
+   */
+  union
+  {
+    /**
+     * Information returned if the status was #MHD_HTTP_OK.
+     */
+    struct
+    {
+
+      /**
+       * description of the otp_device
+       */
+      const char *otp_device_description;
+
+      /**
+       * OTP device key.
+       */
+      const char *otp_key;
+
+      /**
+       * current counter.
+       */
+      uint64_t otp_ctr;
+
+      /**
+       * OTP algorithm used.
+       */
+      enum TALER_MerchantConfirmationAlgorithm otp_alg;
+
+    } ok;
+
+  } details;
+
+};
+
+
+/**
+ * Function called with the result of the GET /otp-device/$ID operation.
+ *
+ * @param cls closure
+ * @param tgr HTTP response details
+ */
+typedef void
+(*TALER_MERCHANT_OtpDeviceGetCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_OtpDeviceGetResponse *tgr);
+
+
+/**
+ * Make a GET /otp-device/$ID request to get details about an
+ * individual OTP device.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param otp_device_id identifier of the otp_device to inquire about
+ * @param cb function to call with the backend's otp_device information
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_OtpDeviceGetHandle *
+TALER_MERCHANT_otp_device_get (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *otp_device_id,
+  TALER_MERCHANT_OtpDeviceGetCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel GET /otp-devices/$ID operation.
+ *
+ * @param tgh operation to cancel
+ */
+void
+TALER_MERCHANT_otp_device_get_cancel (
+  struct TALER_MERCHANT_OtpDeviceGetHandle *tgh);
+
+
+/**
+ * Handle for a POST /otp-devices operation.
+ */
+struct TALER_MERCHANT_OtpDevicesPostHandle;
+
+
+/**
+ * Function called with the result of the POST /otp-devices operation.
+ *
+ * @param cls closure
+ * @param hr HTTP response details
+ */
+typedef void
+(*TALER_MERCHANT_OtpDevicesPostCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_HttpResponse *hr);
+
+
+/**
+ * Make a POST /otp-devices request to add an OTP device
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param otp_id identifier to use for the OTP device
+ * @param otp_device_description description of the OTP device
+ * @param otp_key key of the OTP device
+ * @param otp_alg OTP algorithm used
+ * @param otp_ctr counter for counter-based OTP
+ * @param cb function to call with the backend's result
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_OtpDevicesPostHandle *
+TALER_MERCHANT_otp_devices_post (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *otp_id,
+  const char *otp_device_description,
+  const char *otp_key,
+  enum TALER_MerchantConfirmationAlgorithm otp_alg,
+  uint64_t otp_ctr,
+  TALER_MERCHANT_OtpDevicesPostCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel POST /otp-devices operation.
+ *
+ * @param[in] tph operation to cancel
+ */
+void
+TALER_MERCHANT_otp_devices_post_cancel (
+  struct TALER_MERCHANT_OtpDevicesPostHandle *tph);
+
+
+/**
+ * Handle for a PATCH /otp-device operation.
+ */
+struct TALER_MERCHANT_OtpDevicePatchHandle;
+
+
+/**
+ * Function called with the result of the PATCH /otp-device operation.
+ *
+ * @param cls closure
+ * @param hr HTTP response details
+ */
+typedef void
+(*TALER_MERCHANT_OtpDevicePatchCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_HttpResponse *hr);
+
+
+/**
+ * Make a PATCH /otp-device request to update OTP device details
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param otp_id identifier to use for the OTP device; the OTP device must 
exist,
+ *                    or the transaction will fail with a #MHD_HTTP_NOT_FOUND
+ *                    HTTP status code
+ * @param otp_device_description description of the otp_device
+ * @param otp_key key of the OTP device
+ * @param otp_alg OTP algorithm used
+ * @param otp_ctr counter for counter-based OTP
+ * @param cb function to call with the backend's result
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_OtpDevicePatchHandle *
+TALER_MERCHANT_otp_device_patch (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *otp_id,
+  const char *otp_device_description,
+  const char *otp_key,
+  enum TALER_MerchantConfirmationAlgorithm otp_alg,
+  uint64_t otp_ctr,
+  TALER_MERCHANT_OtpDevicePatchCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel PATCH /otp-device operation.
+ *
+ * @param[in] tph operation to cancel
+ */
+void
+TALER_MERCHANT_otp_device_patch_cancel (
+  struct TALER_MERCHANT_OtpDevicePatchHandle *tph);
+
+
+/**
+ * Handle for a DELETE /otp-device/$ID operation.
+ */
+struct TALER_MERCHANT_OtpDeviceDeleteHandle;
+
+
+/**
+ * Function called with the result of the DELETE /otp-device/$ID operation.
+ *
+ * @param cls closure
+ * @param hr HTTP response details
+ */
+typedef void
+(*TALER_MERCHANT_OtpDeviceDeleteCallback)(
+  void *cls,
+  const struct TALER_MERCHANT_HttpResponse *hr);
+
+
+/**
+ * Make a DELETE /otp-device/$ID request to delete an OTP device.
+ *
+ * @param ctx the context
+ * @param backend_url HTTP base URL for the backend
+ * @param otp_device_id identifier of the OTP device
+ * @param cb function to call with the backend's deletion status
+ * @param cb_cls closure for @a cb
+ * @return the request handle; NULL upon error
+ */
+struct TALER_MERCHANT_OtpDeviceDeleteHandle *
+TALER_MERCHANT_otp_device_delete (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *otp_device_id,
+  TALER_MERCHANT_OtpDeviceDeleteCallback cb,
+  void *cb_cls);
+
+
+/**
+ * Cancel DELETE /otp-device/$ID operation.
+ *
+ * @param[in] tdh operation to cancel
+ */
+void
+TALER_MERCHANT_otp_device_delete_cancel (
+  struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh);
+
+
 /* ********************* /templates *********************** */
 
 
@@ -5053,25 +5630,15 @@ struct TALER_MERCHANT_TemplateGetResponse
       const char *template_description;
 
       /**
-       * Shared key with the POS
-       */
-      const char *pos_key;
-
-      /**
-       * Option that add amount of the order
+       * OTP device ID used by the POS, NULL if none.
        */
-      const enum TALER_MerchantConfirmationAlgorithm pos_alg;
+      const char *otp_id;
 
       /**
        * Template for the contract.
        */
       const json_t *template_contract;
 
-      /**
-       * Algorithm used to generate confirmations for the merchant.
-       */
-      enum TALER_MerchantConfirmationAlgorithm mca;
-
     } ok;
 
   } details;
@@ -5146,8 +5713,7 @@ typedef void
  * @param backend_url HTTP base URL for the backend
  * @param template_id identifier to use for the template
  * @param template_description description of the template
- * @param pos_key shared key with the POS
- * @param mca algorithm used to generate confirmations
+ * @param otp_id ID of the OTP device, or NULL if OTP is not used
  * @param template_contract is the contract of the company
  * @param cb function to call with the backend's result
  * @param cb_cls closure for @a cb
@@ -5159,8 +5725,7 @@ TALER_MERCHANT_templates_post (
   const char *backend_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm mca,
+  const char *otp_id,
   const json_t *template_contract,
   TALER_MERCHANT_TemplatesPostCallback cb,
   void *cb_cls);
@@ -5203,8 +5768,7 @@ typedef void
  *                    or the transaction will fail with a #MHD_HTTP_NOT_FOUND
  *                    HTTP status code
  * @param template_description description of the template
- * @param pos_key shared key with the POS
- * @param mca algorithm used to generate confirmations
+ * @param otp_id device ID of the OTP device, or NULL if OTP is not used
  * @param template_contract is the contract of the company
  * @param cb function to call with the backend's result
  * @param cb_cls closure for @a cb
@@ -5216,8 +5780,7 @@ TALER_MERCHANT_template_patch (
   const char *backend_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm mca,
+  const char *otp_id,
   json_t *template_contract,
   TALER_MERCHANT_TemplatePatchCallback cb,
   void *cb_cls);
diff --git a/src/include/taler_merchant_testing_lib.h 
b/src/include/taler_merchant_testing_lib.h
index bc888fa7..ded1ac5a 100644
--- a/src/include/taler_merchant_testing_lib.h
+++ b/src/include/taler_merchant_testing_lib.h
@@ -91,7 +91,6 @@ TALER_TESTING_cmd_merchant_get_instances (const char *label,
  * @param merchant_url base URL of the merchant serving the
  *        POST /instances request.
  * @param instance_id the ID of the instance to create
- * @param payto_uri payment URI to use
  * @param http_status expected HTTP response code.
  * @return the command.
  */
@@ -99,7 +98,6 @@ struct TALER_TESTING_Command
 TALER_TESTING_cmd_merchant_post_instances (const char *label,
                                            const char *merchant_url,
                                            const char *instance_id,
-                                           const char *payto_uri,
                                            unsigned int http_status);
 
 
@@ -129,8 +127,6 @@ TALER_TESTING_cmd_merchant_post_instance_auth (const char 
*label,
  * @param merchant_url base URL of the merchant serving the
  *        POST /instances request.
  * @param instance_id the ID of the instance to query
- * @param accounts_length length of the @a payto_uris array
- * @param payto_uris URIs of the bank accounts of the merchant instance
  * @param name name of the merchant instance
  * @param address physical address of the merchant instance
  * @param jurisdiction jurisdiction of the merchant instance
@@ -146,8 +142,6 @@ TALER_TESTING_cmd_merchant_post_instances2 (
   const char *label,
   const char *merchant_url,
   const char *instance_id,
-  unsigned int accounts_length,
-  const char *payto_uris[],
   const char *name,
   json_t *address,
   json_t *jurisdiction,
@@ -180,20 +174,40 @@ TALER_TESTING_cmd_merchant_post_account (
   unsigned int http_status);
 
 
+/**
+ * Define a "PATCH /account" CMD.
+ *
+ * @param label command label.
+ * @param merchant_url base URL of the merchant serving the
+ *        POST /instances request.
+ * @param create_account_ref reference to account setup command
+ * @param credit_facade_url credit facade URL to configure, can be NULL
+ * @param credit_facade_credentials credit facade credentials to use, can be 
NULL
+ * @param http_status expected HTTP response code.
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_patch_account (
+  const char *label,
+  const char *merchant_url,
+  const char *create_account_ref,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials,
+  unsigned int http_status);
+
+
 /**
  * Define a "DELETE /account" CMD.
  *
  * @param label command label.
- * @param get_instance_ref reference to a GET instance command
- * @param payto_uri payto URI of the account to delete, must be in the 
response of the GET instance command
+ * @param create_account_ref reference to account setup command
  * @param http_status expected HTTP response code.
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_merchant_delete_account (
   const char *label,
-  const char *get_instance_ref,
-  const char *payto_uri,
+  const char *create_account_ref,
   unsigned int http_status);
 
 
@@ -220,8 +234,6 @@ TALER_TESTING_cmd_merchant_patch_instance (
   const char *label,
   const char *merchant_url,
   const char *instance_id,
-  unsigned int payto_uris_length,
-  const char *payto_uris[],
   const char *name,
   json_t *address,
   json_t *jurisdiction,
@@ -251,35 +263,6 @@ TALER_TESTING_cmd_merchant_get_instance (const char *label,
                                          const char *instance_reference);
 
 
-/**
- * Define a "GET instance" CMD that compares accounts returned.
- *
- * @param label command label.
- * @param merchant_url base URL of the merchant serving the
- *        GET /instances/$ID request.
- * @param instance_id the ID of the instance to query
- * @param http_status expected HTTP response code.
- * @param instance_reference reference to a "POST /instances" or "PATCH 
/instances/$ID" CMD
- *        that will provide what we expect the backend to return to us
- * @param active_accounts the accounts the merchant is actively using.
- * @param active_accounts_length length of @e active_accounts.
- * @param inactive_accounts the accounts the merchant is no longer using.
- * @param inactive_accounts_length length of @e inactive_accounts.
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_get_instance2 (const char *label,
-                                          const char *merchant_url,
-                                          const char *instance_id,
-                                          unsigned int http_status,
-                                          const char *instance_reference,
-                                          const char *active_accounts[],
-                                          unsigned int active_accounts_length,
-                                          const char *inactive_accounts[],
-                                          unsigned int
-                                          inactive_accounts_length);
-
-
 /**
  * Define a "PURGE instance" CMD.
  *
@@ -1511,6 +1494,117 @@ TALER_TESTING_cmd_merchant_kyc_get (
   enum TALER_AmlDecisionState expected_aml_state);
 
 
+/* ****** OTP devices ******* */
+
+
+/**
+ * Define a "POST /otp-devices" CMD.
+ *
+ * @param label command label.
+ * @param merchant_url base URL of the merchant serving the
+ *        POST /otps request.
+ * @param otp_id the ID of the otp device to modify
+ * @param otp_description description of the otp device
+ * @param otp_key base32-encoded key to verify the payment
+ * @param otp_alg is an option that show the amount of the order. it is linked 
with the @a otp_key
+ * @param otp_ctr counter to use (if in counter mode)
+ * @param http_status expected HTTP response code.
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_post_otp_devices (
+  const char *label,
+  const char *merchant_url,
+  const char *otp_id,
+  const char *otp_description,
+  const char *otp_key,
+  enum TALER_MerchantConfirmationAlgorithm otp_alg,
+  uint64_t otp_ctr,
+  unsigned int http_status);
+
+
+/**
+ * Define a "PATCH /otp-devices/$ID" CMD.
+ *
+ * @param label command label.
+ * @param merchant_url base URL of the merchant serving the
+ *        PATCH /otp-devices request.
+ * @param otp_id the ID of the otp device to modify
+ * @param otp_description description of the otp device
+ * @param otp_key base32-encoded key to verify the payment
+ * @param otp_alg is an option that show the amount of the order. it is linked 
with the @a otp_key
+ * @param otp_ctr counter to use (if in counter mode)
+ * @param http_status expected HTTP response code.
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_patch_otp_device (
+  const char *label,
+  const char *merchant_url,
+  const char *otp_id,
+  const char *otp_description,
+  const char *otp_key,
+  enum TALER_MerchantConfirmationAlgorithm otp_alg,
+  uint64_t otp_ctr,
+  unsigned int http_status);
+
+
+/**
+ * Define a "GET /otp-devices" CMD.
+ *
+ * @param label command label.
+ * @param merchant_url base URL of the merchant serving the
+ *        GET /otp-devices request.
+ * @param http_status expected HTTP response code.
+ * @param ... NULL-terminated list of labels (const char *) of
+ *        otp (commands) we expect to be returned in the list
+ *        (assuming @a http_code is #MHD_HTTP_OK)
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_get_otp_devices (const char *label,
+                                            const char *merchant_url,
+                                            unsigned int http_status,
+                                            ...);
+
+
+/**
+ * Define a "GET otp device" CMD.
+ *
+ * @param label command label.
+ * @param merchant_url base URL of the merchant serving the
+ *        GET /otp-devices/$ID request.
+ * @param otp_id the ID of the otp to query
+ * @param http_status expected HTTP response code.
+ * @param otp_reference reference to a "POST /otp-devices" or "PATCH 
/otp-devices/$ID" CMD
+ *        that will provide what we expect the backend to return to us
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_get_otp_device (const char *label,
+                                           const char *merchant_url,
+                                           const char *otp_id,
+                                           unsigned int http_status,
+                                           const char *otp_reference);
+
+
+/**
+ * Define a "DELETE otp device" CMD.
+ *
+ * @param label command label.
+ * @param merchant_url base URL of the merchant serving the
+ *        DELETE /otp-devices/$ID request.
+ * @param otp_id the ID of the otp to query
+ * @param http_status expected HTTP response code.
+ * @return the command.
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_delete_otp_device (const char *label,
+                                              const char *merchant_url,
+                                              const char *otp_id,
+                                              unsigned int http_status);
+
+
 /* ****** Templates ******* */
 
 /**
@@ -1521,8 +1615,7 @@ TALER_TESTING_cmd_merchant_kyc_get (
  *        POST /templates request.
  * @param template_id the ID of the template to query
  * @param template_description description of the template
- * @param pos_key base32-encoded key to verify the payment
- * @param pos_alg is an option that show the amount of the order. it is linked 
with the pos_key
+ * @param otp_id OTP device ID, NULL for none
  * @param template_contract where the contract of the company is
  * @param http_status expected HTTP response code.
  * @return the command.
@@ -1533,8 +1626,7 @@ TALER_TESTING_cmd_merchant_post_templates2 (
   const char *merchant_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  const enum TALER_MerchantConfirmationAlgorithm pos_alg,
+  const char *otp_id,
   json_t *template_contract,
   unsigned int http_status);
 
@@ -1566,8 +1658,7 @@ TALER_TESTING_cmd_merchant_post_templates (const char 
*label,
  *        PATCH /template request.
  * @param template_id the ID of the template to query
  * @param template_description description of the template
- * @param pos_key base32-encoded key to verify the payment
- * @param pos_alg is an option that show the amount of the order. it is linked 
with the pos_key
+ * @param otp_id OTP device to use
  * @param template_contract contract of the company
  * @param http_status expected HTTP response code.
  * @return the command.
@@ -1578,8 +1669,7 @@ TALER_TESTING_cmd_merchant_patch_template (
   const char *merchant_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  const enum TALER_MerchantConfirmationAlgorithm pos_alg,
+  const char *otp_id,
   json_t *template_contract,
   unsigned int http_status);
 
@@ -1645,6 +1735,7 @@ TALER_TESTING_cmd_merchant_delete_template (const char 
*label,
  *
  * @param label command label.
  * @param template_ref label of command that created the template to use
+ * @param otp_ref label of command that created OTP device we use (or NULL for 
no OTP)
  * @param merchant_url base URL of the merchant serving the
  *        POST /using-templates request.
  * @param using_template_id template ID to use
@@ -1659,6 +1750,7 @@ struct TALER_TESTING_Command
 TALER_TESTING_cmd_merchant_post_using_templates (
   const char *label,
   const char *template_ref,
+  const char *otp_ref,
   const char *merchant_url,
   const char *using_template_id,
   const char *summary,
@@ -1909,8 +2001,10 @@ TALER_TESTING_cmd_checkserver2 (const char *label,
   op (h_wire, const struct TALER_MerchantWireHashP) \
   op (proposal_reference, const char) \
   op (template_description, const char) \
-  op (template_pos_key, const char) \
-  op (template_pos_alg, const enum TALER_MerchantConfirmationAlgorithm) \
+  op (otp_device_description, const char) \
+  op (otp_id, const char) \
+  op (otp_key, const char) \
+  op (otp_alg, const enum TALER_MerchantConfirmationAlgorithm) \
   op (template_id, const char) \
   op (template_contract, const json_t) \
   op (event_type, const char)   \
diff --git a/src/include/taler_merchantdb_plugin.h 
b/src/include/taler_merchantdb_plugin.h
index 1c0cd37c..14dedce5 100644
--- a/src/include/taler_merchantdb_plugin.h
+++ b/src/include/taler_merchantdb_plugin.h
@@ -96,20 +96,20 @@ struct TALER_MERCHANTDB_AccountDetails
   /**
    * Actual account address as a payto://-URI.
    */
-  const char *payto_uri;
+  char *payto_uri;
 
   /**
    * Where can the taler-merchant-wirewatch helper
    * download information about incoming transfers?
    * NULL if not available.
    */
-  const char *credit_facade_url;
+  char *credit_facade_url;
 
   /**
    * JSON with credentials to use to access the
    * @e credit_facade_url.
    */
-  const json_t *credit_facade_credentials;
+  json_t *credit_facade_credentials;
 
   /**
    * Is the account set for active use in new contracts?
@@ -219,9 +219,20 @@ typedef void
   const struct TALER_MerchantPublicKeyP *merchant_pub,
   const struct TALER_MerchantPrivateKeyP *merchant_priv,
   const struct TALER_MERCHANTDB_InstanceSettings *is,
-  const struct TALER_MERCHANTDB_InstanceAuthSettings *ias,
-  unsigned int accounts_length,
-  const struct TALER_MERCHANTDB_AccountDetails accounts[]);
+  const struct TALER_MERCHANTDB_InstanceAuthSettings *ias);
+
+
+/**
+ * Callback invoked with information about a bank account.
+ *
+ * @param cls closure
+ * @param ad details about the account
+ */
+typedef void
+(*TALER_MERCHANTDB_AccountCallback)(
+  void *cls,
+  const struct TALER_MERCHANTDB_AccountDetails *ad);
+
 
 /**
  * Typically called by `lookup_products`.
@@ -311,7 +322,7 @@ struct TALER_MERCHANTDB_ProductDetails
 /**
  * Typically called by `lookup_templates`.
  *
- * @param cls a `json_t *` JSON array to build
+ * @param cls closure
  * @param template_id ID of the template
  * @param template_description description of the template
  */
@@ -321,6 +332,19 @@ typedef void
                                       const char *template_description);
 
 
+/**
+ * Typically called by `lookup_otp_devices`.
+ *
+ * @param cls closure
+ * @param otp_id ID of the OTP device
+ * @param otp_description description of the OTP device
+ */
+typedef void
+(*TALER_MERCHANTDB_OtpDeviceCallback)(void *cls,
+                                      const char *otp_id,
+                                      const char *otp_description);
+
+
 /**
  * Details about a template.
  */
@@ -332,19 +356,42 @@ struct TALER_MERCHANTDB_TemplateDetails
   char *template_description;
 
   /**
-   * Base64-encoded key, or NULL.
+   * In this template contract, we can have additional information.
+   */
+  json_t *template_contract;
+  
+  /**
+   * ID of the OTP device linked to the template, or NULL.
    */
-  char *pos_key;
+  char *otp_id;
+};
+
+
+/**
+ * Details about an OTP device.
+ */
+struct TALER_MERCHANTDB_OtpDeviceDetails
+{
 
   /**
-   * In this template contract, we can have additional information.
+   * Description of the device.
    */
-  json_t *template_contract;
+  char *otp_description;
+
+  /**
+   * Current usage counter value.
+   */
+  uint64_t otp_ctr;
+
+  /**
+   * Base64-encoded key.
+   */
+  char *otp_key;
 
   /**
    * Algorithm used to compute purchase confirmations.
    */
-  enum TALER_MerchantConfirmationAlgorithm pos_algorithm;
+  enum TALER_MerchantConfirmationAlgorithm otp_algorithm;
 };
 
 
@@ -1223,14 +1270,52 @@ struct TALER_MERCHANTDB_Plugin
    *
    * @param cls closure
    * @param id identifier of the instance
-   * @param account_details details about the account to update
+   * @param h_wire which account to update
+   * @param credit_facade_url new facade URL, can be NULL
+   * @param credit_facade_credentials new credentials, can be NULL
    * @return database result code
    */
   enum GNUNET_DB_QueryStatus
   (*update_account)(
     void *cls,
     const char *id,
-    const struct TALER_MERCHANTDB_AccountDetails *account_details);
+    const struct TALER_MerchantWireHashP *h_wire,
+    const char *credit_facade_url,
+    const json_t *credit_facade_credentials);
+  
+
+  /**
+   * Obtain information about an instance's accounts.
+   *
+   * @param cls closure
+   * @param id identifier of the instance
+   * @param cb function to call on each account
+   * @param cb_cls closure for @a cb
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_accounts)(
+    void *cls,
+    const char *id,
+    TALER_MERCHANTDB_AccountCallback cb,
+    void *cb_cls);
+
+
+  /**
+   * Obtain detailed information about an instance's account.
+   *
+   * @param cls closure
+   * @param id identifier of the instance
+   * @param h_wire wire hash of the account
+   * @param[out] ad account details returned
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_account)(
+    void *cls,
+    const char *id,
+    const struct TALER_MerchantWireHashP *h_wire,
+    struct TALER_MERCHANTDB_AccountDetails *ad);
 
 
   /**
@@ -2867,6 +2952,7 @@ struct TALER_MERCHANTDB_Plugin
    * @param cls closure
    * @param instance_id instance to insert template for
    * @param template_id template identifier of template to insert
+   * @param otp_serial_id 0 if no OTP device is associated
    * @param td the template details to insert
    * @return database result code
    */
@@ -2874,9 +2960,105 @@ struct TALER_MERCHANTDB_Plugin
   (*insert_template)(void *cls,
                      const char *instance_id,
                      const char *template_id,
+                     uint64_t otp_serial_id,
                      const struct TALER_MERCHANTDB_TemplateDetails *td);
 
 
+  /**
+   * Delete information about an OTP device.
+   *
+   * @param cls closure
+   * @param instance_id instance to delete OTP device of
+   * @param otp_id otp device to delete
+   * @return DB status code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS
+   *           if template unknown.
+   */
+  enum GNUNET_DB_QueryStatus
+  (*delete_otp) (void *cls,
+                 const char *instance_id,
+                 const char *otp_id);
+
+  /**
+   * Insert details about a particular OTP device.
+   *
+   * @param cls closure
+   * @param instance_id instance to insert OTP device for
+   * @param otp_id otp identifier of OTP device to insert
+   * @param td the OTP device details to insert
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*insert_otp) (void *cls,
+                 const char *instance_id,
+                 const char *otp_id,
+                 const struct TALER_MERCHANTDB_OtpDeviceDetails *td);
+
+
+  /**
+   * Update details about a particular OTP device.
+   *
+   * @param cls closure
+   * @param instance_id instance to update OTP device for
+   * @param otp_id OTP device to update
+   * @param td update to the OTP device details on success, can be NULL
+   *             (in that case we only want to check if the template exists)
+   * @return database result code, #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the 
template
+   *         does not yet exist.
+   */
+  enum GNUNET_DB_QueryStatus
+  (*update_otp) (void *cls,
+                 const char *instance_id,
+                 const char *otp_id,
+                 const struct TALER_MERCHANTDB_OtpDeviceDetails *td);
+
+  /**
+   * Lookup all of the OTP devices the given instance has configured.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup OTP devices for
+   * @param cb function to call on all OTP devices found
+   * @param cb_cls closure for @a cb
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*lookup_otp_devices) (void *cls,
+                         const char *instance_id,
+                         TALER_MERCHANTDB_OtpDeviceCallback cb,
+                         void *cb_cls);
+
+
+  /**
+   * Lookup details about an OTP device.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup template for
+   * @param otp_id OTP device to lookup
+   * @param[out] td set to the OTP device details on success, can be NULL
+   *             (in that case we only want to check if the template exists)
+   * @return database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_otp) (void *cls,
+                 const char *instance_id,
+                 const char *otp_id,
+                 struct TALER_MERCHANTDB_OtpDeviceDetails *td);
+
+
+  /**
+   * Lookup serial number of an OTP device.
+   *
+   * @param cls closure
+   * @param instance_id instance to lookup template for
+   * @param otp_id OTP device to lookup
+   * @param[out] serial set to the OTP device serial number   * @return 
database result code
+   */
+  enum GNUNET_DB_QueryStatus
+  (*select_otp_serial) (void *cls,
+                        const char *instance_id,
+                        const char *otp_id,
+                        uint64_t *serial);
+
+  
   /**
    * Update details about a particular template.
    *
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index a696f3ea..41cd6674 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -19,16 +19,21 @@ libtalermerchant_la_SOURCES = \
   merchant_api_delete_account.c \
   merchant_api_delete_instance.c \
   merchant_api_delete_order.c \
+  merchant_api_delete_otp_device.c \
   merchant_api_delete_product.c \
   merchant_api_delete_reserve.c \
   merchant_api_delete_template.c \
   merchant_api_delete_transfer.c \
   merchant_api_delete_webhook.c \
+  merchant_api_get_account.c \
+  merchant_api_get_accounts.c \
   merchant_api_get_config.c \
   merchant_api_get_instance.c \
   merchant_api_get_instances.c \
   merchant_api_get_kyc.c \
   merchant_api_get_orders.c \
+  merchant_api_get_otp_device.c \
+  merchant_api_get_otp_devices.c \
   merchant_api_get_product.c \
   merchant_api_get_products.c \
   merchant_api_get_reserve.c \
@@ -42,8 +47,10 @@ libtalermerchant_la_SOURCES = \
   merchant_api_lock_product.c \
   merchant_api_merchant_get_order.c \
   merchant_api_merchant_get_reward.c \
+  merchant_api_patch_account.c \
   merchant_api_patch_instance.c \
   merchant_api_patch_order_forget.c \
+  merchant_api_patch_otp_device.c \
   merchant_api_patch_product.c \
   merchant_api_patch_template.c \
   merchant_api_patch_webhook.c \
@@ -56,6 +63,7 @@ libtalermerchant_la_SOURCES = \
   merchant_api_post_order_paid.c \
   merchant_api_post_order_pay.c \
   merchant_api_post_order_refund.c \
+  merchant_api_post_otp_devices.c \
   merchant_api_post_products.c \
   merchant_api_post_reserves.c \
   merchant_api_post_transfers.c \
diff --git a/src/lib/merchant_api_delete_otp_device.c 
b/src/lib/merchant_api_delete_otp_device.c
new file mode 100644
index 00000000..5397606c
--- /dev/null
+++ b/src/lib/merchant_api_delete_otp_device.c
@@ -0,0 +1,184 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Lesser General Public License as published by the Free 
Software
+  Foundation; either version 2.1, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
+
+  You should have received a copy of the GNU Lesser General Public License 
along with
+  TALER; see the file COPYING.LGPL.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file merchant_api_delete_otp_device.c
+ * @brief Implementation of the DELETE /otp-devices/$ID request of the 
merchant's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_merchant_service.h"
+#include "merchant_api_curl_defaults.h"
+#include <taler/taler_json_lib.h>
+#include <taler/taler_signatures.h>
+
+
+/**
+ * Handle for a DELETE /otp-devices/$ID operation.
+ */
+struct TALER_MERCHANT_OtpDeviceDeleteHandle
+{
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_MERCHANT_OtpDeviceDeleteCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Reference to the execution context.
+   */
+  struct GNUNET_CURL_Context *ctx;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP GET /otp-devices/$ID request.
+ *
+ * @param cls the `struct TALER_MERCHANT_OtpDeviceDeleteHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response response body, NULL if not in JSON
+ */
+static void
+handle_delete_otp_device_finished (void *cls,
+                                   long response_code,
+                                   const void *response)
+{
+  struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh = cls;
+  const json_t *json = response;
+  struct TALER_MERCHANT_HttpResponse hr = {
+    .http_status = (unsigned int) response_code,
+    .reply = json
+  };
+
+  tdh->job = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Got DELETE /otp-devices/$ID response with status code %u\n",
+              (unsigned int) response_code);
+  switch (response_code)
+  {
+  case MHD_HTTP_NO_CONTENT:
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    hr.ec = TALER_JSON_get_error_code (json);
+    hr.hint = TALER_JSON_get_error_hint (json);
+    /* Nothing really to verify, merchant says we need to authenticate. */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    hr.ec = TALER_JSON_get_error_code (json);
+    hr.hint = TALER_JSON_get_error_hint (json);
+    break;
+  case MHD_HTTP_CONFLICT:
+    hr.ec = TALER_JSON_get_error_code (json);
+    hr.hint = TALER_JSON_get_error_hint (json);
+    break;
+  default:
+    /* unexpected response code */
+    hr.ec = TALER_JSON_get_error_code (json);
+    hr.hint = TALER_JSON_get_error_hint (json);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u/%d\n",
+                (unsigned int) response_code,
+                (int) hr.ec);
+    break;
+  }
+  tdh->cb (tdh->cb_cls,
+           &hr);
+  TALER_MERCHANT_otp_device_delete_cancel (tdh);
+}
+
+
+struct TALER_MERCHANT_OtpDeviceDeleteHandle *
+TALER_MERCHANT_otp_device_delete (
+  struct GNUNET_CURL_Context *ctx,
+  const char *backend_url,
+  const char *otp_device_id,
+  TALER_MERCHANT_OtpDeviceDeleteCallback cb,
+  void *cb_cls)
+{
+  struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh;
+
+  tdh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceDeleteHandle);
+  tdh->ctx = ctx;
+  tdh->cb = cb;
+  tdh->cb_cls = cb_cls;
+  {
+    char *path;
+
+    GNUNET_asprintf (&path,
+                     "private/otp-devices/%s",
+                     otp_device_id);
+    tdh->url = TALER_url_join (backend_url,
+                               path,
+                               NULL);
+    GNUNET_free (path);
+  }
+  if (NULL == tdh->url)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not construct request URL.\n");
+    GNUNET_free (tdh);
+    return NULL;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Requesting URL '%s'\n",
+              tdh->url);
+  {
+    CURL *eh;
+
+    eh = TALER_MERCHANT_curl_easy_get_ (tdh->url);
+    GNUNET_assert (CURLE_OK ==
+                   curl_easy_setopt (eh,
+                                     CURLOPT_CUSTOMREQUEST,
+                                     MHD_HTTP_METHOD_DELETE));
+    tdh->job = GNUNET_CURL_job_add (ctx,
+                                    eh,
+                                    &handle_delete_otp_device_finished,
+                                    tdh);
+  }
+  return tdh;
+}
+
+
+void
+TALER_MERCHANT_otp_device_delete_cancel (
+  struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh)
+{
+  if (NULL != tdh->job)
+    GNUNET_CURL_job_cancel (tdh->job);
+  GNUNET_free (tdh->url);
+  GNUNET_free (tdh);
+}
diff --git a/src/lib/merchant_api_get_template.c 
b/src/lib/merchant_api_get_account.c
similarity index 67%
copy from src/lib/merchant_api_get_template.c
copy to src/lib/merchant_api_get_account.c
index 3e4a23a8..e9a13b33 100644
--- a/src/lib/merchant_api_get_template.c
+++ b/src/lib/merchant_api_get_account.c
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_get_template.c
- * @brief Implementation of the GET /templates/$ID request of the merchant's 
HTTP API
+ * @file merchant_api_get_account.c
+ * @brief Implementation of the GET /accounts/$ID request of the merchant's 
HTTP API
  * @author Priscilla HUANG
  */
 #include "platform.h"
@@ -32,9 +32,9 @@
 
 
 /**
- * Handle for a GET /templates/$ID operation.
+ * Handle for a GET /accounts/$ID operation.
  */
-struct TALER_MERCHANT_TemplateGetHandle
+struct TALER_MERCHANT_AccountGetHandle
 {
   /**
    * The url for this request.
@@ -49,7 +49,7 @@ struct TALER_MERCHANT_TemplateGetHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplateGetCallback cb;
+  TALER_MERCHANT_AccountGetCallback cb;
 
   /**
    * Closure for @a cb.
@@ -66,45 +66,45 @@ struct TALER_MERCHANT_TemplateGetHandle
 
 /**
  * Function called when we're done processing the
- * HTTP GET /templates/$ID request.
+ * HTTP GET /accounts/$ID request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplateGetHandle`
+ * @param cls the `struct TALER_MERCHANT_AccountGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_get_template_finished (void *cls,
+handle_get_account_finished (void *cls,
                               long response_code,
                               const void *response)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh = cls;
+  struct TALER_MERCHANT_AccountGetHandle *tgh = cls;
   const json_t *json = response;
-  struct TALER_MERCHANT_TemplateGetResponse tgr = {
+  struct TALER_MERCHANT_AccountGetResponse tgr = {
     .hr.http_status = (unsigned int) response_code,
     .hr.reply = json
   };
 
   tgh->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got /templates/$ID response with status code %u\n",
+              "Got /accounts/$ID response with status code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
   case MHD_HTTP_OK:
     {
-      uint32_t alg32;
-      const json_t *contract;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_string ("template_description",
-                                 &tgr.details.ok.template_description),
-        GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                                 &alg32),
+        GNUNET_JSON_spec_fixed_auto ("salt",
+                                     &tgr.details.ok.ad.salt),
         GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("pos_key",
-                                   &tgr.details.ok.pos_key),
+          GNUNET_JSON_spec_string ("credit_facade_url",
+                                   &tgr.details.ok.ad.credit_facade_url),
           NULL),
-        GNUNET_JSON_spec_object_const ("template_contract",
-                                       &contract),
+        GNUNET_JSON_spec_string ("payto_uri",
+                                 &tgr.details.ok.ad.payto_uri),
+        GNUNET_JSON_spec_fixed_auto ("h_wire",
+                                     &tgr.details.ok.ad.h_wire),
+        GNUNET_JSON_spec_bool ("active",
+                               &tgr.details.ok.ad.active),
         GNUNET_JSON_spec_end ()
       };
 
@@ -113,12 +113,9 @@ handle_get_template_finished (void *cls,
                              spec,
                              NULL, NULL))
       {
-        tgr.details.ok.mca =
-          (enum TALER_MerchantConfirmationAlgorithm) alg32;
-        tgr.details.ok.template_contract = contract;
         tgh->cb (tgh->cb_cls,
                  &tgr);
-        TALER_MERCHANT_template_get_cancel (tgh);
+        TALER_MERCHANT_account_get_cancel (tgh);
         return;
       }
       tgr.hr.http_status = 0;
@@ -146,31 +143,39 @@ handle_get_template_finished (void *cls,
   }
   tgh->cb (tgh->cb_cls,
            &tgr);
-  TALER_MERCHANT_template_get_cancel (tgh);
+  TALER_MERCHANT_account_get_cancel (tgh);
 }
 
 
-struct TALER_MERCHANT_TemplateGetHandle *
-TALER_MERCHANT_template_get (
+struct TALER_MERCHANT_AccountGetHandle *
+TALER_MERCHANT_account_get (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  TALER_MERCHANT_TemplateGetCallback cb,
+  const char *instance_id,
+  const struct TALER_MerchantWireHashP *h_wire,
+  TALER_MERCHANT_AccountGetCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh;
+  struct TALER_MERCHANT_AccountGetHandle *tgh;
   CURL *eh;
 
-  tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle);
+  tgh = GNUNET_new (struct TALER_MERCHANT_AccountGetHandle);
   tgh->ctx = ctx;
   tgh->cb = cb;
   tgh->cb_cls = cb_cls;
   {
+    char w_str[sizeof (*h_wire) * 2];
     char *path;
+    char *end;
 
+    end = GNUNET_STRINGS_data_to_string (h_wire,
+                                         sizeof (*h_wire),
+                                         w_str,
+                                         sizeof (w_str));
+    *end = '\0';
     GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
+                     "private/accounts/%s",
+                     w_str);
     tgh->url = TALER_url_join (backend_url,
                                path,
                                NULL);
@@ -189,15 +194,15 @@ TALER_MERCHANT_template_get (
   eh = TALER_MERCHANT_curl_easy_get_ (tgh->url);
   tgh->job = GNUNET_CURL_job_add (ctx,
                                   eh,
-                                  &handle_get_template_finished,
+                                  &handle_get_account_finished,
                                   tgh);
   return tgh;
 }
 
 
 void
-TALER_MERCHANT_template_get_cancel (
-  struct TALER_MERCHANT_TemplateGetHandle *tgh)
+TALER_MERCHANT_account_get_cancel (
+  struct TALER_MERCHANT_AccountGetHandle *tgh)
 {
   if (NULL != tgh->job)
     GNUNET_CURL_job_cancel (tgh->job);
diff --git a/src/lib/merchant_api_get_template.c 
b/src/lib/merchant_api_get_accounts.c
similarity index 54%
copy from src/lib/merchant_api_get_template.c
copy to src/lib/merchant_api_get_accounts.c
index 3e4a23a8..9d09463b 100644
--- a/src/lib/merchant_api_get_template.c
+++ b/src/lib/merchant_api_get_accounts.c
@@ -15,9 +15,9 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_get_template.c
- * @brief Implementation of the GET /templates/$ID request of the merchant's 
HTTP API
- * @author Priscilla HUANG
+ * @file merchant_api_get_accounts.c
+ * @brief Implementation of the GET /accounts request of the merchant's HTTP 
API
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <curl/curl.h>
@@ -32,9 +32,9 @@
 
 
 /**
- * Handle for a GET /templates/$ID operation.
+ * Handle for a GET /accounts operation.
  */
-struct TALER_MERCHANT_TemplateGetHandle
+struct TALER_MERCHANT_AccountsGetHandle
 {
   /**
    * The url for this request.
@@ -49,7 +49,7 @@ struct TALER_MERCHANT_TemplateGetHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplateGetCallback cb;
+  TALER_MERCHANT_AccountsGetCallback cb;
 
   /**
    * Closure for @a cb.
@@ -64,61 +64,102 @@ struct TALER_MERCHANT_TemplateGetHandle
 };
 
 
+/**
+ * Parse account information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with account data
+ * @param[in] tgr partially filled response
+ * @param tgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_accounts (const json_t *ia,
+                struct TALER_MERCHANT_AccountsGetResponse *tgr,
+                struct TALER_MERCHANT_AccountsGetHandle *tgh)
+{
+  unsigned int tmpl_len = json_array_size (ia);
+  struct TALER_MERCHANT_AccountEntry tmpl[GNUNET_NZL (tmpl_len)];
+  size_t index;
+  json_t *value;
+
+  json_array_foreach (ia, index, value) {
+    struct TALER_MERCHANT_AccountEntry *ie = &tmpl[index];
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_string ("payto_uri",
+                               &ie->payto_uri),
+      GNUNET_JSON_spec_fixed_auto ("h_wire",
+                                   &ie->h_wire),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (value,
+                           spec,
+                           NULL, NULL))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  }
+  tgr->details.ok.accounts_length = tmpl_len;
+  tgr->details.ok.accounts = tmpl;
+  tgh->cb (tgh->cb_cls,
+           tgr);
+  tgh->cb = NULL; /* just to be sure */
+  return GNUNET_OK;
+}
+
+
 /**
  * Function called when we're done processing the
- * HTTP GET /templates/$ID request.
+ * HTTP /accounts request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplateGetHandle`
+ * @param cls the `struct TALER_MERCHANT_AccountsGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_get_template_finished (void *cls,
-                              long response_code,
-                              const void *response)
+handle_get_accounts_finished (void *cls,
+                               long response_code,
+                               const void *response)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh = cls;
+  struct TALER_MERCHANT_AccountsGetHandle *tgh = cls;
   const json_t *json = response;
-  struct TALER_MERCHANT_TemplateGetResponse tgr = {
+  struct TALER_MERCHANT_AccountsGetResponse tgr = {
     .hr.http_status = (unsigned int) response_code,
     .hr.reply = json
   };
 
   tgh->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got /templates/$ID response with status code %u\n",
+              "Got /accounts response with status code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
   case MHD_HTTP_OK:
     {
-      uint32_t alg32;
-      const json_t *contract;
+      const json_t *accounts;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_string ("template_description",
-                                 &tgr.details.ok.template_description),
-        GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                                 &alg32),
-        GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("pos_key",
-                                   &tgr.details.ok.pos_key),
-          NULL),
-        GNUNET_JSON_spec_object_const ("template_contract",
-                                       &contract),
+        GNUNET_JSON_spec_array_const ("accounts",
+                                      &accounts),
         GNUNET_JSON_spec_end ()
       };
 
-      if (GNUNET_OK ==
+      if (GNUNET_OK !=
           GNUNET_JSON_parse (json,
                              spec,
                              NULL, NULL))
       {
-        tgr.details.ok.mca =
-          (enum TALER_MerchantConfirmationAlgorithm) alg32;
-        tgr.details.ok.template_contract = contract;
-        tgh->cb (tgh->cb_cls,
-                 &tgr);
-        TALER_MERCHANT_template_get_cancel (tgh);
+        tgr.hr.http_status = 0;
+        tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+        break;
+      }
+      if (GNUNET_OK ==
+          parse_accounts (accounts,
+                           &tgr,
+                           tgh))
+      {
+        TALER_MERCHANT_accounts_get_cancel (tgh);
         return;
       }
       tgr.hr.http_status = 0;
@@ -130,10 +171,6 @@ handle_get_template_finished (void *cls,
     tgr.hr.hint = TALER_JSON_get_error_hint (json);
     /* Nothing really to verify, merchant says we need to authenticate. */
     break;
-  case MHD_HTTP_NOT_FOUND:
-    tgr.hr.ec = TALER_JSON_get_error_code (json);
-    tgr.hr.hint = TALER_JSON_get_error_hint (json);
-    break;
   default:
     /* unexpected response code */
     tgr.hr.ec = TALER_JSON_get_error_code (json);
@@ -146,36 +183,27 @@ handle_get_template_finished (void *cls,
   }
   tgh->cb (tgh->cb_cls,
            &tgr);
-  TALER_MERCHANT_template_get_cancel (tgh);
+  TALER_MERCHANT_accounts_get_cancel (tgh);
 }
 
 
-struct TALER_MERCHANT_TemplateGetHandle *
-TALER_MERCHANT_template_get (
+struct TALER_MERCHANT_AccountsGetHandle *
+TALER_MERCHANT_accounts_get (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  TALER_MERCHANT_TemplateGetCallback cb,
+  TALER_MERCHANT_AccountsGetCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh;
+  struct TALER_MERCHANT_AccountsGetHandle *tgh;
   CURL *eh;
 
-  tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle);
+  tgh = GNUNET_new (struct TALER_MERCHANT_AccountsGetHandle);
   tgh->ctx = ctx;
   tgh->cb = cb;
   tgh->cb_cls = cb_cls;
-  {
-    char *path;
-
-    GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
-    tgh->url = TALER_url_join (backend_url,
-                               path,
-                               NULL);
-    GNUNET_free (path);
-  }
+  tgh->url = TALER_url_join (backend_url,
+                             "private/accounts",
+                             NULL);
   if (NULL == tgh->url)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -189,15 +217,15 @@ TALER_MERCHANT_template_get (
   eh = TALER_MERCHANT_curl_easy_get_ (tgh->url);
   tgh->job = GNUNET_CURL_job_add (ctx,
                                   eh,
-                                  &handle_get_template_finished,
+                                  &handle_get_accounts_finished,
                                   tgh);
   return tgh;
 }
 
 
 void
-TALER_MERCHANT_template_get_cancel (
-  struct TALER_MERCHANT_TemplateGetHandle *tgh)
+TALER_MERCHANT_accounts_get_cancel (
+  struct TALER_MERCHANT_AccountsGetHandle *tgh)
 {
   if (NULL != tgh->job)
     GNUNET_CURL_job_cancel (tgh->job);
diff --git a/src/lib/merchant_api_get_instance.c 
b/src/lib/merchant_api_get_instance.c
index 9cfbcf83..eef95b84 100644
--- a/src/lib/merchant_api_get_instance.c
+++ b/src/lib/merchant_api_get_instance.c
@@ -93,14 +93,10 @@ handle_get_instance_finished (void *cls,
   {
   case MHD_HTTP_OK:
     {
-      const json_t *accounts;
       const char *uts;
       const json_t *address;
       const json_t *jurisdiction;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_array_const (
-          "accounts",
-          &accounts),
         GNUNET_JSON_spec_string (
           "name",
           &igr.details.ok.details.name),
@@ -128,78 +124,32 @@ handle_get_instance_finished (void *cls,
         GNUNET_JSON_spec_end ()
       };
 
-      if (GNUNET_OK ==
+      if (GNUNET_OK !=
           GNUNET_JSON_parse (json,
                              spec,
                              NULL, NULL))
       {
-        unsigned int accounts_length = json_array_size (accounts);
-        struct TALER_MERCHANT_Account aa[GNUNET_NZL (accounts_length)];
-        size_t index;
-        json_t *value;
-        int ret = GNUNET_OK;
-
-        memset (aa,
-                0,
-                sizeof (aa));
-        json_array_foreach (accounts, index, value)
-        {
-          struct GNUNET_JSON_Specification ispec[] = {
-            GNUNET_JSON_spec_fixed_auto ("salt",
-                                         &aa[index].salt),
-            GNUNET_JSON_spec_mark_optional (
-              GNUNET_JSON_spec_string ("credit_facade_url",
-                                       &aa[index].credit_facade_url),
-              NULL),
-            GNUNET_JSON_spec_string ("payto_uri",
-                                     &aa[index].payto_uri),
-            GNUNET_JSON_spec_fixed_auto ("h_wire",
-                                         &aa[index].h_wire),
-            GNUNET_JSON_spec_bool ("active",
-                                   &aa[index].active),
-            GNUNET_JSON_spec_end ()
-          };
-
-          if (GNUNET_OK !=
-              GNUNET_JSON_parse (value,
-                                 ispec,
-                                 NULL, NULL))
-          {
-            GNUNET_break_op (0);
-            ret = GNUNET_SYSERR;
-            igr.hr.http_status = 0;
-            igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
-            break;
-          }
-        }
-
-        if (GNUNET_OK == ret)
-        {
-          igr.details.ok.details.address = address;
-          igr.details.ok.details.jurisdiction = jurisdiction;
-          if (GNUNET_OK !=
-              TALER_KYCLOGIC_kyc_user_type_from_string (
-                uts,
-                &igr.details.ok.details.ut))
-          {
-            GNUNET_break_op (0);
-            ret = GNUNET_SYSERR;
-            igr.hr.http_status = 0;
-            igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
-            break;
-          }
-          igr.details.ok.accounts_length = accounts_length;
-          igr.details.ok.accounts = aa;
-          igh->cb (igh->cb_cls,
-                   &igr);
-          TALER_MERCHANT_instance_get_cancel (igh);
-          return;
-        }
+        GNUNET_break_op (0);
+        igr.hr.http_status = 0;
+        igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+        break;
+      }
+      igr.details.ok.details.address = address;
+      igr.details.ok.details.jurisdiction = jurisdiction;
+      if (GNUNET_OK !=
+          TALER_KYCLOGIC_kyc_user_type_from_string (
+                                                    uts,
+                                                    
&igr.details.ok.details.ut))
+      {
+        GNUNET_break_op (0);
+        igr.hr.http_status = 0;
+        igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+        break;
       }
-      GNUNET_break_op (0);
-      igr.hr.http_status = 0;
-      igr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
-      break;
+      igh->cb (igh->cb_cls,
+               &igr);
+      TALER_MERCHANT_instance_get_cancel (igh);
+      return;
     }
   case MHD_HTTP_UNAUTHORIZED:
     igr.hr.ec = TALER_JSON_get_error_code (json);
diff --git a/src/lib/merchant_api_get_template.c 
b/src/lib/merchant_api_get_otp_device.c
similarity index 71%
copy from src/lib/merchant_api_get_template.c
copy to src/lib/merchant_api_get_otp_device.c
index 3e4a23a8..93e065af 100644
--- a/src/lib/merchant_api_get_template.c
+++ b/src/lib/merchant_api_get_otp_device.c
@@ -15,9 +15,9 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_get_template.c
- * @brief Implementation of the GET /templates/$ID request of the merchant's 
HTTP API
- * @author Priscilla HUANG
+ * @file merchant_api_get_otp_device.c
+ * @brief Implementation of the GET /otp-devices/$ID request of the merchant's 
HTTP API
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <curl/curl.h>
@@ -32,9 +32,9 @@
 
 
 /**
- * Handle for a GET /templates/$ID operation.
+ * Handle for a GET /otp-devices/$ID operation.
  */
-struct TALER_MERCHANT_TemplateGetHandle
+struct TALER_MERCHANT_OtpDeviceGetHandle
 {
   /**
    * The url for this request.
@@ -49,7 +49,7 @@ struct TALER_MERCHANT_TemplateGetHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplateGetCallback cb;
+  TALER_MERCHANT_OtpDeviceGetCallback cb;
 
   /**
    * Closure for @a cb.
@@ -66,45 +66,44 @@ struct TALER_MERCHANT_TemplateGetHandle
 
 /**
  * Function called when we're done processing the
- * HTTP GET /templates/$ID request.
+ * HTTP GET /otp-devices/$ID request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplateGetHandle`
+ * @param cls the `struct TALER_MERCHANT_OtpDeviceGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_get_template_finished (void *cls,
+handle_get_otp_device_finished (void *cls,
                               long response_code,
                               const void *response)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh = cls;
+  struct TALER_MERCHANT_OtpDeviceGetHandle *tgh = cls;
   const json_t *json = response;
-  struct TALER_MERCHANT_TemplateGetResponse tgr = {
+  struct TALER_MERCHANT_OtpDeviceGetResponse tgr = {
     .hr.http_status = (unsigned int) response_code,
     .hr.reply = json
   };
 
   tgh->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got /templates/$ID response with status code %u\n",
+              "Got /otp-devices/$ID response with status code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
   case MHD_HTTP_OK:
     {
       uint32_t alg32;
-      const json_t *contract;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_string ("template_description",
-                                 &tgr.details.ok.template_description),
-        GNUNET_JSON_spec_uint32 ("pos_algorithm",
+        GNUNET_JSON_spec_string ("otp_device_description",
+                                 &tgr.details.ok.otp_device_description),
+        GNUNET_JSON_spec_uint32 ("otp_algorithm",
                                  &alg32),
+        GNUNET_JSON_spec_string ("otp_key",
+                                 &tgr.details.ok.otp_key),
         GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("pos_key",
-                                   &tgr.details.ok.pos_key),
+          GNUNET_JSON_spec_uint64 ("otp_ctr",
+                                   &tgr.details.ok.otp_ctr),
           NULL),
-        GNUNET_JSON_spec_object_const ("template_contract",
-                                       &contract),
         GNUNET_JSON_spec_end ()
       };
 
@@ -113,12 +112,11 @@ handle_get_template_finished (void *cls,
                              spec,
                              NULL, NULL))
       {
-        tgr.details.ok.mca =
+        tgr.details.ok.otp_alg =
           (enum TALER_MerchantConfirmationAlgorithm) alg32;
-        tgr.details.ok.template_contract = contract;
         tgh->cb (tgh->cb_cls,
                  &tgr);
-        TALER_MERCHANT_template_get_cancel (tgh);
+        TALER_MERCHANT_otp_device_get_cancel (tgh);
         return;
       }
       tgr.hr.http_status = 0;
@@ -146,22 +144,22 @@ handle_get_template_finished (void *cls,
   }
   tgh->cb (tgh->cb_cls,
            &tgr);
-  TALER_MERCHANT_template_get_cancel (tgh);
+  TALER_MERCHANT_otp_device_get_cancel (tgh);
 }
 
 
-struct TALER_MERCHANT_TemplateGetHandle *
-TALER_MERCHANT_template_get (
+struct TALER_MERCHANT_OtpDeviceGetHandle *
+TALER_MERCHANT_otp_device_get (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  TALER_MERCHANT_TemplateGetCallback cb,
+  const char *otp_device_id,
+  TALER_MERCHANT_OtpDeviceGetCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh;
+  struct TALER_MERCHANT_OtpDeviceGetHandle *tgh;
   CURL *eh;
 
-  tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle);
+  tgh = GNUNET_new (struct TALER_MERCHANT_OtpDeviceGetHandle);
   tgh->ctx = ctx;
   tgh->cb = cb;
   tgh->cb_cls = cb_cls;
@@ -169,8 +167,8 @@ TALER_MERCHANT_template_get (
     char *path;
 
     GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
+                     "private/otp-devices/%s",
+                     otp_device_id);
     tgh->url = TALER_url_join (backend_url,
                                path,
                                NULL);
@@ -189,15 +187,15 @@ TALER_MERCHANT_template_get (
   eh = TALER_MERCHANT_curl_easy_get_ (tgh->url);
   tgh->job = GNUNET_CURL_job_add (ctx,
                                   eh,
-                                  &handle_get_template_finished,
+                                  &handle_get_otp_device_finished,
                                   tgh);
   return tgh;
 }
 
 
 void
-TALER_MERCHANT_template_get_cancel (
-  struct TALER_MERCHANT_TemplateGetHandle *tgh)
+TALER_MERCHANT_otp_device_get_cancel (
+  struct TALER_MERCHANT_OtpDeviceGetHandle *tgh)
 {
   if (NULL != tgh->job)
     GNUNET_CURL_job_cancel (tgh->job);
diff --git a/src/lib/merchant_api_get_template.c 
b/src/lib/merchant_api_get_otp_devices.c
similarity index 53%
copy from src/lib/merchant_api_get_template.c
copy to src/lib/merchant_api_get_otp_devices.c
index 3e4a23a8..3e48486a 100644
--- a/src/lib/merchant_api_get_template.c
+++ b/src/lib/merchant_api_get_otp_devices.c
@@ -15,9 +15,9 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_get_template.c
- * @brief Implementation of the GET /templates/$ID request of the merchant's 
HTTP API
- * @author Priscilla HUANG
+ * @file merchant_api_get_otp_devices.c
+ * @brief Implementation of the GET /otp-devices request of the merchant's 
HTTP API
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <curl/curl.h>
@@ -32,9 +32,9 @@
 
 
 /**
- * Handle for a GET /templates/$ID operation.
+ * Handle for a GET /otp-devices operation.
  */
-struct TALER_MERCHANT_TemplateGetHandle
+struct TALER_MERCHANT_OtpDevicesGetHandle
 {
   /**
    * The url for this request.
@@ -49,7 +49,7 @@ struct TALER_MERCHANT_TemplateGetHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplateGetCallback cb;
+  TALER_MERCHANT_OtpDevicesGetCallback cb;
 
   /**
    * Closure for @a cb.
@@ -64,61 +64,102 @@ struct TALER_MERCHANT_TemplateGetHandle
 };
 
 
+/**
+ * Parse OTP device information from @a ia.
+ *
+ * @param ia JSON array (or NULL!) with otp_device data
+ * @param[in] tgr partially filled response
+ * @param tgh operation handle
+ * @return #GNUNET_OK on success
+ */
+static enum GNUNET_GenericReturnValue
+parse_otp_devices (const json_t *ia,
+                   struct TALER_MERCHANT_OtpDevicesGetResponse *tgr,
+                   struct TALER_MERCHANT_OtpDevicesGetHandle *tgh)
+{
+  unsigned int tmpl_len = json_array_size (ia);
+  struct TALER_MERCHANT_OtpDeviceEntry tmpl[GNUNET_NZL (tmpl_len)];
+  size_t index;
+  json_t *value;
+
+  json_array_foreach (ia, index, value) {
+    struct TALER_MERCHANT_OtpDeviceEntry *ie = &tmpl[index];
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_string ("otp_device_id",
+                               &ie->otp_device_id),
+      GNUNET_JSON_spec_string ("device_description",
+                               &ie->device_description),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (value,
+                           spec,
+                           NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+  }
+  tgr->details.ok.otp_devices_length = tmpl_len;
+  tgr->details.ok.otp_devices = tmpl;
+  tgh->cb (tgh->cb_cls,
+           tgr);
+  tgh->cb = NULL; /* just to be sure */
+  return GNUNET_OK;
+}
+
+
 /**
  * Function called when we're done processing the
- * HTTP GET /templates/$ID request.
+ * HTTP /otp-devices request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplateGetHandle`
+ * @param cls the `struct TALER_MERCHANT_OtpDevicesGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_get_template_finished (void *cls,
-                              long response_code,
-                              const void *response)
+handle_get_otp_devices_finished (void *cls,
+                                 long response_code,
+                                 const void *response)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh = cls;
+  struct TALER_MERCHANT_OtpDevicesGetHandle *tgh = cls;
   const json_t *json = response;
-  struct TALER_MERCHANT_TemplateGetResponse tgr = {
+  struct TALER_MERCHANT_OtpDevicesGetResponse tgr = {
     .hr.http_status = (unsigned int) response_code,
     .hr.reply = json
   };
 
   tgh->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got /templates/$ID response with status code %u\n",
+              "Got /otp-devices response with status code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
   case MHD_HTTP_OK:
     {
-      uint32_t alg32;
-      const json_t *contract;
+      const json_t *otp_devices;
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_string ("template_description",
-                                 &tgr.details.ok.template_description),
-        GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                                 &alg32),
-        GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("pos_key",
-                                   &tgr.details.ok.pos_key),
-          NULL),
-        GNUNET_JSON_spec_object_const ("template_contract",
-                                       &contract),
+        GNUNET_JSON_spec_array_const ("otp_devices",
+                                      &otp_devices),
         GNUNET_JSON_spec_end ()
       };
 
-      if (GNUNET_OK ==
+      if (GNUNET_OK !=
           GNUNET_JSON_parse (json,
                              spec,
                              NULL, NULL))
       {
-        tgr.details.ok.mca =
-          (enum TALER_MerchantConfirmationAlgorithm) alg32;
-        tgr.details.ok.template_contract = contract;
-        tgh->cb (tgh->cb_cls,
-                 &tgr);
-        TALER_MERCHANT_template_get_cancel (tgh);
+        tgr.hr.http_status = 0;
+        tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+        break;
+      }
+      if (GNUNET_OK ==
+          parse_otp_devices (otp_devices,
+                           &tgr,
+                           tgh))
+      {
+        TALER_MERCHANT_otp_devices_get_cancel (tgh);
         return;
       }
       tgr.hr.http_status = 0;
@@ -130,10 +171,6 @@ handle_get_template_finished (void *cls,
     tgr.hr.hint = TALER_JSON_get_error_hint (json);
     /* Nothing really to verify, merchant says we need to authenticate. */
     break;
-  case MHD_HTTP_NOT_FOUND:
-    tgr.hr.ec = TALER_JSON_get_error_code (json);
-    tgr.hr.hint = TALER_JSON_get_error_hint (json);
-    break;
   default:
     /* unexpected response code */
     tgr.hr.ec = TALER_JSON_get_error_code (json);
@@ -146,36 +183,27 @@ handle_get_template_finished (void *cls,
   }
   tgh->cb (tgh->cb_cls,
            &tgr);
-  TALER_MERCHANT_template_get_cancel (tgh);
+  TALER_MERCHANT_otp_devices_get_cancel (tgh);
 }
 
 
-struct TALER_MERCHANT_TemplateGetHandle *
-TALER_MERCHANT_template_get (
+struct TALER_MERCHANT_OtpDevicesGetHandle *
+TALER_MERCHANT_otp_devices_get (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  TALER_MERCHANT_TemplateGetCallback cb,
+  TALER_MERCHANT_OtpDevicesGetCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplateGetHandle *tgh;
+  struct TALER_MERCHANT_OtpDevicesGetHandle *tgh;
   CURL *eh;
 
-  tgh = GNUNET_new (struct TALER_MERCHANT_TemplateGetHandle);
+  tgh = GNUNET_new (struct TALER_MERCHANT_OtpDevicesGetHandle);
   tgh->ctx = ctx;
   tgh->cb = cb;
   tgh->cb_cls = cb_cls;
-  {
-    char *path;
-
-    GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
-    tgh->url = TALER_url_join (backend_url,
-                               path,
-                               NULL);
-    GNUNET_free (path);
-  }
+  tgh->url = TALER_url_join (backend_url,
+                             "private/otp-devices",
+                             NULL);
   if (NULL == tgh->url)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -189,15 +217,15 @@ TALER_MERCHANT_template_get (
   eh = TALER_MERCHANT_curl_easy_get_ (tgh->url);
   tgh->job = GNUNET_CURL_job_add (ctx,
                                   eh,
-                                  &handle_get_template_finished,
+                                  &handle_get_otp_devices_finished,
                                   tgh);
   return tgh;
 }
 
 
 void
-TALER_MERCHANT_template_get_cancel (
-  struct TALER_MERCHANT_TemplateGetHandle *tgh)
+TALER_MERCHANT_otp_devices_get_cancel (
+  struct TALER_MERCHANT_OtpDevicesGetHandle *tgh)
 {
   if (NULL != tgh->job)
     GNUNET_CURL_job_cancel (tgh->job);
diff --git a/src/lib/merchant_api_get_template.c 
b/src/lib/merchant_api_get_template.c
index 3e4a23a8..9bbcc93a 100644
--- a/src/lib/merchant_api_get_template.c
+++ b/src/lib/merchant_api_get_template.c
@@ -92,16 +92,13 @@ handle_get_template_finished (void *cls,
   {
   case MHD_HTTP_OK:
     {
-      uint32_t alg32;
       const json_t *contract;
       struct GNUNET_JSON_Specification spec[] = {
         GNUNET_JSON_spec_string ("template_description",
                                  &tgr.details.ok.template_description),
-        GNUNET_JSON_spec_uint32 ("pos_algorithm",
-                                 &alg32),
         GNUNET_JSON_spec_mark_optional (
-          GNUNET_JSON_spec_string ("pos_key",
-                                   &tgr.details.ok.pos_key),
+          GNUNET_JSON_spec_string ("otp_id",
+                                   &tgr.details.ok.otp_id),
           NULL),
         GNUNET_JSON_spec_object_const ("template_contract",
                                        &contract),
@@ -113,8 +110,6 @@ handle_get_template_finished (void *cls,
                              spec,
                              NULL, NULL))
       {
-        tgr.details.ok.mca =
-          (enum TALER_MerchantConfirmationAlgorithm) alg32;
         tgr.details.ok.template_contract = contract;
         tgh->cb (tgh->cb_cls,
                  &tgr);
diff --git a/src/lib/merchant_api_patch_template.c 
b/src/lib/merchant_api_patch_account.c
similarity index 76%
copy from src/lib/merchant_api_patch_template.c
copy to src/lib/merchant_api_patch_account.c
index 0922586a..ce0e74d4 100644
--- a/src/lib/merchant_api_patch_template.c
+++ b/src/lib/merchant_api_patch_account.c
@@ -17,8 +17,8 @@
   If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_patch_template.c
- * @brief Implementation of the PATCH /templates/$ID request
+ * @file merchant_api_patch_account.c
+ * @brief Implementation of the PATCH /accounts/$ID request
  *        of the merchant's HTTP API
  * @author Priscilla HUANG
  */
@@ -35,9 +35,9 @@
 
 
 /**
- * Handle for a PATCH /templates/$ID operation.
+ * Handle for a PATCH /accounts/$ID operation.
  */
-struct TALER_MERCHANT_TemplatePatchHandle
+struct TALER_MERCHANT_AccountPatchHandle
 {
 
   /**
@@ -53,7 +53,7 @@ struct TALER_MERCHANT_TemplatePatchHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplatePatchCallback cb;
+  TALER_MERCHANT_AccountPatchCallback cb;
 
   /**
    * Closure for @a cb.
@@ -75,18 +75,18 @@ struct TALER_MERCHANT_TemplatePatchHandle
 
 /**
  * Function called when we're done processing the
- * HTTP PATCH /templates/$ID request.
+ * HTTP PATCH /accounts/$ID request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle`
+ * @param cls the `struct TALER_MERCHANT_AccountPatchHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_patch_template_finished (void *cls,
+handle_patch_account_finished (void *cls,
                                 long response_code,
                                 const void *response)
 {
-  struct TALER_MERCHANT_TemplatePatchHandle *tph = cls;
+  struct TALER_MERCHANT_AccountPatchHandle *tph = cls;
   const json_t *json = response;
   struct TALER_MERCHANT_HttpResponse hr = {
     .http_status = (unsigned int) response_code,
@@ -95,7 +95,7 @@ handle_patch_template_finished (void *cls,
 
   tph->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "PATCH /templates/$ID completed with response code %u\n",
+              "PATCH /accounts/$ID completed with response code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
@@ -152,45 +152,47 @@ handle_patch_template_finished (void *cls,
   }
   tph->cb (tph->cb_cls,
            &hr);
-  TALER_MERCHANT_template_patch_cancel (tph);
+  TALER_MERCHANT_account_patch_cancel (tph);
 }
 
 
-struct TALER_MERCHANT_TemplatePatchHandle *
-TALER_MERCHANT_template_patch (
+struct TALER_MERCHANT_AccountPatchHandle *
+TALER_MERCHANT_account_patch (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  const char *template_description,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm mca,
-  json_t *template_contract,
-  TALER_MERCHANT_TemplatePatchCallback cb,
+  const struct TALER_MerchantWireHashP *h_wire,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials,
+  TALER_MERCHANT_AccountPatchCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplatePatchHandle *tph;
+  struct TALER_MERCHANT_AccountPatchHandle *tph;
   json_t *req_obj;
 
   req_obj = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_string ("template_description",
-                             template_description),
-    GNUNET_JSON_pack_uint64 ("pos_algorithm",
-                             (uint32_t) mca),
     GNUNET_JSON_pack_allow_null (
-      GNUNET_JSON_pack_string ("pos_key",
-                               pos_key)),
-    GNUNET_JSON_pack_object_incref ("template_contract",
-                                    (json_t *) template_contract));
-  tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle);
+      GNUNET_JSON_pack_string ("credit_facade_url",
+                               credit_facade_url)),
+    GNUNET_JSON_pack_allow_null (
+      GNUNET_JSON_pack_object_incref ("credit_facade_credentials",
+                                      (json_t *) credit_facade_credentials)));
+  tph = GNUNET_new (struct TALER_MERCHANT_AccountPatchHandle);
   tph->ctx = ctx;
   tph->cb = cb;
   tph->cb_cls = cb_cls;
   {
+    char w_str[sizeof (*h_wire) * 2];
     char *path;
+    char *end;
 
+    end = GNUNET_STRINGS_data_to_string (h_wire,
+                                         sizeof (*h_wire),
+                                         w_str,
+                                         sizeof (w_str));
+    *end = '\0';
     GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
+                     "private/accounts/%s",
+                     w_str);
     tph->url = TALER_url_join (backend_url,
                                path,
                                NULL);
@@ -227,7 +229,7 @@ TALER_MERCHANT_template_patch (
     tph->job = GNUNET_CURL_job_add2 (ctx,
                                      eh,
                                      tph->post_ctx.headers,
-                                     &handle_patch_template_finished,
+                                     &handle_patch_account_finished,
                                      tph);
   }
   return tph;
@@ -235,8 +237,8 @@ TALER_MERCHANT_template_patch (
 
 
 void
-TALER_MERCHANT_template_patch_cancel (
-  struct TALER_MERCHANT_TemplatePatchHandle *tph)
+TALER_MERCHANT_account_patch_cancel (
+  struct TALER_MERCHANT_AccountPatchHandle *tph)
 {
   if (NULL != tph->job)
   {
@@ -249,4 +251,4 @@ TALER_MERCHANT_template_patch_cancel (
 }
 
 
-/* end of merchant_api_patch_template.c */
+/* end of merchant_api_patch_account.c */
diff --git a/src/lib/merchant_api_patch_instance.c 
b/src/lib/merchant_api_patch_instance.c
index 8b4b6205..420cd549 100644
--- a/src/lib/merchant_api_patch_instance.c
+++ b/src/lib/merchant_api_patch_instance.c
@@ -158,8 +158,6 @@ TALER_MERCHANT_instance_patch (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
   const char *instance_id,
-  unsigned int accounts_length,
-  const struct TALER_MERCHANT_AccountConfig accounts[static accounts_length],
   const char *name,
   enum TALER_KYCLOGIC_KycUserType ut,
   const json_t *address,
@@ -171,7 +169,6 @@ TALER_MERCHANT_instance_patch (
   void *cb_cls)
 {
   struct TALER_MERCHANT_InstancePatchHandle *iph;
-  json_t *jaccounts;
   json_t *req_obj;
   const char *uts;
 
@@ -181,44 +178,7 @@ TALER_MERCHANT_instance_patch (
     GNUNET_break (0);
     return NULL;
   }
-  jaccounts = json_array ();
-  if (NULL == jaccounts)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  for (unsigned int i = 0; i<accounts_length; i++)
-  {
-    const struct TALER_MERCHANT_AccountConfig *account = &accounts[i];
-    json_t *jaccount;
-
-    jaccount =
-      GNUNET_JSON_PACK (
-        GNUNET_JSON_pack_string (
-          "payto_uri",
-          account->payto_uri),
-        GNUNET_JSON_pack_allow_null (
-          GNUNET_JSON_pack_string (
-            "credit_facade_url",
-            account->credit_facade_url)),
-        GNUNET_JSON_pack_allow_null (
-          GNUNET_JSON_pack_object_incref (
-            "credit_facade_credentials",
-            accounts->credit_facade_credentials))
-        );
-
-    if (0 !=
-        json_array_append_new (jaccounts,
-                               jaccount))
-    {
-      GNUNET_break (0);
-      json_decref (jaccounts);
-      return NULL;
-    }
-  }
   req_obj = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_array_steal ("accounts",
-                                  jaccounts),
     GNUNET_JSON_pack_string ("name",
                              name),
     GNUNET_JSON_pack_string ("user_type",
diff --git a/src/lib/merchant_api_patch_template.c 
b/src/lib/merchant_api_patch_otp_device.c
similarity index 76%
copy from src/lib/merchant_api_patch_template.c
copy to src/lib/merchant_api_patch_otp_device.c
index 0922586a..322efe7b 100644
--- a/src/lib/merchant_api_patch_template.c
+++ b/src/lib/merchant_api_patch_otp_device.c
@@ -17,10 +17,10 @@
   If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_patch_template.c
- * @brief Implementation of the PATCH /templates/$ID request
+ * @file merchant_api_patch_otp_device.c
+ * @brief Implementation of the PATCH /otp-devices/$ID request
  *        of the merchant's HTTP API
- * @author Priscilla HUANG
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <curl/curl.h>
@@ -35,9 +35,9 @@
 
 
 /**
- * Handle for a PATCH /templates/$ID operation.
+ * Handle for a PATCH /otp-devices/$ID operation.
  */
-struct TALER_MERCHANT_TemplatePatchHandle
+struct TALER_MERCHANT_OtpDevicePatchHandle
 {
 
   /**
@@ -53,7 +53,7 @@ struct TALER_MERCHANT_TemplatePatchHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplatePatchCallback cb;
+  TALER_MERCHANT_OtpDevicePatchCallback cb;
 
   /**
    * Closure for @a cb.
@@ -75,18 +75,18 @@ struct TALER_MERCHANT_TemplatePatchHandle
 
 /**
  * Function called when we're done processing the
- * HTTP PATCH /templates/$ID request.
+ * HTTP PATCH /otp-devices/$ID request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle`
+ * @param cls the `struct TALER_MERCHANT_OtpDevicePatchHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_patch_template_finished (void *cls,
-                                long response_code,
-                                const void *response)
+handle_patch_otp_device_finished (void *cls,
+                                  long response_code,
+                                  const void *response)
 {
-  struct TALER_MERCHANT_TemplatePatchHandle *tph = cls;
+  struct TALER_MERCHANT_OtpDevicePatchHandle *tph = cls;
   const json_t *json = response;
   struct TALER_MERCHANT_HttpResponse hr = {
     .http_status = (unsigned int) response_code,
@@ -95,7 +95,7 @@ handle_patch_template_finished (void *cls,
 
   tph->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "PATCH /templates/$ID completed with response code %u\n",
+              "PATCH /otp-devices/$ID completed with response code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
@@ -152,36 +152,36 @@ handle_patch_template_finished (void *cls,
   }
   tph->cb (tph->cb_cls,
            &hr);
-  TALER_MERCHANT_template_patch_cancel (tph);
+  TALER_MERCHANT_otp_device_patch_cancel (tph);
 }
 
 
-struct TALER_MERCHANT_TemplatePatchHandle *
-TALER_MERCHANT_template_patch (
+struct TALER_MERCHANT_OtpDevicePatchHandle *
+TALER_MERCHANT_otp_device_patch (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  const char *template_description,
-  const char *pos_key,
+  const char *otp_device_id,
+  const char *otp_device_description,
+  const char *otp_key,
   enum TALER_MerchantConfirmationAlgorithm mca,
-  json_t *template_contract,
-  TALER_MERCHANT_TemplatePatchCallback cb,
+  uint64_t otp_ctr,
+  TALER_MERCHANT_OtpDevicePatchCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplatePatchHandle *tph;
+  struct TALER_MERCHANT_OtpDevicePatchHandle *tph;
   json_t *req_obj;
 
   req_obj = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_string ("template_description",
-                             template_description),
-    GNUNET_JSON_pack_uint64 ("pos_algorithm",
+    GNUNET_JSON_pack_string ("otp_device_description",
+                             otp_device_description),
+    GNUNET_JSON_pack_uint64 ("otp_algorithm",
                              (uint32_t) mca),
     GNUNET_JSON_pack_allow_null (
-      GNUNET_JSON_pack_string ("pos_key",
-                               pos_key)),
-    GNUNET_JSON_pack_object_incref ("template_contract",
-                                    (json_t *) template_contract));
-  tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle);
+      GNUNET_JSON_pack_string ("otp_key",
+                               otp_key)),
+    GNUNET_JSON_pack_uint64 ("otp_ctr",
+                             otp_ctr));
+  tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicePatchHandle);
   tph->ctx = ctx;
   tph->cb = cb;
   tph->cb_cls = cb_cls;
@@ -189,8 +189,8 @@ TALER_MERCHANT_template_patch (
     char *path;
 
     GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
+                     "private/otp-devices/%s",
+                     otp_device_id);
     tph->url = TALER_url_join (backend_url,
                                path,
                                NULL);
@@ -227,7 +227,7 @@ TALER_MERCHANT_template_patch (
     tph->job = GNUNET_CURL_job_add2 (ctx,
                                      eh,
                                      tph->post_ctx.headers,
-                                     &handle_patch_template_finished,
+                                     &handle_patch_otp_device_finished,
                                      tph);
   }
   return tph;
@@ -235,8 +235,8 @@ TALER_MERCHANT_template_patch (
 
 
 void
-TALER_MERCHANT_template_patch_cancel (
-  struct TALER_MERCHANT_TemplatePatchHandle *tph)
+TALER_MERCHANT_otp_device_patch_cancel (
+  struct TALER_MERCHANT_OtpDevicePatchHandle *tph)
 {
   if (NULL != tph->job)
   {
@@ -249,4 +249,4 @@ TALER_MERCHANT_template_patch_cancel (
 }
 
 
-/* end of merchant_api_patch_template.c */
+/* end of merchant_api_patch_otp_device.c */
diff --git a/src/lib/merchant_api_patch_template.c 
b/src/lib/merchant_api_patch_template.c
index 0922586a..7dfebf9c 100644
--- a/src/lib/merchant_api_patch_template.c
+++ b/src/lib/merchant_api_patch_template.c
@@ -162,8 +162,7 @@ TALER_MERCHANT_template_patch (
   const char *backend_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm mca,
+  const char *otp_id,
   json_t *template_contract,
   TALER_MERCHANT_TemplatePatchCallback cb,
   void *cb_cls)
@@ -174,11 +173,9 @@ TALER_MERCHANT_template_patch (
   req_obj = GNUNET_JSON_PACK (
     GNUNET_JSON_pack_string ("template_description",
                              template_description),
-    GNUNET_JSON_pack_uint64 ("pos_algorithm",
-                             (uint32_t) mca),
     GNUNET_JSON_pack_allow_null (
-      GNUNET_JSON_pack_string ("pos_key",
-                               pos_key)),
+      GNUNET_JSON_pack_string ("otp_id",
+                               otp_id)),
     GNUNET_JSON_pack_object_incref ("template_contract",
                                     (json_t *) template_contract));
   tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle);
diff --git a/src/lib/merchant_api_post_account.c 
b/src/lib/merchant_api_post_account.c
index 5ed3f3b3..690aef17 100644
--- a/src/lib/merchant_api_post_account.c
+++ b/src/lib/merchant_api_post_account.c
@@ -35,9 +35,9 @@
 
 
 /**
- * Handle for a POST /private/account operation.
+ * Handle for a POST /private/accounts operation.
  */
-struct TALER_MERCHANT_AccountPostHandle
+struct TALER_MERCHANT_AccountsPostHandle
 {
 
   /**
@@ -53,7 +53,7 @@ struct TALER_MERCHANT_AccountPostHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_AccountPostCallback cb;
+  TALER_MERCHANT_AccountsPostCallback cb;
 
   /**
    * Closure for @a cb.
@@ -86,23 +86,43 @@ handle_post_account_finished (void *cls,
                               long response_code,
                               const void *response)
 {
-  struct TALER_MERCHANT_AccountPostHandle *aph = cls;
+  struct TALER_MERCHANT_AccountsPostHandle *aph = cls;
   const json_t *json = response;
-  struct TALER_MERCHANT_AccountPostResponse apr = {
+  struct TALER_MERCHANT_AccountsPostResponse apr = {
     .hr.http_status = (unsigned int) response_code,
     .hr.reply = json
   };
 
   aph->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "POST /account completed with response code %u\n",
+              "POST /accounts completed with response code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
   case 0:
     apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     break;
-  case MHD_HTTP_NO_CONTENT:
+  case MHD_HTTP_OK:
+    {
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("h_wire",
+                                     &apr.details.ok.h_wire),
+        GNUNET_JSON_spec_fixed_auto ("salt",
+                                     &apr.details.ok.salt),
+        GNUNET_JSON_spec_end ()
+      };
+
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (json,
+                             spec,
+                             NULL, NULL))
+        {
+        GNUNET_break_op (0);
+        apr.hr.http_status = 0;
+        apr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+        break;
+      }
+    }
     break;
   case MHD_HTTP_BAD_REQUEST:
     GNUNET_break_op (0);
@@ -147,40 +167,42 @@ handle_post_account_finished (void *cls,
   }
   aph->cb (aph->cb_cls,
            &apr);
-  TALER_MERCHANT_account_post_cancel (aph);
+  TALER_MERCHANT_accounts_post_cancel (aph);
 }
 
 
-struct TALER_MERCHANT_AccountPostHandle *
-TALER_MERCHANT_account_post (
+struct TALER_MERCHANT_AccountsPostHandle *
+TALER_MERCHANT_accounts_post (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const struct TALER_MERCHANT_AccountConfig *account,
-  TALER_MERCHANT_AccountPostCallback cb,
+  const char *payto_uri,
+  const char *credit_facade_url,
+  const json_t *credit_facade_credentials,
+  TALER_MERCHANT_AccountsPostCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_AccountPostHandle *aph;
+  struct TALER_MERCHANT_AccountsPostHandle *aph;
   json_t *req_obj;
 
   req_obj = GNUNET_JSON_PACK (
     GNUNET_JSON_pack_string (
       "payto_uri",
-      account->payto_uri),
+      payto_uri),
     GNUNET_JSON_pack_allow_null (
       GNUNET_JSON_pack_string (
         "credit_facade_url",
-        account->credit_facade_url)),
+        credit_facade_url)),
     GNUNET_JSON_pack_allow_null (
       GNUNET_JSON_pack_object_incref (
         "credit_facade_credentials",
-        account->credit_facade_credentials))
+        (json_t *) credit_facade_credentials))
     );
-  aph = GNUNET_new (struct TALER_MERCHANT_AccountPostHandle);
+  aph = GNUNET_new (struct TALER_MERCHANT_AccountsPostHandle);
   aph->ctx = ctx;
   aph->cb = cb;
   aph->cb_cls = cb_cls;
   aph->url = TALER_url_join (backend_url,
-                             "private/account",
+                             "private/accounts",
                              NULL);
   if (NULL == aph->url)
   {
@@ -211,8 +233,8 @@ TALER_MERCHANT_account_post (
 
 
 void
-TALER_MERCHANT_account_post_cancel (
-  struct TALER_MERCHANT_AccountPostHandle *aph)
+TALER_MERCHANT_accounts_post_cancel (
+  struct TALER_MERCHANT_AccountsPostHandle *aph)
 {
   if (NULL != aph->job)
   {
diff --git a/src/lib/merchant_api_post_instances.c 
b/src/lib/merchant_api_post_instances.c
index 77336187..73d36369 100644
--- a/src/lib/merchant_api_post_instances.c
+++ b/src/lib/merchant_api_post_instances.c
@@ -164,8 +164,6 @@ TALER_MERCHANT_instances_post (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
   const char *instance_id,
-  unsigned int accounts_length,
-  const struct TALER_MERCHANT_AccountConfig accounts[],
   const char *name,
   enum TALER_KYCLOGIC_KycUserType ut,
   const json_t *address,
@@ -178,7 +176,6 @@ TALER_MERCHANT_instances_post (
   void *cb_cls)
 {
   struct TALER_MERCHANT_InstancesPostHandle *iph;
-  json_t *jaccounts;
   json_t *req_obj;
   json_t *auth_obj;
   const char *uts;
@@ -217,44 +214,7 @@ TALER_MERCHANT_instances_post (
     GNUNET_break (0);
     return NULL;
   }
-  jaccounts = json_array ();
-  if (NULL == jaccounts)
-  {
-    json_decref (auth_obj);
-    GNUNET_break (0);
-    return NULL;
-  }
-  for (unsigned int i = 0; i<accounts_length; i++)
-  {
-    const struct TALER_MERCHANT_AccountConfig *account = &accounts[i];
-    json_t *jaccount =
-      GNUNET_JSON_PACK (
-        GNUNET_JSON_pack_string (
-          "payto_uri",
-          account->payto_uri),
-        GNUNET_JSON_pack_allow_null (
-          GNUNET_JSON_pack_string (
-            "credit_facade_url",
-            account->credit_facade_url)),
-        GNUNET_JSON_pack_allow_null (
-          GNUNET_JSON_pack_object_incref (
-            "credit_facade_credentials",
-            account->credit_facade_credentials))
-        );
-
-    if (0 !=
-        json_array_append_new (jaccounts,
-                               jaccount))
-    {
-      GNUNET_break (0);
-      json_decref (auth_obj);
-      json_decref (jaccounts);
-      return NULL;
-    }
-  }
   req_obj = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_array_steal ("accounts",
-                                  jaccounts),
     GNUNET_JSON_pack_string ("id",
                              instance_id),
     GNUNET_JSON_pack_string ("name",
diff --git a/src/lib/merchant_api_patch_template.c 
b/src/lib/merchant_api_post_otp_devices.c
similarity index 66%
copy from src/lib/merchant_api_patch_template.c
copy to src/lib/merchant_api_post_otp_devices.c
index 0922586a..456abd09 100644
--- a/src/lib/merchant_api_patch_template.c
+++ b/src/lib/merchant_api_post_otp_devices.c
@@ -17,10 +17,10 @@
   If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file merchant_api_patch_template.c
- * @brief Implementation of the PATCH /templates/$ID request
+ * @file merchant_api_post_otp_devices.c
+ * @brief Implementation of the POST /otp-devices request
  *        of the merchant's HTTP API
- * @author Priscilla HUANG
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <curl/curl.h>
@@ -28,16 +28,16 @@
 #include <microhttpd.h> /* just for HTTP status codes */
 #include <gnunet/gnunet_util_lib.h>
 #include "taler_merchant_service.h"
-#include "merchant_api_common.h"
 #include "merchant_api_curl_defaults.h"
+#include "merchant_api_common.h"
 #include <taler/taler_json_lib.h>
 #include <taler/taler_curl_lib.h>
 
 
 /**
- * Handle for a PATCH /templates/$ID operation.
+ * Handle for a POST /otp-devices/$ID operation.
  */
-struct TALER_MERCHANT_TemplatePatchHandle
+struct TALER_MERCHANT_OtpDevicesPostHandle
 {
 
   /**
@@ -53,7 +53,7 @@ struct TALER_MERCHANT_TemplatePatchHandle
   /**
    * Function to call with the result.
    */
-  TALER_MERCHANT_TemplatePatchCallback cb;
+  TALER_MERCHANT_OtpDevicesPostCallback cb;
 
   /**
    * Closure for @a cb.
@@ -69,24 +69,23 @@ struct TALER_MERCHANT_TemplatePatchHandle
    * Minor context that holds body and headers.
    */
   struct TALER_CURL_PostContext post_ctx;
-
 };
 
 
 /**
  * Function called when we're done processing the
- * HTTP PATCH /templates/$ID request.
+ * HTTP POST /otp-devices request.
  *
- * @param cls the `struct TALER_MERCHANT_TemplatePatchHandle`
+ * @param cls the `struct TALER_MERCHANT_OtpDevicesPostHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response response body, NULL if not in JSON
  */
 static void
-handle_patch_template_finished (void *cls,
-                                long response_code,
-                                const void *response)
+handle_post_otp_devices_finished (void *cls,
+                                  long response_code,
+                                  const void *response)
 {
-  struct TALER_MERCHANT_TemplatePatchHandle *tph = cls;
+  struct TALER_MERCHANT_OtpDevicesPostHandle *tph = cls;
   const json_t *json = response;
   struct TALER_MERCHANT_HttpResponse hr = {
     .http_status = (unsigned int) response_code,
@@ -95,7 +94,7 @@ handle_patch_template_finished (void *cls,
 
   tph->job = NULL;
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "PATCH /templates/$ID completed with response code %u\n",
+              "POST /otp-devices completed with response code %u\n",
               (unsigned int) response_code);
   switch (response_code)
   {
@@ -107,7 +106,6 @@ handle_patch_template_finished (void *cls,
   case MHD_HTTP_BAD_REQUEST:
     hr.ec = TALER_JSON_get_error_code (json);
     hr.hint = TALER_JSON_get_error_hint (json);
-    GNUNET_break_op (0);
     /* This should never happen, either us
      * or the merchant is buggy (or API version conflict);
      * just pass JSON reply to the application */
@@ -127,6 +125,9 @@ handle_patch_template_finished (void *cls,
   case MHD_HTTP_NOT_FOUND:
     hr.ec = TALER_JSON_get_error_code (json);
     hr.hint = TALER_JSON_get_error_hint (json);
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the
+       application */
     break;
   case MHD_HTTP_CONFLICT:
     hr.ec = TALER_JSON_get_error_code (json);
@@ -152,50 +153,44 @@ handle_patch_template_finished (void *cls,
   }
   tph->cb (tph->cb_cls,
            &hr);
-  TALER_MERCHANT_template_patch_cancel (tph);
+  TALER_MERCHANT_otp_devices_post_cancel (tph);
 }
 
 
-struct TALER_MERCHANT_TemplatePatchHandle *
-TALER_MERCHANT_template_patch (
+struct TALER_MERCHANT_OtpDevicesPostHandle *
+TALER_MERCHANT_otp_devices_post (
   struct GNUNET_CURL_Context *ctx,
   const char *backend_url,
-  const char *template_id,
-  const char *template_description,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm mca,
-  json_t *template_contract,
-  TALER_MERCHANT_TemplatePatchCallback cb,
+  const char *otp_device_id,
+  const char *otp_device_description,
+  const char *otp_key,
+  enum TALER_MerchantConfirmationAlgorithm otp_algorithm,
+  uint64_t otp_ctr,
+  TALER_MERCHANT_OtpDevicesPostCallback cb,
   void *cb_cls)
 {
-  struct TALER_MERCHANT_TemplatePatchHandle *tph;
+  struct TALER_MERCHANT_OtpDevicesPostHandle *tph;
   json_t *req_obj;
 
   req_obj = GNUNET_JSON_PACK (
-    GNUNET_JSON_pack_string ("template_description",
-                             template_description),
-    GNUNET_JSON_pack_uint64 ("pos_algorithm",
-                             (uint32_t) mca),
+    GNUNET_JSON_pack_string ("otp_device_id",
+                             otp_device_id),
+    GNUNET_JSON_pack_string ("otp_device_description",
+                             otp_device_description),
+    GNUNET_JSON_pack_uint64 ("otp_algorithm",
+                             (uint32_t) otp_algorithm),
     GNUNET_JSON_pack_allow_null (
-      GNUNET_JSON_pack_string ("pos_key",
-                               pos_key)),
-    GNUNET_JSON_pack_object_incref ("template_contract",
-                                    (json_t *) template_contract));
-  tph = GNUNET_new (struct TALER_MERCHANT_TemplatePatchHandle);
+      GNUNET_JSON_pack_string ("otp_key",
+                               otp_key)),
+    GNUNET_JSON_pack_uint64 ("otp_ctr",
+                             otp_ctr));
+  tph = GNUNET_new (struct TALER_MERCHANT_OtpDevicesPostHandle);
   tph->ctx = ctx;
   tph->cb = cb;
   tph->cb_cls = cb_cls;
-  {
-    char *path;
-
-    GNUNET_asprintf (&path,
-                     "private/templates/%s",
-                     template_id);
-    tph->url = TALER_url_join (backend_url,
-                               path,
-                               NULL);
-    GNUNET_free (path);
-  }
+  tph->url = TALER_url_join (backend_url,
+                             "private/otp-devices",
+                             NULL);
   if (NULL == tph->url)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -208,35 +203,25 @@ TALER_MERCHANT_template_patch (
     CURL *eh;
 
     eh = TALER_MERCHANT_curl_easy_get_ (tph->url);
-    if (GNUNET_OK !=
-        TALER_curl_easy_post (&tph->post_ctx,
-                              eh,
-                              req_obj))
-    {
-      GNUNET_break (0);
-      curl_easy_cleanup (eh);
-      json_decref (req_obj);
-      GNUNET_free (tph);
-      return NULL;
-    }
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_curl_easy_post (&tph->post_ctx,
+                                         eh,
+                                         req_obj));
     json_decref (req_obj);
-    GNUNET_assert (CURLE_OK ==
-                   curl_easy_setopt (eh,
-                                     CURLOPT_CUSTOMREQUEST,
-                                     MHD_HTTP_METHOD_PATCH));
     tph->job = GNUNET_CURL_job_add2 (ctx,
                                      eh,
                                      tph->post_ctx.headers,
-                                     &handle_patch_template_finished,
+                                     &handle_post_otp_devices_finished,
                                      tph);
+    GNUNET_assert (NULL != tph->job);
   }
   return tph;
 }
 
 
 void
-TALER_MERCHANT_template_patch_cancel (
-  struct TALER_MERCHANT_TemplatePatchHandle *tph)
+TALER_MERCHANT_otp_devices_post_cancel (
+  struct TALER_MERCHANT_OtpDevicesPostHandle *tph)
 {
   if (NULL != tph->job)
   {
@@ -249,4 +234,4 @@ TALER_MERCHANT_template_patch_cancel (
 }
 
 
-/* end of merchant_api_patch_template.c */
+/* end of merchant_api_post_otp_devices.c */
diff --git a/src/lib/merchant_api_post_templates.c 
b/src/lib/merchant_api_post_templates.c
index bdba52f9..3ab4320c 100644
--- a/src/lib/merchant_api_post_templates.c
+++ b/src/lib/merchant_api_post_templates.c
@@ -203,8 +203,7 @@ TALER_MERCHANT_templates_post (
   const char *backend_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  enum TALER_MerchantConfirmationAlgorithm pos_algorithm,
+  const char *otp_id,
   const json_t *template_contract,
   TALER_MERCHANT_TemplatesPostCallback cb,
   void *cb_cls)
@@ -222,11 +221,9 @@ TALER_MERCHANT_templates_post (
                              template_id),
     GNUNET_JSON_pack_string ("template_description",
                              template_description),
-    GNUNET_JSON_pack_uint64 ("pos_algorithm",
-                             (uint32_t) pos_algorithm),
     GNUNET_JSON_pack_allow_null (
-      GNUNET_JSON_pack_string ("pos_key",
-                               pos_key)),
+      GNUNET_JSON_pack_string ("otp_id",
+                               otp_id)),
     GNUNET_JSON_pack_object_incref ("template_contract",
                                     (json_t *) template_contract));
   tph = GNUNET_new (struct TALER_MERCHANT_TemplatesPostHandle);
diff --git a/src/merchant-tools/taler-merchant-benchmark.c 
b/src/merchant-tools/taler-merchant-benchmark.c
index ce4491e0..238b9f03 100644
--- a/src/merchant-tools/taler-merchant-benchmark.c
+++ b/src/merchant-tools/taler-merchant-benchmark.c
@@ -182,9 +182,13 @@ run (void *cls,
         "instance-create-default",
         merchant_url,
         "default",
-        cred.user42_payto,
         MHD_HTTP_NO_CONTENT),
-
+      TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-default-account",
+        merchant_url,
+        cred.user42_payto,
+        NULL, NULL,
+        MHD_HTTP_OK),
       TALER_TESTING_cmd_admin_add_incoming (
         "create-reserve-1",
         CURRENCY_10_02,
@@ -259,14 +263,24 @@ run (void *cls,
         "instance-create-default",
         merchant_url,
         "default",
-        cred.user42_payto,
         MHD_HTTP_NO_CONTENT),
+      TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-default-account",
+        merchant_url,
+        cred.user42_payto,
+        NULL, NULL,
+        MHD_HTTP_OK),
       TALER_TESTING_cmd_merchant_post_instances (
         "instance-create-alt",
         merchant_url,
         alt_instance_id,
-        cred.user42_payto,
         MHD_HTTP_NO_CONTENT),
+      TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-alt-account",
+        alt_instance_url,
+        cred.user42_payto,
+        NULL, NULL,
+        MHD_HTTP_OK),
       TALER_TESTING_cmd_admin_add_incoming (
         "create-reserve-1",
         CURRENCY_5_01,
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index a2327ad7..d81e6da9 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -37,6 +37,8 @@ libtalermerchanttesting_la_SOURCES = \
   testing_api_cmd_get_instance.c \
   testing_api_cmd_get_instances.c \
   testing_api_cmd_get_orders.c \
+  testing_api_cmd_get_otp_device.c \
+  testing_api_cmd_get_otp_devices.c \
   testing_api_cmd_get_product.c \
   testing_api_cmd_get_products.c \
   testing_api_cmd_get_reserve.c \
@@ -50,6 +52,7 @@ libtalermerchanttesting_la_SOURCES = \
   testing_api_cmd_delete_account.c \
   testing_api_cmd_delete_instance.c \
   testing_api_cmd_delete_order.c \
+  testing_api_cmd_delete_otp_device.c \
   testing_api_cmd_delete_product.c \
   testing_api_cmd_delete_template.c \
   testing_api_cmd_delete_webhook.c \
@@ -61,21 +64,23 @@ libtalermerchanttesting_la_SOURCES = \
   testing_api_cmd_instance_auth.c \
   testing_api_cmd_merchant_get_order.c \
   testing_api_cmd_merchant_get_reward.c \
+  testing_api_cmd_patch_instance.c \
+  testing_api_cmd_patch_otp_device.c \
+  testing_api_cmd_patch_product.c \
+  testing_api_cmd_patch_template.c \
+  testing_api_cmd_patch_webhook.c \
   testing_api_cmd_pay_order.c \
   testing_api_cmd_post_account.c \
   testing_api_cmd_post_instances.c \
   testing_api_cmd_post_orders_paid.c \
   testing_api_cmd_post_orders.c \
+  testing_api_cmd_post_otp_devices.c \
   testing_api_cmd_post_products.c \
   testing_api_cmd_post_reserves.c \
   testing_api_cmd_post_transfers.c \
   testing_api_cmd_post_templates.c \
   testing_api_cmd_post_using_templates.c \
   testing_api_cmd_post_webhooks.c \
-  testing_api_cmd_patch_instance.c \
-  testing_api_cmd_patch_product.c \
-  testing_api_cmd_patch_template.c \
-  testing_api_cmd_patch_webhook.c \
   testing_api_cmd_refund_order.c \
   testing_api_cmd_reward_authorize.c \
   testing_api_cmd_reward_pickup.c \
diff --git a/src/testing/test_kyc_api.c b/src/testing/test_kyc_api.c
index e26a1fd3..c8b683d8 100644
--- a/src/testing/test_kyc_api.c
+++ b/src/testing/test_kyc_api.c
@@ -395,8 +395,13 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-default-setup",
                                                merchant_url,
                                                "default",
-                                               merchant_payto,
                                                MHD_HTTP_NO_CONTENT),
+    TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-default-account",
+        merchant_url,
+        merchant_payto,
+        NULL, NULL,
+        MHD_HTTP_OK),
     TALER_TESTING_cmd_batch ("pay",
                              pay),
     TALER_TESTING_cmd_batch ("aml",
diff --git a/src/testing/test_merchant_api.c b/src/testing/test_merchant_api.c
index 32674332..a93ee179 100644
--- a/src/testing/test_merchant_api.c
+++ b/src/testing/test_merchant_api.c
@@ -120,15 +120,6 @@ static char *merchant_url_i1a;
  */
 #define MERCHANT_ACCOUNT_NAME "3"
 
-/**
- * Payto URIs to use for testing accounts on the merchant.
- */
-static const char *payto_uris[] = {
-  PAYTO_I1,
-  "payto://iban/CH9300762011623852957?receiver-name=Test"
-  /* Just for testing account inactivation. */
-};
-
 static const char *order_1_transfers[] = {
   "post-transfer-1",
   NULL
@@ -223,8 +214,13 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-default",
                                                merchant_url,
                                                "default",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
+    TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-default-account",
+        merchant_url,
+        PAYTO_I1,
+        NULL, NULL,
+        MHD_HTTP_OK),
     TALER_TESTING_cmd_merchant_kyc_get ("instance-create-kyc-0",
                                         merchant_url,
                                         NULL,
@@ -939,8 +935,13 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-i1a",
                                                merchant_url,
                                                "i1a",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
+    TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-i1a-account",
+        merchant_url_i1a,
+        PAYTO_I1,
+        NULL, NULL,
+        MHD_HTTP_OK),
     TALER_TESTING_cmd_merchant_get_product ("get-nx-product-i1a-1",
                                             merchant_url_i1a,
                                             "nx-product",
@@ -1413,7 +1414,6 @@ run (void *cls,
       "template-2",
       "another template",
       NULL,
-      TALER_MCA_NONE,
       GNUNET_JSON_PACK (
         GNUNET_JSON_pack_uint64 ("minimum_age", 0),
         GNUNET_JSON_pack_time_rel ("pay_duration",
@@ -1429,13 +1429,21 @@ run (void *cls,
                                              "template-nx",
                                              MHD_HTTP_NOT_FOUND,
                                              NULL),
+    TALER_TESTING_cmd_merchant_post_otp_devices (
+      "post-otp-device",
+      merchant_url,
+      "otp-dev",
+      "my OTP device",
+      "otp-key",
+      TALER_MCA_WITH_PRICE,
+      0,
+      MHD_HTTP_NO_CONTENT),
     TALER_TESTING_cmd_merchant_patch_template (
       "patch-templates-t3-nx",
       merchant_url,
       "template-3",
       "updated template",
-      NULL,
-      TALER_MCA_WITH_PRICE,
+      "otp-dev",
       GNUNET_JSON_PACK (
         GNUNET_JSON_pack_uint64 ("minimum_age", 0),
         GNUNET_JSON_pack_time_rel ("pay_duration",
@@ -1446,8 +1454,7 @@ run (void *cls,
       merchant_url,
       "template-amount",
       "a different template with an amount",
-      NULL, /* pos_key */
-      TALER_MCA_NONE,
+      NULL, 
       GNUNET_JSON_PACK (
         GNUNET_JSON_pack_uint64 ("minimum_age", 0),
         GNUNET_JSON_pack_time_rel ("pay_duration",
@@ -1458,6 +1465,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "using-templates-t1",
       "post-templates-t1",
+      NULL,
       merchant_url,
       "1",
       "summary-1",
@@ -1468,6 +1476,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "using-templates-t1-amount-missing",
       "post-templates-t1",
+      NULL,
       merchant_url,
       "2",
       "summary-1",
@@ -1478,6 +1487,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "using-templates-t1-summary-missing",
       "post-templates-t1",
+      NULL,
       merchant_url,
       "3",
       NULL,
@@ -1488,6 +1498,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "using-templates-t1-amount-conflict",
       "post-templates-t3-amount",
+      NULL,
       merchant_url,
       "4",
       "summary-1",
@@ -1498,6 +1509,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "using-templates-t1-amount-duplicate",
       "post-templates-t3-amount",
+      NULL,
       merchant_url,
       "4",
       "summary-1",
@@ -1524,6 +1536,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "post-templates-t1-deleted",
       "post-templates-t1",
+      NULL,
       merchant_url,
       "0",
       "summary-1",
@@ -1531,13 +1544,21 @@ run (void *cls,
       GNUNET_TIME_UNIT_ZERO_TS,
       GNUNET_TIME_UNIT_FOREVER_TS,
       MHD_HTTP_NOT_FOUND),
+    TALER_TESTING_cmd_merchant_post_otp_devices (
+      "post-otp-device",
+      merchant_url,
+      "otp-dev-2",
+      "my OTP device",
+      "secret",
+      TALER_MCA_WITH_PRICE,
+      0,
+      MHD_HTTP_NO_CONTENT),
     TALER_TESTING_cmd_merchant_post_templates2 (
       "post-templates-with-pos-key",
       merchant_url,
       "template-key",
       "a different template with POS KEY",
-      "secret", /* pos_key */
-      TALER_MCA_WITH_PRICE,
+      "otp-dev-2",
       GNUNET_JSON_PACK (
         GNUNET_JSON_pack_uint64 ("minimum_age", 0),
         GNUNET_JSON_pack_time_rel ("pay_duration",
@@ -1547,6 +1568,7 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_using_templates (
       "using-templates-pos-key",
       "post-templates-with-pos-key",
+      "post-otp-device",
       merchant_url,
       "1",
       "summary-1-pos",
@@ -1669,12 +1691,16 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-default-setup",
                                                merchant_url,
                                                "default",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
+    TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-default-account",
+        merchant_url,
+        PAYTO_I1,
+        NULL, NULL,
+        MHD_HTTP_OK),
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-i1",
                                                merchant_url,
                                                "i1",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
     TALER_TESTING_cmd_merchant_get_instances ("instances-get-i1",
                                               merchant_url,
@@ -1690,8 +1716,6 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_patch_instance ("instance-patch-i1",
                                                merchant_url,
                                                "i1",
-                                               2,
-                                               payto_uris,
                                                "bob-the-merchant",
                                                json_pack ("{s:s}",
                                                           "street",
@@ -1703,41 +1727,11 @@ run (void *cls,
                                                GNUNET_TIME_UNIT_MINUTES,
                                                GNUNET_TIME_UNIT_MINUTES,
                                                MHD_HTTP_NO_CONTENT),
-    TALER_TESTING_cmd_merchant_get_instance2 ("instances-get-i1-2",
-                                              merchant_url,
-                                              "i1",
-                                              MHD_HTTP_OK,
-                                              "instance-patch-i1",
-                                              payto_uris,
-                                              2,
-                                              NULL,
-                                              0),
-    TALER_TESTING_cmd_merchant_patch_instance (
-      "instance-patch-i1-inactivate-account",
-      merchant_url,
-      "i1",
-      1,
-      payto_uris,
-      "bob-the-merchant",
-      json_pack ("{s:s}",
-                 "street",
-                 "bobstreet"),
-      json_pack ("{s:s}",
-                 "street",
-                 "bobjuryst"),
-      true,
-      GNUNET_TIME_UNIT_MINUTES,
-      GNUNET_TIME_UNIT_MINUTES,
-      MHD_HTTP_NO_CONTENT),
-    TALER_TESTING_cmd_merchant_get_instance2 ("instances-get-i1-3",
-                                              merchant_url,
-                                              "i1",
-                                              MHD_HTTP_OK,
-                                              
"instance-patch-i1-inactivate-account",
-                                              payto_uris,
-                                              1,
-                                              &payto_uris[1],
-                                              1),
+    TALER_TESTING_cmd_merchant_get_instance ("instances-get-i1-2",
+                                             merchant_url,
+                                             "i1",
+                                             MHD_HTTP_OK,
+                                             "instance-patch-i1"),
     TALER_TESTING_cmd_merchant_get_instance ("instances-get-i2-nx",
                                              merchant_url,
                                              "i2",
@@ -1746,7 +1740,6 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances2 ("instance-create-ACL",
                                                 merchant_url,
                                                 "i-acl",
-                                                0, NULL,
                                                 "controlled instance",
                                                 json_pack ("{s:s}", "city",
                                                            "shopcity"),
@@ -1763,8 +1756,6 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_patch_instance ("instance-patch-ACL",
                                                merchant_url,
                                                "i-acl",
-                                               1,
-                                               payto_uris,
                                                "controlled instance",
                                                json_pack ("{s:s}",
                                                           "street",
@@ -1779,18 +1770,11 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2",
                                                merchant_url,
                                                "i2",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2-idem",
                                                merchant_url,
                                                "i2",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
-    TALER_TESTING_cmd_merchant_post_instances ("instance-create-i2-non-idem",
-                                               merchant_url,
-                                               "i2",
-                                               
"payto://other-method/?receiver-name=X",
-                                               MHD_HTTP_CONFLICT),
     TALER_TESTING_cmd_merchant_delete_instance ("instance-delete-i2",
                                                 merchant_url,
                                                 "i2",
@@ -1824,8 +1808,13 @@ run (void *cls,
       "instance-create-default-after-purge",
       merchant_url,
       "default",
-      PAYTO_I1,
       MHD_HTTP_NO_CONTENT),
+    TALER_TESTING_cmd_merchant_post_account (
+      "instance-create-default-account-after-purge",
+      merchant_url,
+      PAYTO_I1,
+      NULL, NULL,
+      MHD_HTTP_OK),
     TALER_TESTING_cmd_merchant_get_products ("get-products-empty",
                                              merchant_url,
                                              MHD_HTTP_OK,
diff --git 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1692810704
 
b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1692810704
deleted file mode 100644
index 7fd4a991..00000000
--- 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1692810704
+++ /dev/null
@@ -1 +0,0 @@
-��K���H"r^Y�g�n���2�^�Ћ�
\ No newline at end of file
diff --git 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1692810704
 
b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1692810704
deleted file mode 100644
index 609223fc..00000000
--- 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1692810704
+++ /dev/null
@@ -1,2 +0,0 @@
-��    ��6
-��M��������R�O&4��g�
\ No newline at end of file
diff --git 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1692810704
 
b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1692810704
deleted file mode 100644
index a03556d9..00000000
--- 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1692810704
+++ /dev/null
@@ -1 +0,0 @@
-0�b���m�qj��fӌ����YP�Z���
\ No newline at end of file
diff --git 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1692810704
 
b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1692810704
deleted file mode 100644
index d43290a6..00000000
--- 
a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1692810704
+++ /dev/null
@@ -1 +0,0 @@
-����y�\��U��B��6Қ�6Dꄄ��&
\ No newline at end of file
diff --git a/src/testing/test_merchant_api_twisted.c 
b/src/testing/test_merchant_api_twisted.c
index b55388ab..71a6485b 100644
--- a/src/testing/test_merchant_api_twisted.c
+++ b/src/testing/test_merchant_api_twisted.c
@@ -337,8 +337,13 @@ run (void *cls,
     TALER_TESTING_cmd_merchant_post_instances ("instance-create-default",
                                                twister_merchant_url,
                                                "default",
-                                               PAYTO_I1,
                                                MHD_HTTP_NO_CONTENT),
+    TALER_TESTING_cmd_merchant_post_account (
+        "instance-create-default-account",
+        twister_merchant_url,
+        PAYTO_I1,
+        NULL, NULL,
+        MHD_HTTP_OK),
     TALER_TESTING_cmd_batch ("pay",
                              pay),
     /* Malform the response from the exchange. */
diff --git a/src/testing/test_merchant_instance_auth.sh 
b/src/testing/test_merchant_instance_auth.sh
index 5cc4de92..8d6e347a 100755
--- a/src/testing/test_merchant_instance_auth.sh
+++ b/src/testing/test_merchant_instance_auth.sh
@@ -42,13 +42,26 @@ echo -n "Configuring 'default' instance ..." >&2
 
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"token","token":"secret-token:new_value"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"token","token":"secret-token:new_value"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
     exit_fail "Expected 204, instance created. got: $STATUS" >&2
 fi
+
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:new_value' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/43"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+
 echo " OK" >&2
 
 # Kill merchant
@@ -115,7 +128,7 @@ echo -n "Configuring 'second' instance ..." >&2
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer '$NEW_SECRET \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"token","token":"secret-token:second"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"second","name":"second","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"token","token":"secret-token:second"},"id":"second","name":"second","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
diff --git a/src/testing/test_merchant_instance_creation.sh 
b/src/testing/test_merchant_instance_creation.sh
index 8d81ab8b..c7eda54b 100755
--- a/src/testing/test_merchant_instance_creation.sh
+++ b/src/testing/test_merchant_instance_creation.sh
@@ -27,7 +27,7 @@ echo -n "Configuring a merchant instance before configuring 
the default instance
 
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"first","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"first","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
@@ -43,7 +43,7 @@ echo -n "Configuring default instance ..."
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "401" ]
@@ -58,7 +58,7 @@ echo -n "Configuring a second merchant instance ..."
 
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"second","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"second","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "401" ]
diff --git a/src/testing/test_merchant_instance_purge.sh 
b/src/testing/test_merchant_instance_purge.sh
index 0d8c0c2c..f3992495 100755
--- a/src/testing/test_merchant_instance_purge.sh
+++ b/src/testing/test_merchant_instance_purge.sh
@@ -27,7 +27,7 @@ echo -n "Configuring default instance ..." >&2
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
@@ -41,7 +41,7 @@ echo -n "Configuring merchant instance ..." >&2
 
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"test","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"test","name":"test","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
diff --git a/src/testing/test_merchant_instance_response.sh 
b/src/testing/test_merchant_instance_response.sh
index b2eee199..336c33cc 100755
--- a/src/testing/test_merchant_instance_response.sh
+++ b/src/testing/test_merchant_instance_response.sh
@@ -29,7 +29,7 @@ STATUS=$(curl -H "Content-Type: application/json" -X OPTIONS \
 
 if [ "$STATUS" != "204" ]
 then
-    exit_fail "Expected 204 when default instance doest not exist yet. got: 
$STATUS"
+    exit_fail "Expected 204 when default instance does not exist yet. got: 
$STATUS"
 fi
 
 STATUS=$(curl -H "Content-Type: application/json" -X GET \
@@ -45,7 +45,7 @@ fi
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost/43"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
+    -d 
'{"auth":{"method":"token","token":"secret-token:other_secret"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 3600000000},"default_pay_delay":{"d_us": 3600000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
diff --git a/src/testing/test_merchant_kyc.sh b/src/testing/test_merchant_kyc.sh
index 2b3bb188..663590b1 100755
--- a/src/testing/test_merchant_kyc.sh
+++ b/src/testing/test_merchant_kyc.sh
@@ -24,12 +24,12 @@ set -eu
 setup -c "test_template.conf" -m -u "exchange-account-1"
 LAST_RESPONSE=$(mktemp -p "${TMPDIR:-/tmp}" test_response.conf-XXXXXX)
 
-echo -n "Configuring a merchant instance before configuring the default 
instance ..."
+echo -n "Configuring a merchant default instance ..."
 
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost:8082/43"},{"payto_uri":"payto://x-taler-bank/localhost:8082/44"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
@@ -37,6 +37,30 @@ then
     exit_fail "Expected 204 ok, instance created. got: $STATUS"
 fi
 
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/43"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"payto://x-taler-bank/localhost:8082/44"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+
 echo " OK"
 
 echo -n "Check the instance exists ..."
diff --git a/src/testing/test_merchant_order_autocleanup.sh 
b/src/testing/test_merchant_order_autocleanup.sh
index 08655d18..60ee19e0 100755
--- a/src/testing/test_merchant_order_autocleanup.sh
+++ b/src/testing/test_merchant_order_autocleanup.sh
@@ -83,11 +83,10 @@ else
     FORTYTHREE=$(get_payto_uri fortythree x)
 fi
 
-# create with 2 address
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     "http://localhost:9966/management/instances"; \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
@@ -95,6 +94,17 @@ then
     exit_fail "Expected 204, instance created. got: $STATUS"
 fi
 
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"'"$FORTYTHREE"'"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected '200 OK' response. Got instead $STATUS"
+fi
+
 NOW=$(date +%s)
 IN_TEN_SECS=$(( NOW + 10 ))
 
diff --git a/src/testing/test_merchant_order_creation.sh 
b/src/testing/test_merchant_order_creation.sh
index 1461c005..f0355565 100755
--- a/src/testing/test_merchant_order_creation.sh
+++ b/src/testing/test_merchant_order_creation.sh
@@ -83,6 +83,20 @@ fi
 
 echo -n "Configuring merchant instance ..."
 
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/management/instances \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "204" ]
+then
+    exit_fail "Expected '204 No content' response. Got instead $STATUS"
+fi
+echo "Ok"
+
+echo -n "Configuring merchant account ..."
+
 if [ 1 = "$USE_FAKEBANK" ]
 then
     
FORTYTHREE="payto://x-taler-bank/localhost/fortythree?receiver-name=fortythree"
@@ -92,26 +106,51 @@ fi
 # create with 2 bank account addresses
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
-    http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"},{"payto_uri":"payto://iban/SANDBOXX/DE270744?receiver-name=Forty+Four"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"'"$FORTYTHREE"'"}' \
     -w "%{http_code}" -s -o /dev/null)
 
-if [ "$STATUS" != "204" ]
+if [ "$STATUS" != "200" ]
 then
-    exit_fail "Expected '204 No content' response. Got instead $STATUS"
+    exit_fail "Expected '200 OK' response. Got instead $STATUS"
 fi
-echo -n "."
-# remove one account address
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d 
'{"payto_uri":"payto://iban/SANDBOXX/DE270744?receiver-name=Forty+Four"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected '200 OK' response. Got instead $STATUS"
+fi
+
 
+echo "Ok"
+
+echo -n "Get accounts..."
+STATUS=$(curl http://localhost:9966/instances/default/private/accounts \
+    -w "%{http_code}" -s -o "$LAST_RESPONSE")
+PAY_URI=$(jq -r .accounts[1].payto_uri < "$LAST_RESPONSE")
+H_WIRE=$(jq -r .accounts[1].h_wire < "$LAST_RESPONSE")
+if [ "$PAY_URI" != "payto://iban/SANDBOXX/DE270744?receiver-name=Forty+Four" ]
+then
+    cat "$LAST_RESPONSE" >&2
+    exit_fail "Expected second payto URI. Got $PAY_URI"
+fi
+echo "OK"
+
+# remove one account address
+echo -n "Deleting one account ..."
 STATUS=$(curl -H "Content-Type: application/json" -X PATCH \
     -H 'Authorization: Bearer secret-token:super_secret' \
-    http://localhost:9966/instances/default/private/ \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000000},"default_pay_delay":{"d_us": 60000000000}}' \
+    "http://localhost:9966/instances/default/private/accounts/${H_WIRE}"; \
+    -X DELETE \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
-    exit_fail "Expected '204 No content' response. Got instead: $STATUS"
+    exit_fail "Expected '204 No content' for deletion of ${H_WIRE}. Got 
instead: $STATUS"
 fi
 echo "OK"
 
diff --git a/src/testing/test_merchant_product_creation.sh 
b/src/testing/test_merchant_product_creation.sh
index dd3a60b5..6da5efa2 100755
--- a/src/testing/test_merchant_product_creation.sh
+++ b/src/testing/test_merchant_product_creation.sh
@@ -55,13 +55,25 @@ echo -n "Configuring merchant instance ..."
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     "http://localhost:9966/management/instances"; \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$FORTYTHREE"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
     exit_fail "Expected 204 No content, instance created. Got $STATUS instead"
 fi
+
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"'"$FORTYTHREE"'"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+
 echo "OK"
 
 RANDOM_IMG='data:image/png;base64,abcdefg'
diff --git a/src/testing/test_merchant_reserve_creation.sh 
b/src/testing/test_merchant_reserve_creation.sh
index e7ebf615..36e71e99 100755
--- a/src/testing/test_merchant_reserve_creation.sh
+++ b/src/testing/test_merchant_reserve_creation.sh
@@ -47,17 +47,29 @@ echo -n "Configuring merchant instance ..."
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"payto://x-taler-bank/localhost:18082/fortythree"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
     exit_fail "Expected 204, instance created. Got instead: $STATUS"
 fi
+echo "Ok"
+echo -n "Configuring merchant account ..."
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"payto://x-taler-bank/localhost:18082/fortythree"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+
 echo "OK"
 
 echo -n "Creating reserve ..."
-#bash
 
 STATUS=$(curl 'http://localhost:9966/instances/default/private/reserves' \
     -d 
'{"initial_balance":"TESTKUDOS:2","exchange_url":"http://localhost:8081/","wire_method":";'"$WIRE_METHOD"'"}'
 \
diff --git a/src/testing/test_merchant_transfer_tracking.sh 
b/src/testing/test_merchant_transfer_tracking.sh
index cba85d12..3a2a18a9 100755
--- a/src/testing/test_merchant_transfer_tracking.sh
+++ b/src/testing/test_merchant_transfer_tracking.sh
@@ -108,13 +108,35 @@ fi
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$TOR_PAYTO"'"},{"payto_uri":"'"$GNUNET_PAYTO"'"}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
     exit_fail "Expected 204, instance created. got: $STATUS"
 fi
+
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"'"$TOR_PAYTO"'"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d '{"payto_uri":"'"$GNUNET_PAYTO"'"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+
 echo "OK"
 
 echo -n "Configuring merchant test instance ..."
@@ -123,13 +145,33 @@ echo -n "Configuring merchant test instance ..."
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$SURVEY_PAYTO"'"},{"payto_uri":"'"$TUTORIAL_PAYTO"'"}],"id":"test","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"test","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
     exit_fail "Expected 204, instance created. got: $STATUS"
 fi
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/test/private/accounts \
+    -d '{"payto_uri":"'"$SURVEY_PAYTO"'"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/test/private/accounts \
+    -d '{"payto_uri":"'"$TUTORIAL_PAYTO"'"}' \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
 echo "OK"
 
 # CREATE ORDER AND SELL IT
diff --git a/src/testing/test_merchant_wirewatch.sh 
b/src/testing/test_merchant_wirewatch.sh
index b8a37f2f..b4fdc66b 100755
--- a/src/testing/test_merchant_wirewatch.sh
+++ b/src/testing/test_merchant_wirewatch.sh
@@ -197,13 +197,26 @@ fi
 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     -H 'Authorization: Bearer secret-token:super_secret' \
     http://localhost:9966/management/instances \
-    -d 
'{"auth":{"method":"external"},"accounts":[{"payto_uri":"'"$GNUNET_PAYTO"'","credit_facade_url":"'"${FACADE_URL}"'","credit_facade_credentials":{"type":"basic","username":"'"$FACADE_USERNAME"'","password":"'"$FACADE_PASSWORD"'"}}],"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
+    -d 
'{"auth":{"method":"external"},"id":"default","name":"default","user_type":"business","address":{},"jurisdiction":{},"use_stefan":true,"default_wire_transfer_delay":{"d_us"
 : 50000000},"default_pay_delay":{"d_us": 60000000}}' \
     -w "%{http_code}" -s -o /dev/null)
 
 if [ "$STATUS" != "204" ]
 then
     exit_fail "Expected 204 no content. Got: $STATUS"
 fi
+echo "OK"
+
+echo -n "Configuring bank account..."
+STATUS=$(curl -H "Content-Type: application/json" -X POST \
+    -H 'Authorization: Bearer secret-token:super_secret' \
+    http://localhost:9966/instances/default/private/accounts \
+    -d 
'{"payto_uri":"'"$GNUNET_PAYTO"'","credit_facade_url":"'"${FACADE_URL}"'","credit_facade_credentials":{"type":"basic","username":"'"$FACADE_USERNAME"'","password":"'"$FACADE_PASSWORD"'"}}'
 \
+    -w "%{http_code}" -s -o /dev/null)
+
+if [ "$STATUS" != "200" ]
+then
+    exit_fail "Expected 200 OK. Got: $STATUS"
+fi
 
 echo "OK"
 
diff --git a/src/testing/testing_api_cmd_delete_account.c 
b/src/testing/testing_api_cmd_delete_account.c
index 1490dc31..681faa3c 100644
--- a/src/testing/testing_api_cmd_delete_account.c
+++ b/src/testing/testing_api_cmd_delete_account.c
@@ -29,7 +29,7 @@
 
 
 /**
- * State of a "DELETE /accounts/$ID" CMD.
+ * State of a "DELETE /accounts/$H_WIRE" CMD.
  */
 struct DeleteAccountState
 {
@@ -52,12 +52,7 @@ struct DeleteAccountState
   /**
    * ID of the command to get account details from.
    */
-  const char *get_instance_ref;
-
-  /**
-   * Payto URI to extract h_wire from.
-   */
-  const char *payto_uri;
+  const char *create_account_ref;
 
   /**
    * Expected HTTP response code.
@@ -128,7 +123,7 @@ delete_account_run (void *cls,
 
   das->is = is;
   ref = TALER_TESTING_interpreter_lookup_command (is,
-                                                  das->get_instance_ref);
+                                                  das->create_account_ref);
   if (NULL == ref)
   {
     GNUNET_break (0);
@@ -141,45 +136,22 @@ delete_account_run (void *cls,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Command %s lacked merchant base URL\n",
-                das->get_instance_ref);
+                das->create_account_ref);
     GNUNET_break (0);
     TALER_TESTING_FAIL (is);
     return;
   }
-  for (unsigned int i = 0; i<UINT_MAX; i++)
+  if (GNUNET_OK !=
+      TALER_TESTING_get_trait_h_wires (ref,
+                                       0,
+                                       &h_wire))
   {
-    const char *payto_uri;
-
-    if (GNUNET_OK !=
-        TALER_TESTING_get_trait_payto_uris (ref,
-                                            i,
-                                            &payto_uri))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Command %s did not return payto URI %s\n",
-                  das->get_instance_ref,
-                  das->payto_uri);
-      GNUNET_break (0);
-      TALER_TESTING_FAIL (is);
-      return;
-    }
-    if (0 != strcmp (payto_uri,
-                     das->payto_uri))
-      continue; /* different account */
-    if (GNUNET_OK !=
-        TALER_TESTING_get_trait_h_wires (ref,
-                                         i,
-                                         &h_wire))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Command %s had payto URI %s but lacked h_wire!?\n",
-                  das->get_instance_ref,
-                  das->payto_uri);
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Command %s did not return H_WIRE\n",
+                das->create_account_ref);
       GNUNET_break (0);
       TALER_TESTING_FAIL (is);
       return;
-    }
-    break;
   }
   GNUNET_assert (NULL != h_wire);
   das->adh = TALER_MERCHANT_account_delete (
@@ -217,15 +189,13 @@ delete_account_cleanup (void *cls,
 
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_merchant_delete_account (const char *label,
-                                           const char *get_instance_ref,
-                                           const char *payto_uri,
+                                           const char *create_account_ref,
                                            unsigned int http_status)
 {
   struct DeleteAccountState *das;
 
   das = GNUNET_new (struct DeleteAccountState);
-  das->get_instance_ref = get_instance_ref;
-  das->payto_uri = payto_uri;
+  das->create_account_ref = create_account_ref;
   das->http_status = http_status;
   {
     struct TALER_TESTING_Command cmd = {
diff --git a/src/testing/testing_api_cmd_delete_otp_device.c 
b/src/testing/testing_api_cmd_delete_otp_device.c
new file mode 100644
index 00000000..3d15c645
--- /dev/null
+++ b/src/testing/testing_api_cmd_delete_otp_device.c
@@ -0,0 +1,181 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing_api_cmd_delete_otp_device.c
+ * @brief command to test DELETE /otp-devices/$ID
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+
+
+/**
+ * State of a "DELETE /otp-devices/$ID" CMD.
+ */
+struct DeleteOtpDeviceState
+{
+
+  /**
+   * Handle for a "DELETE otp_device" request.
+   */
+  struct TALER_MERCHANT_OtpDeviceDeleteHandle *tdh;
+
+  /**
+   * The interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Base URL of the merchant serving the request.
+   */
+  const char *merchant_url;
+
+  /**
+   * ID of the otp_device to run DELETE for.
+   */
+  const char *otp_device_id;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int http_status;
+
+};
+
+
+/**
+ * Callback for a /delete/otp-devices/$ID operation.
+ *
+ * @param cls closure for this function
+ * @param hr response being processed
+ */
+static void
+delete_otp_device_cb (void *cls,
+                    const struct TALER_MERCHANT_HttpResponse *hr)
+{
+  struct DeleteOtpDeviceState *dis = cls;
+
+  dis->tdh = NULL;
+  if (dis->http_status != hr->http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                hr->http_status,
+                (int) hr->ec,
+                TALER_TESTING_interpreter_get_current_label (dis->is));
+    TALER_TESTING_interpreter_fail (dis->is);
+    return;
+  }
+  switch (hr->http_status)
+  {
+  case MHD_HTTP_NO_CONTENT:
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    break;
+  case MHD_HTTP_CONFLICT:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unhandled HTTP status %u for DELETE otp_device.\n",
+                hr->http_status);
+  }
+  TALER_TESTING_interpreter_next (dis->is);
+}
+
+
+/**
+ * Run the "DELETE otp_device" CMD.
+ *
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+delete_otp_device_run (void *cls,
+                       const struct TALER_TESTING_Command *cmd,
+                       struct TALER_TESTING_Interpreter *is)
+{
+  struct DeleteOtpDeviceState *dis = cls;
+
+  dis->is = is;
+  dis->tdh = TALER_MERCHANT_otp_device_delete (
+    TALER_TESTING_interpreter_get_context (is),
+    dis->merchant_url,
+    dis->otp_device_id,
+    &delete_otp_device_cb,
+    dis);
+  GNUNET_assert (NULL != dis->tdh);
+}
+
+
+/**
+ * Free the state of a "DELETE otp_device" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+delete_otp_device_cleanup (void *cls,
+                           const struct TALER_TESTING_Command *cmd)
+{
+  struct DeleteOtpDeviceState *dis = cls;
+
+  if (NULL != dis->tdh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "DELETE /otp-devices/$ID operation did not complete\n");
+    TALER_MERCHANT_otp_device_delete_cancel (dis->tdh);
+  }
+  GNUNET_free (dis);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_delete_otp_device (const char *label,
+                                              const char *merchant_url,
+                                              const char *otp_device_id,
+                                              unsigned int http_status)
+{
+  struct DeleteOtpDeviceState *dis;
+
+  dis = GNUNET_new (struct DeleteOtpDeviceState);
+  dis->merchant_url = merchant_url;
+  dis->otp_device_id = otp_device_id;
+  dis->http_status = http_status;
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = dis,
+      .label = label,
+      .run = &delete_otp_device_run,
+      .cleanup = &delete_otp_device_cleanup
+    };
+
+    return cmd;
+  }
+}
+
+
+/* end of testing_api_cmd_delete_otp_device.c */
diff --git a/src/testing/testing_api_cmd_get_instance.c 
b/src/testing/testing_api_cmd_get_instance.c
index e706f85b..c3199a7e 100644
--- a/src/testing/testing_api_cmd_get_instance.c
+++ b/src/testing/testing_api_cmd_get_instance.c
@@ -28,39 +28,6 @@
 #include "taler_merchant_testing_lib.h"
 
 
-/**
- * Details about a merchant's bank account.
- */
-struct MERCHANT_Account
-{
-  /**
-   * salt used to compute h_wire
-   */
-  struct TALER_WireSaltP salt;
-
-  /**
-   * payto:// URI of the account.
-   */
-  char *payto_uri;
-
-  /**
-   * Credit facade URL of the account.
-   */
-  char *credit_facade_url;
-
-  /**
-   * Hash of @e payto_uri and @e salt.
-   */
-  struct TALER_MerchantWireHashP h_wire;
-
-  /**
-   * true if the account is active,
-   * false if it is historic.
-   */
-  bool active;
-};
-
-
 /**
  * State of a "GET instance" CMD.
  */
@@ -92,42 +59,6 @@ struct GetInstanceState
    */
   const char *instance_reference;
 
-  /**
-   * Whether we should check the instance's accounts or not.
-   */
-  bool cmp_accounts;
-
-  /**
-   * The accounts of the merchant we expect to be active.
-   */
-  const char **active_accounts;
-
-  /**
-   * The length of @e active_accounts.
-   */
-  unsigned int active_accounts_length;
-
-  /**
-   * The accounts of the merchant we expect to be inactive.
-   */
-  const char **inactive_accounts;
-
-  /**
-   * Length of the @e accounts array.
-   */
-  unsigned int accounts_length;
-
-  /**
-   * Array of @e accounts_length bank accounts of the merchant instance as
-   * returned by the request.
-   */
-  struct MERCHANT_Account *accounts;
-
-  /**
-   * The length of @e inactive_accounts.
-   */
-  unsigned int inactive_accounts_length;
-
   /**
    * Expected HTTP response code.
    */
@@ -171,21 +102,6 @@ get_instance_cb (void *cls,
       const struct TALER_MERCHANT_InstanceDetails *details =
         &igr->details.ok.details;
 
-      gis->accounts_length = igr->details.ok.accounts_length;
-      gis->accounts = GNUNET_new_array (gis->accounts_length,
-                                        struct MERCHANT_Account);
-      for (unsigned int i = 0; i<gis->accounts_length; i++)
-      {
-        const struct TALER_MERCHANT_Account *src = 
&igr->details.ok.accounts[i];
-        struct MERCHANT_Account *dst = &gis->accounts[i];
-
-        dst->salt = src->salt;
-        dst->payto_uri = GNUNET_strdup (src->payto_uri);
-        if (NULL != src->credit_facade_url)
-          dst->credit_facade_url = GNUNET_strdup (src->credit_facade_url);
-        dst->h_wire = src->h_wire;
-        dst->active = src->active;
-      }
       {
         const char *name;
 
@@ -283,65 +199,6 @@ get_instance_cb (void *cls,
           return;
         }
       }
-      /* We aren't guaranteed an order for the accounts, so we just have to 
check
-         that we can match each account returned with exactly one account
-         expected. */
-      if (gis->cmp_accounts)
-      {
-        unsigned int have_al = igr->details.ok.accounts_length;
-        unsigned int expected_accounts_length =
-          gis->active_accounts_length + gis->inactive_accounts_length;
-        unsigned int matches[GNUNET_NZL (have_al)];
-
-        if (have_al != expected_accounts_length)
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                      "Accounts length does not match\n");
-          TALER_TESTING_interpreter_fail (gis->is);
-          return;
-        }
-
-        memset (matches,
-                0,
-                sizeof (matches));
-
-        /* Compare the accounts */
-        for (unsigned int i = 0; i < have_al; ++i)
-        {
-          const struct TALER_MERCHANT_Account *account
-            = &igr->details.ok.accounts[i];
-          for (unsigned int j = 0; j < gis->active_accounts_length; ++j)
-          {
-            if ((0 == strcasecmp (account->payto_uri,
-                                  gis->active_accounts[j])) &&
-                account->active)
-            {
-              matches[i] += 1;
-            }
-          }
-          for (unsigned int j = 0; j < gis->inactive_accounts_length; ++j)
-          {
-            if ((0 == strcasecmp (account->payto_uri,
-                                  gis->inactive_accounts[j])) &&
-                (! account->active))
-            {
-              matches[i] += 1;
-            }
-          }
-        }
-
-        // Each account should have exactly one match.
-        for (unsigned int i = 0; i < have_al; ++i)
-        {
-          if (1 != matches[i])
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "Instance account does not match\n");
-            TALER_TESTING_interpreter_fail (gis->is);
-            return;
-          }
-        }
-      }
     }
     break;
   case MHD_HTTP_UNAUTHORIZED:
@@ -402,14 +259,6 @@ get_instance_cleanup (void *cls,
                 "GET /instances/$ID operation did not complete\n");
     TALER_MERCHANT_instance_get_cancel (gis->igh);
   }
-  for (unsigned int i = 0; i<gis->accounts_length; i++)
-  {
-    struct MERCHANT_Account *acc = &gis->accounts[i];
-
-    GNUNET_free (acc->payto_uri);
-    GNUNET_free (acc->credit_facade_url);
-  }
-  GNUNET_free (gis->accounts);
   GNUNET_free (gis);
 }
 
@@ -431,37 +280,15 @@ get_instance_traits (void *cls,
                      unsigned int index)
 {
   struct GetInstanceState *pps = cls;
-
-  if (index < pps->accounts_length)
-  {
-    struct TALER_TESTING_Trait traits[] = {
-      TALER_TESTING_make_trait_merchant_base_url (pps->merchant_url),
-      TALER_TESTING_make_trait_h_wires (
-        index,
-        &pps->accounts[index].h_wire),
-      TALER_TESTING_make_trait_payto_uris (
-        index,
-        pps->accounts[index].payto_uri),
-      TALER_TESTING_trait_end (),
-    };
-
-    return TALER_TESTING_get_trait (traits,
-                                    ret,
-                                    trait,
-                                    index);
-  }
-  else
-  {
-    struct TALER_TESTING_Trait traits[] = {
-      TALER_TESTING_make_trait_merchant_base_url (pps->merchant_url),
-      TALER_TESTING_trait_end (),
-    };
-
-    return TALER_TESTING_get_trait (traits,
-                                    ret,
-                                    trait,
-                                    index);
-  }
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_merchant_base_url (pps->merchant_url),
+    TALER_TESTING_trait_end (),
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
 }
 
 
@@ -479,44 +306,6 @@ TALER_TESTING_cmd_merchant_get_instance (const char *label,
   gis->instance_id = instance_id;
   gis->http_status = http_status;
   gis->instance_reference = instance_reference;
-  gis->cmp_accounts = false;
-  {
-    struct TALER_TESTING_Command cmd = {
-      .cls = gis,
-      .label = label,
-      .run = &get_instance_run,
-      .cleanup = &get_instance_cleanup,
-      .traits = &get_instance_traits
-    };
-
-    return cmd;
-  }
-}
-
-
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_get_instance2 (const char *label,
-                                          const char *merchant_url,
-                                          const char *instance_id,
-                                          unsigned int http_status,
-                                          const char *instance_reference,
-                                          const char *active_accounts[],
-                                          unsigned int active_accounts_length,
-                                          const char *inactive_accounts[],
-                                          unsigned int 
inactive_accounts_length)
-{
-  struct GetInstanceState *gis;
-
-  gis = GNUNET_new (struct GetInstanceState);
-  gis->merchant_url = merchant_url;
-  gis->instance_id = instance_id;
-  gis->http_status = http_status;
-  gis->instance_reference = instance_reference;
-  gis->cmp_accounts = true;
-  gis->active_accounts = active_accounts;
-  gis->active_accounts_length = active_accounts_length;
-  gis->inactive_accounts = inactive_accounts;
-  gis->inactive_accounts_length = inactive_accounts_length;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = gis,
diff --git a/src/testing/testing_api_cmd_get_otp_device.c 
b/src/testing/testing_api_cmd_get_otp_device.c
new file mode 100644
index 00000000..272039af
--- /dev/null
+++ b/src/testing/testing_api_cmd_get_otp_device.c
@@ -0,0 +1,227 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing_api_cmd_get_otp_device.c
+ * @brief command to test GET /otp-devices/$ID
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+
+
+/**
+ * State of a "GET OTP device" CMD.
+ */
+struct GetOtpDeviceState
+{
+
+  /**
+   * Handle for a "GET /otp-device/$ID" request.
+   */
+  struct TALER_MERCHANT_OtpDeviceGetHandle *igh;
+
+  /**
+   * The interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Base URL of the merchant serving the request.
+   */
+  const char *merchant_url;
+
+  /**
+   * ID of the otp_device to run GET for.
+   */
+  const char *otp_device_id;
+
+  /**
+   * Reference for a POST or PATCH /otp-devices CMD (optional).
+   */
+  const char *otp_device_reference;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int http_status;
+
+};
+
+
+/**
+ * Callback for a GET /otp-devices/$ID operation.
+ *
+ * @param cls closure for this function
+ * @param tgr HTTP response details
+ */
+static void
+get_otp_device_cb (void *cls,
+                   const struct TALER_MERCHANT_OtpDeviceGetResponse *tgr)
+{
+  struct GetOtpDeviceState *gis = cls;
+  const struct TALER_TESTING_Command *otp_device_cmd;
+
+  gis->igh = NULL;
+  if (gis->http_status != tgr->hr.http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                tgr->hr.http_status,
+                (int) tgr->hr.ec,
+                TALER_TESTING_interpreter_get_current_label (gis->is));
+    TALER_TESTING_interpreter_fail (gis->is);
+    return;
+  }
+  switch (tgr->hr.http_status)
+  {
+  case MHD_HTTP_OK:
+    {
+      const char *expected_description;
+
+      otp_device_cmd = TALER_TESTING_interpreter_lookup_command (
+        gis->is,
+        gis->otp_device_reference);
+      if (GNUNET_OK !=
+          TALER_TESTING_get_trait_otp_device_description (otp_device_cmd,
+                                                        &expected_description))
+        TALER_TESTING_interpreter_fail (gis->is);
+      if (0 != strcmp (tgr->details.ok.otp_device_description,
+                       expected_description))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "OtpDevice description does not match\n");
+        TALER_TESTING_interpreter_fail (gis->is);
+        return;
+      }
+    }
+    {
+      const char *expected_otp_key;
+
+      if (GNUNET_OK !=
+          TALER_TESTING_get_trait_otp_key (otp_device_cmd,
+                                           &expected_otp_key))
+        TALER_TESTING_interpreter_fail (gis->is);
+      if ( ( (NULL == tgr->details.ok.otp_key) && (NULL != expected_otp_key)) 
||
+           ( (NULL != tgr->details.ok.otp_key) && (NULL == expected_otp_key)) 
||
+           ( (NULL != tgr->details.ok.otp_key) &&
+             (0 != strcmp (tgr->details.ok.otp_key,
+                           expected_otp_key)) ) )
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "OtpDevice otp_key `%s' does not match `%s'\n",
+                    tgr->details.ok.otp_key,
+                    expected_otp_key);
+        TALER_TESTING_interpreter_fail (gis->is);
+        return;
+      }
+    }
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unhandled HTTP status.\n");
+  }
+  TALER_TESTING_interpreter_next (gis->is);
+}
+
+
+/**
+ * Run the "GET /otp-device/$ID" CMD.
+ *
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+get_otp_device_run (void *cls,
+                    const struct TALER_TESTING_Command *cmd,
+                    struct TALER_TESTING_Interpreter *is)
+{
+  struct GetOtpDeviceState *gis = cls;
+
+  gis->is = is;
+  gis->igh = TALER_MERCHANT_otp_device_get (
+    TALER_TESTING_interpreter_get_context (is),
+    gis->merchant_url,
+    gis->otp_device_id,
+    &get_otp_device_cb,
+    gis);
+  GNUNET_assert (NULL != gis->igh);
+}
+
+
+/**
+ * Free the state of a "GET /otp-device/$ID" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+get_otp_device_cleanup (void *cls,
+                        const struct TALER_TESTING_Command *cmd)
+{
+  struct GetOtpDeviceState *gis = cls;
+
+  if (NULL != gis->igh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "GET /otp-devices/$ID operation did not complete\n");
+    TALER_MERCHANT_otp_device_get_cancel (gis->igh);
+  }
+  GNUNET_free (gis);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_get_otp_device (
+  const char *label,
+  const char *merchant_url,
+  const char *otp_device_id,
+  unsigned int http_status,
+  const char *otp_device_reference)
+{
+  struct GetOtpDeviceState *gis;
+
+  gis = GNUNET_new (struct GetOtpDeviceState);
+  gis->merchant_url = merchant_url;
+  gis->otp_device_id = otp_device_id;
+  gis->http_status = http_status;
+  gis->otp_device_reference = otp_device_reference;
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = gis,
+      .label = label,
+      .run = &get_otp_device_run,
+      .cleanup = &get_otp_device_cleanup
+    };
+
+    return cmd;
+  }
+}
+
+
+/* end of testing_api_cmd_get_otp_device.c */
diff --git a/src/testing/testing_api_cmd_get_otp_devices.c 
b/src/testing/testing_api_cmd_get_otp_devices.c
new file mode 100644
index 00000000..b4f370c5
--- /dev/null
+++ b/src/testing/testing_api_cmd_get_otp_devices.c
@@ -0,0 +1,238 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2022 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 3, or
+  (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not, see
+  <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file testing_api_cmd_get_otp_devices.c
+ * @brief command to test GET /otp-devices
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <taler/taler_exchange_service.h>
+#include <taler/taler_testing_lib.h>
+#include "taler_merchant_service.h"
+#include "taler_merchant_testing_lib.h"
+
+
+/**
+ * State of a "GET /otp-devices" CMD.
+ */
+struct GetOtpDevicesState
+{
+
+  /**
+   * Handle for a "GET /otp-devices" request.
+   */
+  struct TALER_MERCHANT_OtpDevicesGetHandle *igh;
+
+  /**
+   * The interpreter state.
+   */
+  struct TALER_TESTING_Interpreter *is;
+
+  /**
+   * Base URL of the merchant serving the request.
+   */
+  const char *merchant_url;
+
+  /**
+   * Expected HTTP response code.
+   */
+  unsigned int http_status;
+
+  /**
+   * The list of otp_device references.
+   */
+  const char **otp_devices;
+
+  /**
+   * Length of @e otp_devices.
+   */
+  unsigned int otp_devices_length;
+
+};
+
+
+/**
+ * Callback for a GET /otp-devices operation.
+ *
+ * @param cls closure for this function
+ * @param tgr response details
+ */
+static void
+get_otp_devices_cb (void *cls,
+                  const struct TALER_MERCHANT_OtpDevicesGetResponse *tgr)
+{
+  struct GetOtpDevicesState *gis = cls;
+
+  gis->igh = NULL;
+  if (gis->http_status != tgr->hr.http_status)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u (%d) to command %s\n",
+                tgr->hr.http_status,
+                (int) tgr->hr.ec,
+                TALER_TESTING_interpreter_get_current_label (gis->is));
+    TALER_TESTING_interpreter_fail (gis->is);
+    return;
+  }
+  switch (tgr->hr.http_status)
+  {
+  case MHD_HTTP_OK:
+    if (tgr->details.ok.otp_devices_length != gis->otp_devices_length)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Length of otp_devices found does not match\n");
+      TALER_TESTING_interpreter_fail (gis->is);
+      return;
+    }
+    for (unsigned int i = 0; i < gis->otp_devices_length; ++i)
+    {
+      const struct TALER_TESTING_Command *otp_device_cmd;
+
+      otp_device_cmd = TALER_TESTING_interpreter_lookup_command (
+        gis->is,
+        gis->otp_devices[i]);
+
+      {
+        const char *otp_device_id;
+
+        if (GNUNET_OK !=
+            TALER_TESTING_get_trait_otp_id (otp_device_cmd,
+                                            &otp_device_id))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "Could not fetch otp_device id\n");
+          TALER_TESTING_interpreter_fail (gis->is);
+          return;
+        }
+        if (0 != strcmp (tgr->details.ok.otp_devices[i].otp_device_id,
+                         otp_device_id))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      "OtpDevice id does not match\n");
+          TALER_TESTING_interpreter_fail (gis->is);
+          return;
+        }
+      }
+    }
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* instance does not exist */
+    break;
+  default:
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Unhandled HTTP status %u (%d).\n",
+                tgr->hr.http_status,
+                tgr->hr.ec);
+    break;
+  }
+  TALER_TESTING_interpreter_next (gis->is);
+}
+
+
+/**
+ * Run the "GET /otp-devices" CMD.
+ *
+ *
+ * @param cls closure.
+ * @param cmd command being run now.
+ * @param is interpreter state.
+ */
+static void
+get_otp_devices_run (void *cls,
+                     const struct TALER_TESTING_Command *cmd,
+                     struct TALER_TESTING_Interpreter *is)
+{
+  struct GetOtpDevicesState *gis = cls;
+
+  gis->is = is;
+  gis->igh = TALER_MERCHANT_otp_devices_get (
+    TALER_TESTING_interpreter_get_context (is),
+    gis->merchant_url,
+    &get_otp_devices_cb,
+    gis);
+  GNUNET_assert (NULL != gis->igh);
+}
+
+
+/**
+ * Free the state of a "GET otp_device" CMD, and possibly
+ * cancel a pending operation thereof.
+ *
+ * @param cls closure.
+ * @param cmd command being run.
+ */
+static void
+get_otp_devices_cleanup (void *cls,
+                         const struct TALER_TESTING_Command *cmd)
+{
+  struct GetOtpDevicesState *gis = cls;
+
+  if (NULL != gis->igh)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "GET /otp-devices operation did not complete\n");
+    TALER_MERCHANT_otp_devices_get_cancel (gis->igh);
+  }
+  GNUNET_array_grow (gis->otp_devices,
+                     gis->otp_devices_length,
+                     0);
+  GNUNET_free (gis);
+}
+
+
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_merchant_get_otp_devices (const char *label,
+                                            const char *merchant_url,
+                                            unsigned int http_status,
+                                            ...)
+{
+  struct GetOtpDevicesState *gis;
+
+  gis = GNUNET_new (struct GetOtpDevicesState);
+  gis->merchant_url = merchant_url;
+  gis->http_status = http_status;
+  {
+    const char *clabel;
+    va_list ap;
+
+    va_start (ap, http_status);
+    while (NULL != (clabel = va_arg (ap, const char *)))
+    {
+      GNUNET_array_append (gis->otp_devices,
+                           gis->otp_devices_length,
+                           clabel);
+    }
+    va_end (ap);
+  }
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = gis,
+      .label = label,
+      .run = &get_otp_devices_run,
+      .cleanup = &get_otp_devices_cleanup
+    };
+
+    return cmd;
+  }
+}
+
+
+/* end of testing_api_cmd_get_otp_devices.c */
diff --git a/src/testing/testing_api_cmd_get_template.c 
b/src/testing/testing_api_cmd_get_template.c
index 92888e6f..377ffe44 100644
--- a/src/testing/testing_api_cmd_get_template.c
+++ b/src/testing/testing_api_cmd_get_template.c
@@ -114,22 +114,22 @@ get_template_cb (void *cls,
       }
     }
     {
-      const char *expected_pos_key;
+      const char *expected_otp_id;
 
       if (GNUNET_OK !=
-          TALER_TESTING_get_trait_template_pos_key (template_cmd,
-                                                    &expected_pos_key))
+          TALER_TESTING_get_trait_otp_id (template_cmd,
+                                          &expected_otp_id))
         TALER_TESTING_interpreter_fail (gis->is);
-      if ( ( (NULL == tgr->details.ok.pos_key) && (NULL != expected_pos_key)) 
||
-           ( (NULL != tgr->details.ok.pos_key) && (NULL == expected_pos_key)) 
||
-           ( (NULL != tgr->details.ok.pos_key) &&
-             (0 != strcmp (tgr->details.ok.pos_key,
-                           expected_pos_key)) ) )
+      if ( ( (NULL == tgr->details.ok.otp_id) && (NULL != expected_otp_id)) ||
+           ( (NULL != tgr->details.ok.otp_id) && (NULL == expected_otp_id)) ||
+           ( (NULL != tgr->details.ok.otp_id) &&
+             (0 != strcmp (tgr->details.ok.otp_id,
+                           expected_otp_id)) ) )
       {
         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "Template pos_key `%s' does not match `%s'\n",
-                    tgr->details.ok.pos_key,
-                    expected_pos_key);
+                    tgr->details.ok.otp_id,
+                    expected_otp_id);
         TALER_TESTING_interpreter_fail (gis->is);
         return;
       }
diff --git a/src/testing/testing_api_cmd_patch_instance.c 
b/src/testing/testing_api_cmd_patch_instance.c
index 59dcac07..b3a2865c 100644
--- a/src/testing/testing_api_cmd_patch_instance.c
+++ b/src/testing/testing_api_cmd_patch_instance.c
@@ -54,16 +54,6 @@ struct PatchInstanceState
    */
   const char *instance_id;
 
-  /**
-   * Length of the @payto_uris array
-   */
-  unsigned int payto_uris_length;
-
-  /**
-   * Array of payto URIs.
-   */
-  const char **payto_uris;
-
   /**
    * Name of the instance.
    */
@@ -161,21 +151,12 @@ patch_instance_run (void *cls,
                     struct TALER_TESTING_Interpreter *is)
 {
   struct PatchInstanceState *pis = cls;
-  struct TALER_MERCHANT_AccountConfig accounts[GNUNET_NZL (
-                                                 pis->payto_uris_length)];
-
-  memset (accounts,
-          0,
-          sizeof (accounts));
-  for (unsigned int i = 0; i<pis->payto_uris_length; i++)
-    accounts[i].payto_uri = pis->payto_uris[i];
+
   pis->is = is;
   pis->iph = TALER_MERCHANT_instance_patch (
     TALER_TESTING_interpreter_get_context (is),
     pis->merchant_url,
     pis->instance_id,
-    pis->payto_uris_length,
-    accounts,
     pis->name,
     TALER_KYCLOGIC_KYC_UT_BUSINESS,
     pis->address,
@@ -199,39 +180,23 @@ patch_instance_run (void *cls,
  * @param index index number of the object to extract.
  * @return #GNUNET_OK on success
  */
-static int
+static enum GNUNET_GenericReturnValue
 patch_instance_traits (void *cls,
                        const void **ret,
                        const char *trait,
                        unsigned int index)
 {
   struct PatchInstanceState *pis = cls;
-  #define NUM_TRAITS (pis->payto_uris_length) + 9
-  struct TALER_TESTING_Trait traits[NUM_TRAITS];
-  traits[0] =
-    TALER_TESTING_make_trait_instance_name (pis->name);
-  traits[1] =
-    TALER_TESTING_make_trait_instance_id (pis->instance_id);
-  traits[2] =
-    TALER_TESTING_make_trait_address (pis->address);
-  traits[3] =
-    TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction);
-  traits[4] =
-    TALER_TESTING_make_trait_use_stefan (&pis->use_stefan);
-  traits[5] =
-    TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay);
-  traits[6] =
-    TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay);
-  traits[7] =
-    TALER_TESTING_make_trait_payto_length (&pis->payto_uris_length);
-  traits[NUM_TRAITS - 1] =
-    TALER_TESTING_trait_end ();
-  for (unsigned int i = 0; i < pis->payto_uris_length; ++i)
-  {
-    traits[10 + i] =
-      TALER_TESTING_make_trait_payto_uris (i,
-                                           pis->payto_uris[i]);
-  }
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_instance_name (pis->name),
+    TALER_TESTING_make_trait_instance_id (pis->instance_id),
+    TALER_TESTING_make_trait_address (pis->address),
+    TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction),
+    TALER_TESTING_make_trait_use_stefan (&pis->use_stefan),
+    TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay),
+    TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay),
+    TALER_TESTING_trait_end ()
+  };
 
   return TALER_TESTING_get_trait (traits,
                                   ret,
@@ -259,9 +224,7 @@ patch_instance_cleanup (void *cls,
                 "PATCH /instance/$ID operation did not complete\n");
     TALER_MERCHANT_instance_patch_cancel (pis->iph);
   }
-  json_decref (pis->address);
   json_decref (pis->jurisdiction);
-  GNUNET_free (pis->payto_uris);
   GNUNET_free (pis);
 }
 
@@ -271,8 +234,6 @@ TALER_TESTING_cmd_merchant_patch_instance (
   const char *label,
   const char *merchant_url,
   const char *instance_id,
-  unsigned int payto_uris_length,
-  const char *payto_uris[],
   const char *name,
   json_t *address,
   json_t *jurisdiction,
@@ -287,12 +248,6 @@ TALER_TESTING_cmd_merchant_patch_instance (
   pis->merchant_url = merchant_url;
   pis->instance_id = instance_id;
   pis->http_status = http_status;
-  pis->payto_uris_length = payto_uris_length;
-  pis->payto_uris = GNUNET_new_array (payto_uris_length,
-                                      const char *);
-  GNUNET_memcpy (pis->payto_uris,
-                 payto_uris,
-                 sizeof (const char *) * payto_uris_length);
   pis->name = name;
   pis->address = address; /* ownership transfer! */
   pis->jurisdiction = jurisdiction; /* ownership transfer! */
diff --git a/src/testing/testing_api_cmd_patch_template.c 
b/src/testing/testing_api_cmd_patch_otp_device.c
similarity index 55%
copy from src/testing/testing_api_cmd_patch_template.c
copy to src/testing/testing_api_cmd_patch_otp_device.c
index caf3bf23..ce263908 100644
--- a/src/testing/testing_api_cmd_patch_template.c
+++ b/src/testing/testing_api_cmd_patch_otp_device.c
@@ -17,9 +17,9 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file testing_api_cmd_patch_template.c
- * @brief command to test PATCH /template
- * @author Priscilla HUANG
+ * @file testing_api_cmd_patch_otp_device.c
+ * @brief command to test PATCH /otp-device
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <taler/taler_exchange_service.h>
@@ -29,15 +29,15 @@
 
 
 /**
- * State of a "PATCH /template" CMD.
+ * State of a "PATCH /otp-device" CMD.
  */
-struct PatchTemplateState
+struct PatchOtpDeviceState
 {
 
   /**
-   * Handle for a "GET template" request.
+   * Handle for a "GET otp_device" request.
    */
-  struct TALER_MERCHANT_TemplatePatchHandle *iph;
+  struct TALER_MERCHANT_OtpDevicePatchHandle *iph;
 
   /**
    * The interpreter state.
@@ -50,29 +50,29 @@ struct PatchTemplateState
   const char *merchant_url;
 
   /**
-   * ID of the template to run GET for.
+   * ID of the otp_device to run GET for.
    */
-  const char *template_id;
+  const char *otp_device_id;
 
   /**
-   * description of the template
+   * description of the otp_device
    */
-  const char *template_description;
+  const char *otp_device_description;
 
   /**
    * base64-encoded key
    */
-  char *pos_key;
+  char *otp_key;
 
   /**
-   * Option that add amount of the order
+   * Algorithm used by the OTP device
    */
-  enum TALER_MerchantConfirmationAlgorithm pos_alg;
+  enum TALER_MerchantConfirmationAlgorithm otp_alg;
 
   /**
-   * Contract of the company
+   * Counter of the device (if in counter mode).
    */
-  json_t *template_contract;
+  uint64_t otp_ctr;
 
   /**
    * Expected HTTP response code.
@@ -83,16 +83,16 @@ struct PatchTemplateState
 
 
 /**
- * Callback for a PATCH /templates/$ID operation.
+ * Callback for a PATCH /otp-devices/$ID operation.
  *
  * @param cls closure for this function
  * @param hr response being processed
  */
 static void
-patch_template_cb (void *cls,
-                   const struct TALER_MERCHANT_HttpResponse *hr)
+patch_otp_device_cb (void *cls,
+                     const struct TALER_MERCHANT_HttpResponse *hr)
 {
-  struct PatchTemplateState *pis = cls;
+  struct PatchOtpDeviceState *pis = cls;
 
   pis->iph = NULL;
   if (pis->http_status != hr->http_status)
@@ -119,7 +119,7 @@ patch_template_cb (void *cls,
     break;
   default:
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Unhandled HTTP status %u for PATCH /templates/ID.\n",
+                "Unhandled HTTP status %u for PATCH /otp-devices/ID.\n",
                 hr->http_status);
   }
   TALER_TESTING_interpreter_next (pis->is);
@@ -127,7 +127,7 @@ patch_template_cb (void *cls,
 
 
 /**
- * Run the "PATCH /templates/$ID" CMD.
+ * Run the "PATCH /otp-devices/$ID" CMD.
  *
  *
  * @param cls closure.
@@ -135,29 +135,29 @@ patch_template_cb (void *cls,
  * @param is interpreter state.
  */
 static void
-patch_template_run (void *cls,
+patch_otp_device_run (void *cls,
                     const struct TALER_TESTING_Command *cmd,
                     struct TALER_TESTING_Interpreter *is)
 {
-  struct PatchTemplateState *pis = cls;
+  struct PatchOtpDeviceState *pis = cls;
 
   pis->is = is;
-  pis->iph = TALER_MERCHANT_template_patch (
+  pis->iph = TALER_MERCHANT_otp_device_patch (
     TALER_TESTING_interpreter_get_context (is),
     pis->merchant_url,
-    pis->template_id,
-    pis->template_description,
-    pis->pos_key,
-    pis->pos_alg,
-    pis->template_contract,
-    &patch_template_cb,
+    pis->otp_device_id,
+    pis->otp_device_description,
+    pis->otp_key,
+    pis->otp_alg,
+    pis->otp_ctr,
+    &patch_otp_device_cb,
     pis);
   GNUNET_assert (NULL != pis->iph);
 }
 
 
 /**
- * Offers information from the PATCH /templates CMD state to other
+ * Offers information from the PATCH /otp-devices CMD state to other
  * commands.
  *
  * @param cls closure
@@ -167,18 +167,17 @@ patch_template_run (void *cls,
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-patch_template_traits (void *cls,
-                       const void **ret,
-                       const char *trait,
-                       unsigned int index)
+patch_otp_device_traits (void *cls,
+                         const void **ret,
+                         const char *trait,
+                         unsigned int index)
 {
-  struct PatchTemplateState *pts = cls;
+  struct PatchOtpDeviceState *pts = cls;
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_template_description (pts->template_description),
-    TALER_TESTING_make_trait_template_pos_key (pts->pos_key),
-    TALER_TESTING_make_trait_template_pos_alg (&pts->pos_alg),
-    TALER_TESTING_make_trait_template_contract (pts->template_contract),
-    TALER_TESTING_make_trait_template_id (pts->template_id),
+    TALER_TESTING_make_trait_otp_device_description 
(pts->otp_device_description),
+    TALER_TESTING_make_trait_otp_key (pts->otp_key),
+    TALER_TESTING_make_trait_otp_alg (&pts->otp_alg),
+    TALER_TESTING_make_trait_otp_id (pts->otp_device_id),
     TALER_TESTING_trait_end (),
   };
 
@@ -190,58 +189,57 @@ patch_template_traits (void *cls,
 
 
 /**
- * Free the state of a "GET template" CMD, and possibly
+ * Free the state of a "GET otp_device" CMD, and possibly
  * cancel a pending operation thereof.
  *
  * @param cls closure.
  * @param cmd command being run.
  */
 static void
-patch_template_cleanup (void *cls,
-                        const struct TALER_TESTING_Command *cmd)
+patch_otp_device_cleanup (void *cls,
+                          const struct TALER_TESTING_Command *cmd)
 {
-  struct PatchTemplateState *pis = cls;
+  struct PatchOtpDeviceState *pis = cls;
 
   if (NULL != pis->iph)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "PATCH /templates/$ID operation did not complete\n");
-    TALER_MERCHANT_template_patch_cancel (pis->iph);
+                "PATCH /otp-devices/$ID operation did not complete\n");
+    TALER_MERCHANT_otp_device_patch_cancel (pis->iph);
   }
-  GNUNET_free (pis->pos_key);
-  json_decref (pis->template_contract);
+  GNUNET_free (pis->otp_key);
   GNUNET_free (pis);
 }
 
 
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_patch_template (
+TALER_TESTING_cmd_merchant_patch_otp_device (
   const char *label,
   const char *merchant_url,
-  const char *template_id,
-  const char *template_description,
-  const char *pos_key,
-  const enum TALER_MerchantConfirmationAlgorithm pos_alg,
-  json_t *template_contract,
+  const char *otp_device_id,
+  const char *otp_device_description,
+  const char *otp_key,
+  const enum TALER_MerchantConfirmationAlgorithm otp_alg,
+  uint64_t otp_ctr,
   unsigned int http_status)
 {
-  struct PatchTemplateState *pis;
+  struct PatchOtpDeviceState *pis;
 
-  pis = GNUNET_new (struct PatchTemplateState);
+  pis = GNUNET_new (struct PatchOtpDeviceState);
   pis->merchant_url = merchant_url;
-  pis->template_id = template_id;
+  pis->otp_device_id = otp_device_id;
   pis->http_status = http_status;
-  pis->template_description = template_description;
-  pis->pos_key = (NULL == pos_key) ? NULL : GNUNET_strdup (pos_key);
-  pis->pos_alg = pos_alg;
-  pis->template_contract = template_contract; /* ownership taken */
+  pis->otp_device_description = otp_device_description;
+  pis->otp_key = GNUNET_strdup (otp_key);
+  pis->otp_alg = otp_alg;
+  pis->otp_ctr = otp_ctr;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = pis,
       .label = label,
-      .run = &patch_template_run,
-      .cleanup = &patch_template_cleanup,
-      .traits = &patch_template_traits
+      .run = &patch_otp_device_run,
+      .cleanup = &patch_otp_device_cleanup,
+      .traits = &patch_otp_device_traits
     };
 
     return cmd;
@@ -249,4 +247,4 @@ TALER_TESTING_cmd_merchant_patch_template (
 }
 
 
-/* end of testing_api_cmd_patch_template.c */
+/* end of testing_api_cmd_patch_otp_device.c */
diff --git a/src/testing/testing_api_cmd_patch_template.c 
b/src/testing/testing_api_cmd_patch_template.c
index caf3bf23..8ad9d9dc 100644
--- a/src/testing/testing_api_cmd_patch_template.c
+++ b/src/testing/testing_api_cmd_patch_template.c
@@ -60,14 +60,9 @@ struct PatchTemplateState
   const char *template_description;
 
   /**
-   * base64-encoded key
+   * OTP device ID
    */
-  char *pos_key;
-
-  /**
-   * Option that add amount of the order
-   */
-  enum TALER_MerchantConfirmationAlgorithm pos_alg;
+  char *otp_id;
 
   /**
    * Contract of the company
@@ -147,8 +142,7 @@ patch_template_run (void *cls,
     pis->merchant_url,
     pis->template_id,
     pis->template_description,
-    pis->pos_key,
-    pis->pos_alg,
+    pis->otp_id,
     pis->template_contract,
     &patch_template_cb,
     pis);
@@ -175,8 +169,7 @@ patch_template_traits (void *cls,
   struct PatchTemplateState *pts = cls;
   struct TALER_TESTING_Trait traits[] = {
     TALER_TESTING_make_trait_template_description (pts->template_description),
-    TALER_TESTING_make_trait_template_pos_key (pts->pos_key),
-    TALER_TESTING_make_trait_template_pos_alg (&pts->pos_alg),
+    TALER_TESTING_make_trait_otp_id (pts->otp_id),
     TALER_TESTING_make_trait_template_contract (pts->template_contract),
     TALER_TESTING_make_trait_template_id (pts->template_id),
     TALER_TESTING_trait_end (),
@@ -208,7 +201,7 @@ patch_template_cleanup (void *cls,
                 "PATCH /templates/$ID operation did not complete\n");
     TALER_MERCHANT_template_patch_cancel (pis->iph);
   }
-  GNUNET_free (pis->pos_key);
+  GNUNET_free (pis->otp_id);
   json_decref (pis->template_contract);
   GNUNET_free (pis);
 }
@@ -220,8 +213,7 @@ TALER_TESTING_cmd_merchant_patch_template (
   const char *merchant_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  const enum TALER_MerchantConfirmationAlgorithm pos_alg,
+  const char *otp_id,
   json_t *template_contract,
   unsigned int http_status)
 {
@@ -232,8 +224,7 @@ TALER_TESTING_cmd_merchant_patch_template (
   pis->template_id = template_id;
   pis->http_status = http_status;
   pis->template_description = template_description;
-  pis->pos_key = (NULL == pos_key) ? NULL : GNUNET_strdup (pos_key);
-  pis->pos_alg = pos_alg;
+  pis->otp_id = (NULL == otp_id) ? NULL : GNUNET_strdup (otp_id);
   pis->template_contract = template_contract; /* ownership taken */
   {
     struct TALER_TESTING_Command cmd = {
diff --git a/src/testing/testing_api_cmd_pay_order.c 
b/src/testing/testing_api_cmd_pay_order.c
index b49dd716..efc94a80 100644
--- a/src/testing/testing_api_cmd_pay_order.c
+++ b/src/testing/testing_api_cmd_pay_order.c
@@ -349,12 +349,13 @@ pay_run (void *cls,
   if (NULL == contract_terms)
     TALER_TESTING_FAIL (is);
   if (GNUNET_OK !=
-      TALER_TESTING_get_trait_template_pos_key (proposal_cmd,
-                                                &ps->pos_key))
+      TALER_TESTING_get_trait_otp_key (proposal_cmd,
+                                       &ps->pos_key))
     ps->pos_key = NULL;
-  if (GNUNET_OK ==
-      TALER_TESTING_get_trait_template_pos_alg (proposal_cmd,
-                                                &alg_ptr))
+  if ( (GNUNET_OK ==
+        TALER_TESTING_get_trait_otp_alg (proposal_cmd,
+                                         &alg_ptr)) &&
+       (NULL != alg_ptr) )
     ps->pos_alg = *alg_ptr;
   {
     /* Get information that needs to be put verbatim in the
@@ -543,8 +544,8 @@ pay_traits (void *cls,
         TALER_TESTING_make_trait_merchant_pub (merchant_pub),
         TALER_TESTING_make_trait_merchant_sig (&ps->merchant_sig),
         TALER_TESTING_make_trait_amount (&amount_with_fee),
-        TALER_TESTING_make_trait_template_pos_key (ps->pos_key),
-        TALER_TESTING_make_trait_template_pos_alg (&ps->pos_alg),
+        TALER_TESTING_make_trait_otp_key (ps->pos_key),
+        TALER_TESTING_make_trait_otp_alg (&ps->pos_alg),
         TALER_TESTING_trait_end ()
       };
 
diff --git a/src/testing/testing_api_cmd_post_account.c 
b/src/testing/testing_api_cmd_post_account.c
index 345e869f..8ddad94c 100644
--- a/src/testing/testing_api_cmd_post_account.c
+++ b/src/testing/testing_api_cmd_post_account.c
@@ -37,7 +37,7 @@ struct PostAccountState
   /**
    * Handle for a "GET product" request.
    */
-  struct TALER_MERCHANT_AccountPostHandle *aph;
+  struct TALER_MERCHANT_AccountsPostHandle *aph;
 
   /**
    * The interpreter state.
@@ -50,9 +50,24 @@ struct PostAccountState
   const char *merchant_url;
 
   /**
-   * Account configuration for the account to create.
+   * Wire hash of the created account, set on success.
    */
-  struct TALER_MERCHANT_AccountConfig ac;
+  struct TALER_MerchantWireHashP h_wire;
+
+  /**
+   * RFC 8905 URI for the account to create.
+   */
+  char *payto_uri;
+
+  /**
+   * Credit facade URL for the account to create.
+   */
+  char *credit_facade_url;
+
+  /**
+   * Credit facade credentials for the account to create.
+   */
+  json_t *credit_facade_credentials;
 
   /**
    * Expected HTTP response code.
@@ -70,7 +85,7 @@ struct PostAccountState
  */
 static void
 post_account_cb (void *cls,
-                 const struct TALER_MERCHANT_AccountPostResponse *apr)
+                 const struct TALER_MERCHANT_AccountsPostResponse *apr)
 {
   struct PostAccountState *pas = cls;
 
@@ -87,7 +102,8 @@ post_account_cb (void *cls,
   }
   switch (apr->hr.http_status)
   {
-  case MHD_HTTP_NO_CONTENT:
+  case MHD_HTTP_OK:
+    pas->h_wire = apr->details.ok.h_wire;
     break;
   case MHD_HTTP_UNAUTHORIZED:
     break;
@@ -123,10 +139,12 @@ post_account_run (void *cls,
   struct PostAccountState *pas = cls;
 
   pas->is = is;
-  pas->aph = TALER_MERCHANT_account_post (
+  pas->aph = TALER_MERCHANT_accounts_post (
     TALER_TESTING_interpreter_get_context (is),
     pas->merchant_url,
-    &pas->ac,
+    pas->payto_uri,
+    pas->credit_facade_url,
+    pas->credit_facade_credentials,
     &post_account_cb,
     pas);
   GNUNET_assert (NULL != pas->aph);
@@ -149,8 +167,16 @@ post_account_traits (void *cls,
                      const char *trait,
                      unsigned int index)
 {
-  /* struct PostAccountState *pps = cls; */
+  struct PostAccountState *pps = cls; 
   struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_h_wires (
+      0,
+      &pps->h_wire),
+    TALER_TESTING_make_trait_payto_uris (
+      0,
+      pps->payto_uri),
+    TALER_TESTING_make_trait_merchant_base_url (
+      pps->merchant_url),
     TALER_TESTING_trait_end (),
   };
 
@@ -178,9 +204,11 @@ post_account_cleanup (void *cls,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "POST /account operation did not complete\n");
-    TALER_MERCHANT_account_post_cancel (pas->aph);
+    TALER_MERCHANT_accounts_post_cancel (pas->aph);
   }
-  json_decref (pas->ac.credit_facade_credentials);
+  GNUNET_free (pas->payto_uri);
+  GNUNET_free (pas->credit_facade_url);
+  json_decref (pas->credit_facade_credentials);
   GNUNET_free (pas);
 }
 
@@ -198,10 +226,11 @@ TALER_TESTING_cmd_merchant_post_account (
 
   pas = GNUNET_new (struct PostAccountState);
   pas->merchant_url = merchant_url;
-  pas->ac.payto_uri = payto_uri;
-  pas->ac.credit_facade_url = credit_facade_url;
+  pas->payto_uri = GNUNET_strdup (payto_uri);
+  if (NULL != credit_facade_url)
+    pas->credit_facade_url = GNUNET_strdup (credit_facade_url);
   if (NULL != credit_facade_credentials)
-    pas->ac.credit_facade_credentials
+    pas->credit_facade_credentials
       = json_incref ((json_t *) credit_facade_credentials);
   pas->http_status = http_status;
   {
diff --git a/src/testing/testing_api_cmd_post_instances.c 
b/src/testing/testing_api_cmd_post_instances.c
index f90aacaf..f1b81cbf 100644
--- a/src/testing/testing_api_cmd_post_instances.c
+++ b/src/testing/testing_api_cmd_post_instances.c
@@ -54,16 +54,6 @@ struct PostInstancesState
    */
   const char *instance_id;
 
-  /**
-   * Length of the @payto_uris array
-   */
-  unsigned int payto_uris_length;
-
-  /**
-   * Array of payto URIs.
-   */
-  const char **payto_uris;
-
   /**
    * Name of the instance.
    */
@@ -168,21 +158,12 @@ post_instances_run (void *cls,
                     struct TALER_TESTING_Interpreter *is)
 {
   struct PostInstancesState *pis = cls;
-  struct TALER_MERCHANT_AccountConfig accounts[GNUNET_NZL (
-                                                 pis->payto_uris_length)];
-
-  memset (accounts,
-          0,
-          sizeof (accounts));
-  for (unsigned int i = 0; i<pis->payto_uris_length; i++)
-    accounts[i].payto_uri = pis->payto_uris[i];
+
   pis->is = is;
   pis->iph = TALER_MERCHANT_instances_post (
     TALER_TESTING_interpreter_get_context (is),
     pis->merchant_url,
     pis->instance_id,
-    pis->payto_uris_length,
-    accounts,
     pis->name,
     TALER_KYCLOGIC_KYC_UT_BUSINESS,
     pis->address,
@@ -219,32 +200,16 @@ post_instances_traits (void *cls,
                        unsigned int index)
 {
   struct PostInstancesState *pis = cls;
-  #define NUM_TRAITS (pis->payto_uris_length) + 9
-  struct TALER_TESTING_Trait traits[NUM_TRAITS];
-  traits[0] =
-    TALER_TESTING_make_trait_instance_name (pis->name);
-  traits[1] =
-    TALER_TESTING_make_trait_instance_id (pis->instance_id);
-  traits[2] =
-    TALER_TESTING_make_trait_address (pis->address);
-  traits[3] =
-    TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction);
-  traits[4] =
-    TALER_TESTING_make_trait_use_stefan (&pis->use_stefan);
-  traits[5] =
-    TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay);
-  traits[6] =
-    TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay);
-  traits[7] =
-    TALER_TESTING_make_trait_payto_length (&pis->payto_uris_length);
-  traits[NUM_TRAITS - 1] =
-    TALER_TESTING_trait_end ();
-  for (unsigned int i = 0; i < pis->payto_uris_length; ++i)
-  {
-    traits[10 + i] =
-      TALER_TESTING_make_trait_payto_uris (i,
-                                           pis->payto_uris[i]);
-  }
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_instance_name (pis->name),
+    TALER_TESTING_make_trait_instance_id (pis->instance_id),
+    TALER_TESTING_make_trait_address (pis->address),
+    TALER_TESTING_make_trait_jurisdiction (pis->jurisdiction),
+    TALER_TESTING_make_trait_use_stefan (&pis->use_stefan),
+    TALER_TESTING_make_trait_wire_delay (&pis->default_wire_transfer_delay),
+    TALER_TESTING_make_trait_pay_delay (&pis->default_pay_delay),
+    TALER_TESTING_trait_end ()
+  };
 
   return TALER_TESTING_get_trait (traits,
                                   ret,
@@ -274,7 +239,6 @@ post_instances_cleanup (void *cls,
   }
   json_decref (pis->address);
   json_decref (pis->jurisdiction);
-  GNUNET_free (pis->payto_uris);
   GNUNET_free (pis);
 }
 
@@ -284,8 +248,6 @@ TALER_TESTING_cmd_merchant_post_instances2 (
   const char *label,
   const char *merchant_url,
   const char *instance_id,
-  unsigned int payto_uris_length,
-  const char *payto_uris[],
   const char *name,
   json_t *address,
   json_t *jurisdiction,
@@ -301,12 +263,6 @@ TALER_TESTING_cmd_merchant_post_instances2 (
   pis->merchant_url = merchant_url;
   pis->instance_id = instance_id;
   pis->http_status = http_status;
-  pis->payto_uris_length = payto_uris_length;
-  pis->payto_uris = GNUNET_new_array (payto_uris_length,
-                                      const char *);
-  GNUNET_memcpy (pis->payto_uris,
-                 payto_uris,
-                 sizeof (const char *) * payto_uris_length);
   pis->name = name;
   pis->address = address; /* ownership transfer! */
   pis->jurisdiction = jurisdiction; /* ownership transfer! */
@@ -332,19 +288,12 @@ struct TALER_TESTING_Command
 TALER_TESTING_cmd_merchant_post_instances (const char *label,
                                            const char *merchant_url,
                                            const char *instance_id,
-                                           const char *payto_uri,
                                            unsigned int http_status)
 {
-  const char *payto_uris[] = {
-    payto_uri
-  };
-
   return TALER_TESTING_cmd_merchant_post_instances2 (
     label,
     merchant_url,
     instance_id,
-    1,
-    payto_uris,
     instance_id,
     json_pack ("{s:s}", "city", "shopcity"),
     json_pack ("{s:s}", "city", "lawyercity"),
diff --git a/src/testing/testing_api_cmd_post_templates.c 
b/src/testing/testing_api_cmd_post_otp_devices.c
similarity index 52%
copy from src/testing/testing_api_cmd_post_templates.c
copy to src/testing/testing_api_cmd_post_otp_devices.c
index 13ffc24e..09358274 100644
--- a/src/testing/testing_api_cmd_post_templates.c
+++ b/src/testing/testing_api_cmd_post_otp_devices.c
@@ -17,9 +17,9 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file testing_api_cmd_post_templates.c
- * @brief command to test POST /templates
- * @author Priscilla HUANG
+ * @file testing_api_cmd_post_otp_devices.c
+ * @brief command to test POST /otp-devices
+ * @author Christian Grothoff
  */
 #include "platform.h"
 #include <taler/taler_exchange_service.h>
@@ -29,15 +29,15 @@
 
 
 /**
- * State of a "POST /templates" CMD.
+ * State of a "POST /otp-devices" CMD.
  */
-struct PostTemplatesState
+struct PostOtpDevicesState
 {
 
   /**
-   * Handle for a "GET template" request.
+   * Handle for a "GET otp_device" request.
    */
-  struct TALER_MERCHANT_TemplatesPostHandle *iph;
+  struct TALER_MERCHANT_OtpDevicesPostHandle *iph;
 
   /**
    * The interpreter state.
@@ -50,29 +50,29 @@ struct PostTemplatesState
   const char *merchant_url;
 
   /**
-   * ID of the template to run POST for.
+   * ID of the otp_device to run POST for.
    */
-  const char *template_id;
+  const char *otp_device_id;
 
   /**
-   * description of the template
+   * description of the otp_device
    */
-  const char *template_description;
+  const char *otp_device_description;
 
   /**
    * base64-encoded key
    */
-  char *pos_key;
+  char *otp_key;
 
   /**
    * Option that add amount of the order
    */
-  enum TALER_MerchantConfirmationAlgorithm pos_alg;
+  enum TALER_MerchantConfirmationAlgorithm otp_alg;
 
   /**
-   * Contract of the company
+   * Counter at the OTP device.
    */
-  json_t *template_contract;
+  uint64_t otp_ctr;
 
   /**
    * Expected HTTP response code.
@@ -83,16 +83,16 @@ struct PostTemplatesState
 
 
 /**
- * Callback for a POST /templates operation.
+ * Callback for a POST /otp-devices operation.
  *
  * @param cls closure for this function
  * @param hr response being processed
  */
 static void
-post_templates_cb (void *cls,
+post_otp_devices_cb (void *cls,
                    const struct TALER_MERCHANT_HttpResponse *hr)
 {
-  struct PostTemplatesState *tis = cls;
+  struct PostOtpDevicesState *tis = cls;
 
   tis->iph = NULL;
   if (tis->http_status != hr->http_status)
@@ -120,7 +120,7 @@ post_templates_cb (void *cls,
   default:
     GNUNET_break (0);
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Unhandled HTTP status %u for POST /templates.\n",
+                "Unhandled HTTP status %u for POST /otp-devices.\n",
                 hr->http_status);
   }
   TALER_TESTING_interpreter_next (tis->is);
@@ -128,7 +128,7 @@ post_templates_cb (void *cls,
 
 
 /**
- * Run the "POST /templates" CMD.
+ * Run the "POST /otp-devices" CMD.
  *
  *
  * @param cls closure.
@@ -136,22 +136,22 @@ post_templates_cb (void *cls,
  * @param is interpreter state.
  */
 static void
-post_templates_run (void *cls,
+post_otp_devices_run (void *cls,
                     const struct TALER_TESTING_Command *cmd,
                     struct TALER_TESTING_Interpreter *is)
 {
-  struct PostTemplatesState *tis = cls;
+  struct PostOtpDevicesState *tis = cls;
 
   tis->is = is;
-  tis->iph = TALER_MERCHANT_templates_post (
+  tis->iph = TALER_MERCHANT_otp_devices_post (
     TALER_TESTING_interpreter_get_context (is),
     tis->merchant_url,
-    tis->template_id,
-    tis->template_description,
-    tis->pos_key,
-    tis->pos_alg,
-    tis->template_contract,
-    &post_templates_cb,
+    tis->otp_device_id,
+    tis->otp_device_description,
+    tis->otp_key,
+    tis->otp_alg,
+    tis->otp_ctr,
+    &post_otp_devices_cb,
     tis);
   if (NULL == tis->iph)
   {
@@ -163,7 +163,7 @@ post_templates_run (void *cls,
 
 
 /**
- * Offers information from the POST /templates CMD state to other
+ * Offers information from the POST /otp-devices CMD state to other
  * commands.
  *
  * @param cls closure
@@ -173,18 +173,17 @@ post_templates_run (void *cls,
  * @return #GNUNET_OK on success
  */
 static enum GNUNET_GenericReturnValue
-post_templates_traits (void *cls,
+post_otp_devices_traits (void *cls,
                        const void **ret,
                        const char *trait,
                        unsigned int index)
 {
-  struct PostTemplatesState *pts = cls;
+  struct PostOtpDevicesState *pts = cls;
   struct TALER_TESTING_Trait traits[] = {
-    TALER_TESTING_make_trait_template_description (pts->template_description),
-    TALER_TESTING_make_trait_template_pos_key (pts->pos_key),
-    TALER_TESTING_make_trait_template_pos_alg (&pts->pos_alg),
-    TALER_TESTING_make_trait_template_contract (pts->template_contract),
-    TALER_TESTING_make_trait_template_id (pts->template_id),
+    TALER_TESTING_make_trait_otp_device_description 
(pts->otp_device_description),
+    TALER_TESTING_make_trait_otp_key (pts->otp_key),
+    TALER_TESTING_make_trait_otp_alg (&pts->otp_alg),
+    TALER_TESTING_make_trait_otp_id (pts->otp_device_id),
     TALER_TESTING_trait_end (),
   };
 
@@ -196,61 +195,57 @@ post_templates_traits (void *cls,
 
 
 /**
- * Free the state of a "POST template" CMD, and possibly
+ * Free the state of a "POST otp_device" CMD, and possibly
  * cancel a pending operation thereof.
  *
  * @param cls closure.
  * @param cmd command being run.
  */
 static void
-post_templates_cleanup (void *cls,
-                        const struct TALER_TESTING_Command *cmd)
+post_otp_devices_cleanup (void *cls,
+                          const struct TALER_TESTING_Command *cmd)
 {
-  struct PostTemplatesState *tis = cls;
+  struct PostOtpDevicesState *tis = cls;
 
   if (NULL != tis->iph)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "POST /templates operation did not complete\n");
-    TALER_MERCHANT_templates_post_cancel (tis->iph);
+                "POST /otp-devices operation did not complete\n");
+    TALER_MERCHANT_otp_devices_post_cancel (tis->iph);
   }
-  GNUNET_free (tis->pos_key);
-  json_decref (tis->template_contract);
+  GNUNET_free (tis->otp_key);
   GNUNET_free (tis);
 }
 
 
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_post_templates2 (
+TALER_TESTING_cmd_merchant_post_otp_devices (
   const char *label,
   const char *merchant_url,
-  const char *template_id,
-  const char *template_description,
-  const char *pos_key,
-  const enum TALER_MerchantConfirmationAlgorithm pos_alg,
-  json_t *template_contract,
+  const char *otp_device_id,
+  const char *otp_device_description,
+  const char *otp_key,
+  const enum TALER_MerchantConfirmationAlgorithm otp_alg,
+  uint64_t otp_ctr,
   unsigned int http_status)
 {
-  struct PostTemplatesState *tis;
+  struct PostOtpDevicesState *tis;
 
-  GNUNET_assert ((NULL == template_contract) ||
-                 json_is_object (template_contract));
-
-  tis = GNUNET_new (struct PostTemplatesState);
+  tis = GNUNET_new (struct PostOtpDevicesState);
   tis->merchant_url = merchant_url;
-  tis->template_id = template_id;
+  tis->otp_device_id = otp_device_id;
   tis->http_status = http_status;
-  tis->template_description = template_description;
-  tis->pos_key = (NULL == pos_key) ? NULL : GNUNET_strdup (pos_key);
-  tis->pos_alg = pos_alg;
-  tis->template_contract = template_contract;
+  tis->otp_device_description = otp_device_description;
+  tis->otp_key = GNUNET_strdup (otp_key);
+  tis->otp_alg = otp_alg;
+  tis->otp_ctr = otp_ctr;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = tis,
       .label = label,
-      .run = &post_templates_run,
-      .cleanup = &post_templates_cleanup,
-      .traits = &post_templates_traits
+      .run = &post_otp_devices_run,
+      .cleanup = &post_otp_devices_cleanup,
+      .traits = &post_otp_devices_traits
     };
 
     return cmd;
@@ -258,26 +253,4 @@ TALER_TESTING_cmd_merchant_post_templates2 (
 }
 
 
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_merchant_post_templates (const char *label,
-                                           const char *merchant_url,
-                                           const char *template_id,
-                                           const char *template_description,
-                                           unsigned int http_status)
-{
-  return TALER_TESTING_cmd_merchant_post_templates2 (
-    label,
-    merchant_url,
-    template_id,
-    template_description,
-    NULL,
-    TALER_MCA_NONE,
-    GNUNET_JSON_PACK (
-      GNUNET_JSON_pack_uint64 ("minimum_age", 0),
-      GNUNET_JSON_pack_time_rel ("pay_duration",
-                                 GNUNET_TIME_UNIT_MINUTES)),
-    http_status);
-}
-
-
-/* end of testing_api_cmd_post_templates.c */
+/* end of testing_api_cmd_post_otp_devices.c */
diff --git a/src/testing/testing_api_cmd_post_templates.c 
b/src/testing/testing_api_cmd_post_templates.c
index 13ffc24e..8c8cd8ca 100644
--- a/src/testing/testing_api_cmd_post_templates.c
+++ b/src/testing/testing_api_cmd_post_templates.c
@@ -60,14 +60,9 @@ struct PostTemplatesState
   const char *template_description;
 
   /**
-   * base64-encoded key
+   * OTP device ID.
    */
-  char *pos_key;
-
-  /**
-   * Option that add amount of the order
-   */
-  enum TALER_MerchantConfirmationAlgorithm pos_alg;
+  char *otp_id;
 
   /**
    * Contract of the company
@@ -148,8 +143,7 @@ post_templates_run (void *cls,
     tis->merchant_url,
     tis->template_id,
     tis->template_description,
-    tis->pos_key,
-    tis->pos_alg,
+    tis->otp_id,
     tis->template_contract,
     &post_templates_cb,
     tis);
@@ -181,8 +175,7 @@ post_templates_traits (void *cls,
   struct PostTemplatesState *pts = cls;
   struct TALER_TESTING_Trait traits[] = {
     TALER_TESTING_make_trait_template_description (pts->template_description),
-    TALER_TESTING_make_trait_template_pos_key (pts->pos_key),
-    TALER_TESTING_make_trait_template_pos_alg (&pts->pos_alg),
+    TALER_TESTING_make_trait_otp_id (pts->otp_id),
     TALER_TESTING_make_trait_template_contract (pts->template_contract),
     TALER_TESTING_make_trait_template_id (pts->template_id),
     TALER_TESTING_trait_end (),
@@ -214,7 +207,7 @@ post_templates_cleanup (void *cls,
                 "POST /templates operation did not complete\n");
     TALER_MERCHANT_templates_post_cancel (tis->iph);
   }
-  GNUNET_free (tis->pos_key);
+  GNUNET_free (tis->otp_id);
   json_decref (tis->template_contract);
   GNUNET_free (tis);
 }
@@ -226,8 +219,7 @@ TALER_TESTING_cmd_merchant_post_templates2 (
   const char *merchant_url,
   const char *template_id,
   const char *template_description,
-  const char *pos_key,
-  const enum TALER_MerchantConfirmationAlgorithm pos_alg,
+  const char *otp_id,
   json_t *template_contract,
   unsigned int http_status)
 {
@@ -241,8 +233,7 @@ TALER_TESTING_cmd_merchant_post_templates2 (
   tis->template_id = template_id;
   tis->http_status = http_status;
   tis->template_description = template_description;
-  tis->pos_key = (NULL == pos_key) ? NULL : GNUNET_strdup (pos_key);
-  tis->pos_alg = pos_alg;
+  tis->otp_id = (NULL == otp_id) ? NULL : GNUNET_strdup (otp_id);
   tis->template_contract = template_contract;
   {
     struct TALER_TESTING_Command cmd = {
@@ -271,7 +262,6 @@ TALER_TESTING_cmd_merchant_post_templates (const char 
*label,
     template_id,
     template_description,
     NULL,
-    TALER_MCA_NONE,
     GNUNET_JSON_PACK (
       GNUNET_JSON_pack_uint64 ("minimum_age", 0),
       GNUNET_JSON_pack_time_rel ("pay_duration",
diff --git a/src/testing/testing_api_cmd_post_using_templates.c 
b/src/testing/testing_api_cmd_post_using_templates.c
index da45135b..7aeec33d 100644
--- a/src/testing/testing_api_cmd_post_using_templates.c
+++ b/src/testing/testing_api_cmd_post_using_templates.c
@@ -132,15 +132,20 @@ struct PostUsingTemplatesState
    */
   const char *duplicate_of;
 
+  /**
+   * Label of command creating/updating OTP device, or NULL.
+   */
+  const char *otp_ref;
+
   /**
    * Encoded key for the payment verification.
    */
-  const char *template_pos_key;
+  const char *otp_key;
 
   /**
    * Option that add amount of the order
    */
-  const enum TALER_MerchantConfirmationAlgorithm *template_pos_alg;
+  const enum TALER_MerchantConfirmationAlgorithm *otp_alg;
 
   /**
    * Expected HTTP response code.
@@ -358,16 +363,19 @@ post_using_templates_run (void *cls,
       TALER_TESTING_get_trait_template_id (ref,
                                            &template_id))
     TALER_TESTING_FAIL (is);
-
-  if (GNUNET_OK !=
-      TALER_TESTING_get_trait_template_pos_key (ref,
-                                                &tis->template_pos_key))
-    TALER_TESTING_FAIL (is);
-
-  if (GNUNET_OK !=
-      TALER_TESTING_get_trait_template_pos_alg (ref,
-                                                &tis->template_pos_alg))
-    TALER_TESTING_FAIL (is);
+  if (NULL != tis->otp_ref)
+  {
+    ref = TALER_TESTING_interpreter_lookup_command (is,
+                                                    tis->otp_ref);
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_otp_key (ref,
+                                         &tis->otp_key))
+      TALER_TESTING_FAIL (is);
+    if (GNUNET_OK !=
+        TALER_TESTING_get_trait_otp_alg (ref,
+                                         &tis->otp_alg))
+      TALER_TESTING_FAIL (is);
+  }
   tis->iph = TALER_MERCHANT_using_templates_post (
     TALER_TESTING_interpreter_get_context (is),
     tis->merchant_url,
@@ -408,8 +416,8 @@ post_using_templates_traits (void *cls,
     TALER_TESTING_make_trait_merchant_pub (&pts->merchant_pub),
     TALER_TESTING_make_trait_claim_nonce (&pts->nonce),
     TALER_TESTING_make_trait_claim_token (&pts->claim_token),
-    TALER_TESTING_make_trait_template_pos_key (pts->template_pos_key),
-    TALER_TESTING_make_trait_template_pos_alg (pts->template_pos_alg),
+    TALER_TESTING_make_trait_otp_key (pts->otp_key),
+    TALER_TESTING_make_trait_otp_alg (pts->otp_alg),
     TALER_TESTING_trait_end (),
   };
 
@@ -554,6 +562,7 @@ struct TALER_TESTING_Command
 TALER_TESTING_cmd_merchant_post_using_templates (
   const char *label,
   const char *template_ref,
+  const char *otp_ref,
   const char *merchant_url,
   const char *using_template_id,
   const char *summary,
@@ -566,6 +575,7 @@ TALER_TESTING_cmd_merchant_post_using_templates (
 
   tis = GNUNET_new (struct PostUsingTemplatesState);
   tis->template_ref = template_ref;
+  tis->otp_ref = otp_ref;
   tis->merchant_url = merchant_url;
   tis->using_template_id = using_template_id;
   tis->http_status = http_status;

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