gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (30b24448 -> cdc8c5b5)


From: gnunet
Subject: [taler-exchange] branch master updated (30b24448 -> cdc8c5b5)
Date: Sat, 29 Feb 2020 16:59:19 +0100

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

grothoff pushed a change to branch master
in repository exchange.

    from 30b24448 do not NPE if encountering reserve_close for which we have no 
summary
     new fb932433 phase 1 of #6067: update exchange HTTPD to new API style
     new 582ce77d updating libtalerexchange to new REST API (#6067)
     new 5d843e2c fix uninitialized local
     new ec8e3fad fix shutdown logic if shutdown happens during startup
     new c520b0b8 fix failure to remove rc from JSON
     new 502c9a0f more precise logging
     new 8062ca01 proper generation of /refreshes/ requests
     new 30320fa6 skip also here over part of URI that was already parsed
     new cef3b713 nicer logging
     new de9ab28a rename fest, make symbols better match new endpoint names
     new 0a2b0498 big rename fest related to #6067 API renaming
     new cdc8c5b5 limit redirects

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


Summary of changes:
 src/auditor/taler-auditor.c                        |    2 +-
 src/benchmark/taler-exchange-benchmark.c           |    4 +-
 src/exchange/Makefile.am                           |   14 +-
 src/exchange/taler-exchange-httpd.c                |  565 +++++--
 src/exchange/taler-exchange-httpd.h                |   74 +-
 src/exchange/taler-exchange-httpd_deposit.c        |   40 +-
 src/exchange/taler-exchange-httpd_deposit.h        |   23 +-
 ...ction.c => taler-exchange-httpd_deposits_get.c} |  114 +-
 ...ction.h => taler-exchange-httpd_deposits_get.h} |   16 +-
 src/exchange/taler-exchange-httpd_keystate.c       |   17 +-
 src/exchange/taler-exchange-httpd_keystate.h       |   12 +-
 ..._refresh_link.c => taler-exchange-httpd_link.c} |   44 +-
 ..._refresh_link.h => taler-exchange-httpd_link.h} |   14 +-
 ..._refresh_melt.c => taler-exchange-httpd_melt.c} |  119 +-
 ..._refresh_melt.h => taler-exchange-httpd_melt.h} |   27 +-
 src/exchange/taler-exchange-httpd_mhd.c            |   52 +-
 src/exchange/taler-exchange-httpd_mhd.h            |   55 +-
 src/exchange/taler-exchange-httpd_recoup.c         |   40 +-
 src/exchange/taler-exchange-httpd_recoup.h         |   23 +-
 ...l.c => taler-exchange-httpd_refreshes_reveal.c} |   94 +-
 ...l.h => taler-exchange-httpd_refreshes_reveal.h} |   23 +-
 src/exchange/taler-exchange-httpd_refund.c         |   39 +-
 src/exchange/taler-exchange-httpd_refund.h         |   23 +-
 ...tatus.c => taler-exchange-httpd_reserves_get.c} |   49 +-
 ...tatus.h => taler-exchange-httpd_reserves_get.h} |   23 +-
 src/exchange/taler-exchange-httpd_terms.c          |   28 +-
 src/exchange/taler-exchange-httpd_terms.h          |   21 +-
 ...sfer.c => taler-exchange-httpd_transfers_get.c} |   47 +-
 ...sfer.h => taler-exchange-httpd_transfers_get.h} |   15 +-
 src/exchange/taler-exchange-httpd_validation.c     |    1 -
 src/exchange/taler-exchange-httpd_wire.c           |   14 +-
 src/exchange/taler-exchange-httpd_wire.h           |   12 +-
 ..._withdraw.c => taler-exchange-httpd_withdraw.c} |   58 +-
 ..._withdraw.h => taler-exchange-httpd_withdraw.h} |   30 +-
 src/exchangedb/plugin_exchangedb_postgres.c        |   22 +-
 src/include/taler_error_codes.h                    |   70 +
 src/include/taler_exchange_service.h               |  480 +++---
 src/include/taler_exchangedb_plugin.h              |    6 +-
 src/include/taler_testing_lib.h                    |   18 +-
 src/lib/Makefile.am                                |   13 +-
 src/lib/exchange_api_common.c                      |  430 ++++-
 src/lib/exchange_api_curl_defaults.c               |    8 +-
 src/lib/exchange_api_curl_defaults.h               |    2 +-
 src/lib/exchange_api_deposit.c                     |   30 +-
 ...k_transaction.c => exchange_api_deposits_get.c} |  147 +-
 src/lib/exchange_api_handle.c                      |   26 +-
 ...ange_api_refresh_link.c => exchange_api_link.c} |  202 +--
 src/lib/exchange_api_melt.c                        |  505 ++++++
 src/lib/exchange_api_recoup.c                      |   26 +-
 src/lib/exchange_api_refresh.c                     | 1740 --------------------
 src/lib/exchange_api_refresh_common.c              |  631 +++++++
 src/lib/exchange_api_refresh_common.h              |  240 +++
 src/lib/exchange_api_refreshes_reveal.c            |  512 ++++++
 src/lib/exchange_api_refund.c                      |   32 +-
 src/lib/exchange_api_reserve.c                     | 1288 ---------------
 src/lib/exchange_api_reserves_get.c                |  308 ++++
 ...ack_transfer.c => exchange_api_transfers_get.c} |   95 +-
 src/lib/exchange_api_wire.c                        |    6 +-
 src/lib/exchange_api_withdraw.c                    |  611 +++++++
 src/testing/test_auditor_api.c                     |    8 +-
 src/testing/test_exchange_api.c                    |   24 +-
 src/testing/test_exchange_api_revocation.c         |   18 +-
 src/testing/test_exchange_api_twisted.c            |    2 +-
 .../testing_api_cmd_auditor_deposit_confirmation.c |    4 +-
 src/testing/testing_api_cmd_refresh.c              |   92 +-
 src/testing/testing_api_cmd_status.c               |   13 +-
 src/testing/testing_api_cmd_track.c                |   30 +-
 src/testing/testing_api_cmd_withdraw.c             |   16 +-
 src/testing/testing_api_loop.c                     |   10 +
 69 files changed, 4922 insertions(+), 4475 deletions(-)
 rename src/exchange/{taler-exchange-httpd_track_transaction.c => 
taler-exchange-httpd_deposits_get.c} (80%)
 rename src/exchange/{taler-exchange-httpd_track_transaction.h => 
taler-exchange-httpd_deposits_get.h} (69%)
 rename src/exchange/{taler-exchange-httpd_refresh_link.c => 
taler-exchange-httpd_link.c} (84%)
 rename src/exchange/{taler-exchange-httpd_refresh_link.h => 
taler-exchange-httpd_link.h} (69%)
 rename src/exchange/{taler-exchange-httpd_refresh_melt.c => 
taler-exchange-httpd_melt.c} (85%)
 rename src/exchange/{taler-exchange-httpd_refresh_melt.h => 
taler-exchange-httpd_melt.h} (55%)
 rename src/exchange/{taler-exchange-httpd_refresh_reveal.c => 
taler-exchange-httpd_refreshes_reveal.c} (93%)
 rename src/exchange/{taler-exchange-httpd_refresh_reveal.h => 
taler-exchange-httpd_refreshes_reveal.h} (67%)
 rename src/exchange/{taler-exchange-httpd_reserve_status.c => 
taler-exchange-httpd_reserves_get.c} (80%)
 rename src/exchange/{taler-exchange-httpd_reserve_status.h => 
taler-exchange-httpd_reserves_get.h} (63%)
 rename src/exchange/{taler-exchange-httpd_track_transfer.c => 
taler-exchange-httpd_transfers_get.c} (93%)
 rename src/exchange/{taler-exchange-httpd_track_transfer.h => 
taler-exchange-httpd_transfers_get.h} (69%)
 rename src/exchange/{taler-exchange-httpd_reserve_withdraw.c => 
taler-exchange-httpd_withdraw.c} (92%)
 rename src/exchange/{taler-exchange-httpd_reserve_withdraw.h => 
taler-exchange-httpd_withdraw.h} (58%)
 rename src/lib/{exchange_api_track_transaction.c => 
exchange_api_deposits_get.c} (68%)
 rename src/lib/{exchange_api_refresh_link.c => exchange_api_link.c} (71%)
 create mode 100644 src/lib/exchange_api_melt.c
 delete mode 100644 src/lib/exchange_api_refresh.c
 create mode 100644 src/lib/exchange_api_refresh_common.c
 create mode 100644 src/lib/exchange_api_refresh_common.h
 create mode 100644 src/lib/exchange_api_refreshes_reveal.c
 delete mode 100644 src/lib/exchange_api_reserve.c
 create mode 100644 src/lib/exchange_api_reserves_get.c
 rename src/lib/{exchange_api_track_transfer.c => exchange_api_transfers_get.c} 
(80%)
 create mode 100644 src/lib/exchange_api_withdraw.c

diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 1c6f82c8..d211bd94 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -3763,7 +3763,7 @@ refresh_session_cb (void *cls,
                               NULL);
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
-    report_row_inconsistency ("refresh_melt",
+    report_row_inconsistency ("melt",
                               rowid,
                               "denomination key not found");
     return GNUNET_OK;
diff --git a/src/benchmark/taler-exchange-benchmark.c 
b/src/benchmark/taler-exchange-benchmark.c
index 91ed5b04..7e1b8b72 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -374,8 +374,8 @@ run (void *cls,
                          i,
                          j);
         unit[2] =
-          TALER_TESTING_cmd_refresh_melt_with_retry
-            (TALER_TESTING_cmd_refresh_melt
+          TALER_TESTING_cmd_melt_with_retry
+            (TALER_TESTING_cmd_melt
               (add_label (melt_label),
               withdraw_label,
               MHD_HTTP_OK,
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index cf9f984d..61d3341c 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -49,20 +49,20 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd.c taler-exchange-httpd.h \
   taler-exchange-httpd_db.c taler-exchange-httpd_db.h \
   taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
+  taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
   taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \
+  taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
   taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
   taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
-  taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
-  taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
-  taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.h \
+  taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \
+  taler-exchange-httpd_refreshes_reveal.c 
taler-exchange-httpd_refreshes_reveal.h \
   taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
-  taler-exchange-httpd_reserve_status.c taler-exchange-httpd_reserve_status.h \
-  taler-exchange-httpd_reserve_withdraw.c 
taler-exchange-httpd_reserve_withdraw.h \
+  taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \
   taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
   taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
-  taler-exchange-httpd_track_transaction.c 
taler-exchange-httpd_track_transaction.h \
-  taler-exchange-httpd_track_transfer.c taler-exchange-httpd_track_transfer.h \
+  taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
   taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
+  taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h \
   taler-exchange-httpd_validation.c taler-exchange-httpd_validation.h
 taler_exchange_httpd_LDADD = \
   $(LIBGCRYPT_LIBS) \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 6f021d72..d353a796 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016, 2019 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -30,15 +30,15 @@
 #include "taler-exchange-httpd_mhd.h"
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_refund.h"
-#include "taler-exchange-httpd_reserve_status.h"
-#include "taler-exchange-httpd_reserve_withdraw.h"
+#include "taler-exchange-httpd_reserves_get.h"
+#include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_recoup.h"
-#include "taler-exchange-httpd_refresh_link.h"
-#include "taler-exchange-httpd_refresh_melt.h"
-#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_link.h"
+#include "taler-exchange-httpd_melt.h"
+#include "taler-exchange-httpd_refreshes_reveal.h"
 #include "taler-exchange-httpd_terms.h"
-#include "taler-exchange-httpd_track_transfer.h"
-#include "taler-exchange-httpd_track_transaction.h"
+#include "taler-exchange-httpd_transfers_get.h"
+#include "taler-exchange-httpd_deposits_get.h"
 #include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_wire.h"
 #include "taler_exchangedb_plugin.h"
@@ -147,6 +147,92 @@ static unsigned long long req_count;
 static unsigned long long req_max;
 
 
+/**
+ * Handle a "/coins/$COIN_PUB/$OP" POST request.  Parses the "coin_pub"
+ * EdDSA key of the coin and demultiplexes based on $OP.
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param root uploaded JSON data
+ * @param args array of additional options (first must be the
+ *         reserve public key, the second one should be "withdraw")
+ * @return MHD result code
+ */
+static int
+handle_post_coins (const struct TEH_RequestHandler *rh,
+                   struct MHD_Connection *connection,
+                   const json_t *root,
+                   const char *const args[2])
+{
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  static const struct
+  {
+    /**
+     * Name of the operation (args[1])
+     */
+    const char *op;
+
+    /**
+     * Function to call to perform the operation.
+     *
+     * @param connection the MHD connection to handle
+     * @param coin_pub the public key of the coin
+     * @param root uploaded JSON data
+     * @return MHD result code
+     *///
+    int
+    (*handler)(struct MHD_Connection *connection,
+               const struct TALER_CoinSpendPublicKeyP *coin_pub,
+               const json_t *root);
+  } h[] = {
+    {
+      .op = "deposit",
+      .handler = &TEH_DEPOSIT_handler_deposit
+    },
+    {
+      .op = "melt",
+      .handler = &TEH_REFRESH_handler_melt
+    },
+    {
+      .op = "recoup",
+      .handler = &TEH_RECOUP_handler_recoup
+    },
+    {
+      .op = "refund",
+      .handler = &TEH_REFUND_handler_refund
+    },
+    {
+      .op = NULL,
+      .handler = NULL
+    },
+  };
+
+  (void) rh;
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &coin_pub,
+                                     sizeof (coin_pub)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_COINS_INVALID_COIN_PUB,
+                                       "coin public key malformed");
+  }
+  for (unsigned int i = 0; NULL != h[i].op; i++)
+    if (0 == strcmp (h[i].op,
+                     args[1]))
+      return h[i].handler (connection,
+                           &coin_pub,
+                           root);
+  return TALER_MHD_reply_with_error (connection,
+                                     MHD_HTTP_NOT_FOUND,
+                                     TALER_EC_OPERATION_INVALID,
+                                     "requested operation on coin unknown");
+}
+
+
 /**
  * Function called whenever MHD is done with a request.  If the
  * request was a POST, we may have stored a `struct Buffer *` in the
@@ -205,6 +291,132 @@ is_valid_correlation_id (const char *correlation_id)
 }
 
 
+/**
+ * We found @a rh responsible for handling a request. Parse the
+ * @a upload_data (if applicable) and the @a url and call the
+ * handler.
+ *
+ * @param rh request handler to call
+ * @param connection connection being handled
+ * @param url rest of the URL to parse
+ * @param inner_cls closure for the handler, if needed
+ * @param upload_data upload data to parse (if available)
+ * @param upload_data_size[in,out] number of bytes in @a upload_data
+ * @return MHD result code
+ */
+static int
+proceed_with_handler (const struct TEH_RequestHandler *rh,
+                      struct MHD_Connection *connection,
+                      const char *url,
+                      void **inner_cls,
+                      const char *upload_data,
+                      size_t *upload_data_size)
+{
+  const char *args[rh->nargs + 1];
+  size_t ulen = strlen (url) + 1;
+  json_t *root = NULL;
+  int ret;
+
+  /* We do check for "ulen" here, because we'll later stack-allocate a buffer
+     of that size and don't want to enable malicious clients to cause us
+     huge stack allocations. */
+  if (ulen > 512)
+  {
+    /* 512 is simply "big enough", as it is bigger than "6 * 54",
+       which is the longest URL format we ever get (for
+       /deposits/).  The value should be adjusted if we ever define protocol
+       endpoints with plausibly longer inputs.  */
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_URI_TOO_LONG,
+                                       TALER_EC_URI_TOO_LONG,
+                                       "The URI given is too long");
+  }
+
+  /* All POST endpoints come with a body in JSON format. So we parse
+     the JSON here. */
+  if (0 == strcasecmp (rh->method,
+                       MHD_HTTP_METHOD_POST))
+  {
+    int res;
+
+    res = TALER_MHD_parse_post_json (connection,
+                                     inner_cls,
+                                     upload_data,
+                                     upload_data_size,
+                                     &root);
+    if (GNUNET_SYSERR == res)
+      return MHD_NO;
+    if ( (GNUNET_NO == res) || (NULL == root) )
+      return MHD_YES;
+  }
+
+  {
+    char d[ulen];
+
+    /* Parse command-line arguments, if applicable */
+    if (rh->nargs > 0)
+    {
+      unsigned int i;
+      const char *fin;
+
+      /* make a copy of 'url' because 'strtok()' will modify */
+      memcpy (d,
+              url,
+              ulen);
+      i = 0;
+      args[i++] = strtok (d, "/");
+      while ( (NULL != args[i - 1]) &&
+              (i < rh->nargs) )
+        args[i++] = strtok (NULL, "/");
+      /* make sure above loop ran nicely until completion, and also
+         that there is no excess data in 'd' afterwards */
+      if ( (i != rh->nargs) ||
+           (NULL == args[i - 1]) ||
+           (NULL != (fin = strtok (NULL, "/"))) )
+      {
+        char emsg[128 + 512];
+
+        GNUNET_snprintf (emsg,
+                         sizeof (emsg),
+                         "Got %u/%u segments for %s request ('%s')",
+                         (NULL == args[i - 1])
+                         ? i - 1
+                         : i + ((NULL != fin) ? 1 : 0),
+                         rh->nargs,
+                         rh->url,
+                         url);
+        GNUNET_break_op (0);
+        return TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_NOT_FOUND,
+                                           TALER_EC_WRONG_NUMBER_OF_SEGMENTS,
+                                           emsg);
+      }
+    }
+
+    /* just to be safe(r), we always terminate the array with a NULL
+       (which handlers should not read, but at least if they do, they'll
+       crash pretty reliably... */
+    args[rh->nargs] = NULL;
+
+    /* Above logic ensures that 'root' is exactly non-NULL for POST operations 
*/
+    if (NULL != root)
+      ret = rh->handler.post (rh,
+                              connection,
+                              root,
+                              args);
+    else /* and we only have "POST" or "GET" in the API for at this point
+            (OPTIONS/HEAD are taken care of earlier) */
+      ret = rh->handler.get (rh,
+                             connection,
+                             args);
+  }
+  if (NULL != root)
+    json_decref (root);
+  return ret;
+}
+
+
 /**
  * Handle incoming HTTP request.
  *
@@ -229,134 +441,111 @@ handle_mhd_request (void *cls,
                     void **con_cls)
 {
   static struct TEH_RequestHandler handlers[] = {
-    /* Landing page, tell humans to go away. */
-    { "/", MHD_HTTP_METHOD_GET, "text/plain",
-      "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n", 
0,
-      &TEH_MHD_handler_static_response, MHD_HTTP_OK },
     /* /robots.txt: disallow everything */
-    { "/robots.txt", MHD_HTTP_METHOD_GET, "text/plain",
-      "User-agent: *\nDisallow: /\n", 0,
-      &TEH_MHD_handler_static_response, MHD_HTTP_OK },
+    {
+      .url = "robots.txt",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_MHD_handler_static_response,
+      .mime_type = "text/plain",
+      .data = "User-agent: *\nDisallow: /\n",
+      .response_code = MHD_HTTP_OK
+    },
+    /* Landing page, tell humans to go away. */
+    {
+      .url = "",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = TEH_MHD_handler_static_response,
+      .mime_type = "text/plain",
+      .data =
+        "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n",
+      .response_code = MHD_HTTP_OK
+    },
     /* AGPL licensing page, redirect to source. As per the AGPL-license,
        every deployment is required to offer the user a download of the
        source. We make this easy by including a redirect to the source
        here. */
-    { "/agpl", MHD_HTTP_METHOD_GET, "text/plain",
-      NULL, 0,
-      &TEH_MHD_handler_agpl_redirect, MHD_HTTP_FOUND },
+    {
+      .url = "agpl",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_MHD_handler_agpl_redirect
+    },
     /* Terms of service */
-    { "/terms", MHD_HTTP_METHOD_GET, NULL,
-      NULL, 0,
-      &TEH_handler_terms, MHD_HTTP_OK },
+    {
+      .url = "terms",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_handler_terms
+    },
     /* Privacy policy */
-    { "/privacy", MHD_HTTP_METHOD_GET, NULL,
-      NULL, 0,
-      &TEH_handler_privacy, MHD_HTTP_OK },
+    {
+      .url = "privacy",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_handler_privacy
+    },
     /* Return key material and fundamental properties for this exchange */
-    { "/keys", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &TEH_KS_handler_keys, MHD_HTTP_OK },
-    { "/keys", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
+    {
+      .url = "keys",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_KS_handler_keys,
+    },
     /* Requests for wiring information */
-    { "/wire", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &TEH_WIRE_handler_wire, MHD_HTTP_OK },
-    { "/wire", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
+    {
+      .url = "wire",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_WIRE_handler_wire
+    },
     /* Withdrawing coins / interaction with reserves */
-    { "/reserve/status", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &TEH_RESERVE_handler_reserve_status, MHD_HTTP_OK },
-    { "/reserve/status", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { "/reserve/withdraw", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_RESERVE_handler_reserve_withdraw, MHD_HTTP_OK },
-    { "/reserve/withdraw", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    /* Depositing coins */
-    { "/deposit", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_DEPOSIT_handler_deposit, MHD_HTTP_OK },
-    { "/deposit", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    /* Refunding coins */
-    { "/refund", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_REFUND_handler_refund, MHD_HTTP_OK },
-    { "/refund", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    /* Dealing with change */
-    { "/refresh/melt", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_REFRESH_handler_refresh_melt, MHD_HTTP_OK },
-    { "/refresh/melt", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { "/refresh/reveal", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_REFRESH_handler_refresh_reveal, MHD_HTTP_OK },
-    { "/refresh/reveal", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { "/refresh/reveal", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_REFRESH_handler_refresh_reveal, MHD_HTTP_OK },
-    { "/refresh/reveal", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { "/refresh/link", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &TEH_REFRESH_handler_refresh_link, MHD_HTTP_OK },
-    { "/refresh/link", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { "/track/transfer", MHD_HTTP_METHOD_GET, "application/json",
-      NULL, 0,
-      &TEH_TRACKING_handler_track_transfer, MHD_HTTP_OK },
-    { "/track/transfer", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-    { "/track/transaction", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_TRACKING_handler_track_transaction, MHD_HTTP_OK },
-    { "/track/transaction", NULL, "text/plain",
-      "Only POST is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { "/recoup", MHD_HTTP_METHOD_POST, "application/json",
-      NULL, 0,
-      &TEH_RECOUP_handler_recoup, MHD_HTTP_OK },
-    { "/refresh/link", NULL, "text/plain",
-      "Only GET is allowed", 0,
-      &TEH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
-    { NULL, NULL, NULL, NULL, 0, NULL, 0 }
-  };
-  static struct TEH_RequestHandler h404 = {
-    "", NULL, "text/html",
-    "<html><title>404: not found</title></html>", 0,
-    &TEH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
+    {
+      .url = "reserves",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_RESERVE_handler_reserve_status,
+      .nargs = 1
+    },
+    {
+      .url = "reserves",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler.post = &TEH_RESERVE_handler_reserve_withdraw,
+      .nargs = 2
+    },
+    /* coins */
+    {
+      .url = "coins",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler.post = &handle_post_coins,
+      .nargs = 2
+    },
+    {
+      .url = "coins",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = TEH_REFRESH_handler_link,
+      .nargs = 2,
+    },
+    /* refreshing */
+    {
+      .url = "refreshes",
+      .method = MHD_HTTP_METHOD_POST,
+      .handler.post = &TEH_REFRESH_handler_reveal,
+      .nargs = 2
+    },
+    /* tracking transfers */
+    {
+      .url = "transfers",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_TRACKING_handler_track_transfer,
+      .nargs = 1
+    },
+    /* tracking deposits */
+    {
+      .url = "deposits",
+      .method = MHD_HTTP_METHOD_GET,
+      .handler.get = &TEH_TRACKING_handler_track_transaction,
+      .nargs = 4
+    },
+    /* mark end of list */
+    {
+      .url = NULL
+    }
   };
   struct ExchangeHttpRequestClosure *ecls = *con_cls;
-  int ret;
   void **inner_cls;
   struct GNUNET_AsyncScopeSave old_scope;
   const char *correlation_id = NULL;
@@ -367,7 +556,8 @@ handle_mhd_request (void *cls,
   {
     unsigned long long cnt;
 
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Handling new request\n");
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Handling new request\n");
     cnt = __sync_add_and_fetch (&req_count, 1LLU);
     if (req_max == cnt)
     {
@@ -395,7 +585,8 @@ handle_mhd_request (void *cls,
   }
 
   inner_cls = &ecls->opaque_post_parsing_context;
-  GNUNET_async_scope_enter (&ecls->async_scope_id, &old_scope);
+  GNUNET_async_scope_enter (&ecls->async_scope_id,
+                            &old_scope);
   if (NULL != correlation_id)
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Handling request (%s) for URL '%s', correlation_id=%s\n",
@@ -410,55 +601,105 @@ handle_mhd_request (void *cls,
   /* on repeated requests, check our cache first */
   if (NULL != ecls->rh)
   {
-    ret = ecls->rh->handler (ecls->rh,
-                             connection,
-                             inner_cls,
-                             upload_data,
-                             upload_data_size);
+    int ret;
+    const char *start;
+
+    if ('\0' == url[0])
+      /* strange, should start with '/', treat as just "/" */
+      url = "/";
+    start = strchr (url + 1, '/');
+    ret = proceed_with_handler (ecls->rh,
+                                connection,
+                                start,
+                                inner_cls,
+                                upload_data,
+                                upload_data_size);
     GNUNET_async_scope_restore (&old_scope);
     return ret;
   }
+
   if (0 == strcasecmp (method,
                        MHD_HTTP_METHOD_HEAD))
-    method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the 
rest */
-  for (unsigned int i = 0; NULL != handlers[i].url; i++)
-  {
-    struct TEH_RequestHandler *rh = &handlers[i];
-
-    if (0 != strcmp (url, rh->url))
-      continue;
+    method = MHD_HTTP_METHOD_GET;   /* treat HEAD as GET here, MHD will do the 
rest */
 
-    /* The URL is a match!  What we now do depends on the method. */
-    if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS))
+  /* parse first part of URL */
+  {
+    int found = GNUNET_NO;
+    size_t tok_size;
+    const char *tok;
+    const char *rest;
+
+    if ('\0' == url[0])
+      /* strange, should start with '/', treat as just "/" */
+      url = "/";
+    tok = url + 1;
+    rest = strchr (tok, '/');
+    if (NULL == rest)
+    {
+      tok_size = strlen (tok);
+    }
+    else
+    {
+      tok_size = rest - tok;
+      rest++; /* skip over '/' */
+    }
+    for (unsigned int i = 0; NULL != handlers[i].url; i++)
     {
-      GNUNET_async_scope_restore (&old_scope);
-      return TALER_MHD_reply_cors_preflight (connection);
+      struct TEH_RequestHandler *rh = &handlers[i];
+
+      if (0 != strncmp (tok,
+                        rh->url,
+                        tok_size))
+        continue;
+      found = GNUNET_YES;
+      /* The URL is a match!  What we now do depends on the method. */
+      if (0 == strcasecmp (method, MHD_HTTP_METHOD_OPTIONS))
+      {
+        GNUNET_async_scope_restore (&old_scope);
+        return TALER_MHD_reply_cors_preflight (connection);
+      }
+      GNUNET_assert (NULL != rh->method);
+      if (0 == strcasecmp (method,
+                           rh->method))
+      {
+        int ret;
+
+        /* cache to avoid the loop next time */
+        ecls->rh = rh;
+        /* run handler */
+        ret = proceed_with_handler (rh,
+                                    connection,
+                                    url + tok_size + 1,
+                                    inner_cls,
+                                    upload_data,
+                                    upload_data_size);
+        GNUNET_async_scope_restore (&old_scope);
+        return ret;
+      }
     }
 
-    if ( (NULL == rh->method) ||
-         (0 == strcasecmp (method,
-                           rh->method)) )
+    if (GNUNET_YES == found)
     {
-      /* cache to avoid the loop next time */
-      ecls->rh = rh;
-      /* run handler */
-      ret = rh->handler (rh,
-                         connection,
-                         inner_cls,
-                         upload_data,
-                         upload_data_size);
-      GNUNET_async_scope_restore (&old_scope);
-      return ret;
+      /* we found a matching address, but the method is wrong */
+      GNUNET_break_op (0);
+      return TALER_MHD_reply_with_error (connection,
+                                         MHD_HTTP_METHOD_NOT_ALLOWED,
+                                         TALER_EC_METHOD_INVALID,
+                                         "The HTTP method used is invalid for 
this URL");
     }
   }
+
   /* No handler matches, generate not found */
-  ret = TEH_MHD_handler_static_response (&h404,
-                                         connection,
-                                         inner_cls,
-                                         upload_data,
-                                         upload_data_size);
-  GNUNET_async_scope_restore (&old_scope);
-  return ret;
+  {
+    int ret;
+
+    ret = TALER_MHD_reply_with_error (connection,
+                                      MHD_HTTP_NOT_FOUND,
+                                      TALER_EC_ENDPOINT_UNKNOWN,
+                                      "No handler found for the given URL");
+    GNUNET_async_scope_restore (&old_scope);
+    return ret;
+  }
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd.h 
b/src/exchange/taler-exchange-httpd.h
index 38c611c6..8489d179 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -24,6 +24,8 @@
 #define TALER_EXCHANGE_HTTPD_H
 
 #include <microhttpd.h>
+#include "taler_json_lib.h"
+#include "taler_crypto_lib.h"
 
 
 /**
@@ -65,51 +67,77 @@ struct TEH_RequestHandler
 {
 
   /**
-   * URL the handler is for.
+   * URL the handler is for (first part only).
    */
   const char *url;
 
   /**
-   * Method the handler is for, NULL for "all".
+   * Method the handler is for.
    */
   const char *method;
 
+  /**
+   * Callbacks for handling of the request. Which one is used
+   * depends on @e method.
+   */
+  union
+  {
+    /**
+     * Function to call to handle a GET requests (and those
+     * with @e method NULL).
+     *
+     * @param rh this struct
+     * @param mime_type the @e mime_type for the reply (hint, can be NULL)
+     * @param connection the MHD connection to handle
+     * @param args array of arguments, needs to be of length @e args_expected
+     * @return MHD result code
+     */
+    int (*get)(const struct TEH_RequestHandler *rh,
+               struct MHD_Connection *connection,
+               const char *const args[]);
+
+
+    /**
+     * Function to call to handle a POST request.
+     *
+     * @param rh this struct
+     * @param mime_type the @e mime_type for the reply (hint, can be NULL)
+     * @param connection the MHD connection to handle
+     * @param json uploaded JSON data
+     * @param args array of arguments, needs to be of length @e args_expected
+     * @return MHD result code
+     */
+    int (*post)(const struct TEH_RequestHandler *rh,
+                struct MHD_Connection *connection,
+                const json_t *root,
+                const char *const args[]);
+
+  } handler;
+
+  /**
+   * Number of arguments this handler expects in the @a args array.
+   */
+  unsigned int nargs;
+
   /**
    * Mime type to use in reply (hint, can be NULL).
    */
   const char *mime_type;
 
   /**
-   * Raw data for the @e handler
+   * Raw data for the @e handler, can be NULL for none provided.
    */
   const void *data;
 
   /**
-   * Number of bytes in @e data, 0 for 0-terminated.
+   * Number of bytes in @e data, 0 for data is 0-terminated (!).
    */
   size_t data_size;
 
   /**
-   * Function to call to handle the request.
-   *
-   * @param rh this struct
-   * @param mime_type the @e mime_type for the reply (hint, can be NULL)
-   * @param connection the MHD connection to handle
-   * @param[in,out] connection_cls the connection's closure (can be updated)
-   * @param upload_data upload data
-   * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
-   * @return MHD result code
-   */
-  int (*handler)(struct TEH_RequestHandler *rh,
-                 struct MHD_Connection *connection,
-                 void **connection_cls,
-                 const char *upload_data,
-                 size_t *upload_data_size);
-
-  /**
-   * Default response code.
+   * Default response code. 0 for none provided.
    */
-  int response_code;
+  unsigned int response_code;
 };
 
 
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index 49b9cc2f..da89ff47 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -381,27 +381,22 @@ check_timestamp_current (struct GNUNET_TIME_Absolute ts)
 
 
 /**
- * Handle a "/deposit" request.  Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_deposit() to further
- * check the details of the operation specified.  If everything checks
+ * Handle a "/coins/$COIN_PUB/deposit" request.  Parses the JSON, and, if
+ * successful, passes the JSON data to #verify_and_execute_deposit() to
+ * further check the details of the operation specified.  If everything checks
  * out, this will ultimately lead to the "/deposit" being executed, or
  * rejected.
  *
- * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
   */
 int
-TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
-                             struct MHD_Connection *connection,
-                             void **connection_cls,
-                             const char *upload_data,
-                             size_t *upload_data_size)
+TEH_DEPOSIT_handler_deposit (struct MHD_Connection *connection,
+                             const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                             const json_t *root)
 {
-  json_t *json;
   int res;
   json_t *wire;
   enum TALER_ErrorCode ec;
@@ -415,7 +410,6 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
                                  &deposit.coin.denom_pub_hash),
     TALER_JSON_spec_denomination_signature ("ub_sig", &deposit.coin.denom_sig),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &deposit.coin.coin_pub),
     GNUNET_JSON_spec_fixed_auto ("merchant_pub", &deposit.merchant_pub),
     GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 
&deposit.h_contract_terms),
     GNUNET_JSON_spec_fixed_auto ("h_wire", &deposit.h_wire),
@@ -428,27 +422,13 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler 
*rh,
     GNUNET_JSON_spec_end ()
   };
 
-  (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &json);
-  if (GNUNET_SYSERR == res)
-  {
-    GNUNET_break (0);
-    return MHD_NO;
-  }
-  if ( (GNUNET_NO == res) ||
-       (NULL == json) )
-    return MHD_YES;
   memset (&deposit,
           0,
           sizeof (deposit));
+  deposit.coin.coin_pub = *coin_pub;
   res = TALER_MHD_parse_json_data (connection,
-                                   json,
+                                   root,
                                    spec);
-  json_decref (json);
   if (GNUNET_SYSERR == res)
   {
     GNUNET_break (0);
diff --git a/src/exchange/taler-exchange-httpd_deposit.h 
b/src/exchange/taler-exchange-httpd_deposit.h
index ed1f87d5..23c46c28 100644
--- a/src/exchange/taler-exchange-httpd_deposit.h
+++ b/src/exchange/taler-exchange-httpd_deposit.h
@@ -29,22 +29,21 @@
 
 
 /**
- * Handle a "/deposit" request.  Parses the JSON, and, if successful,
- * checks the signatures.  If everything checks out, this will
- * ultimately lead to the "/deposit" being executed, or rejected.
+ * Handle a "/coins/$COIN_PUB/deposit" request.  Parses the JSON, and, if
+ * successful, passes the JSON data to #verify_and_execute_deposit() to
+ * further check the details of the operation specified.  If everything checks
+ * out, this will ultimately lead to the "/deposit" being executed, or
+ * rejected.
  *
- * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
   */
 int
-TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
-                             struct MHD_Connection *connection,
-                             void **connection_cls,
-                             const char *upload_data,
-                             size_t *upload_data_size);
+TEH_DEPOSIT_handler_deposit (struct MHD_Connection *connection,
+                             const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                             const json_t *root);
+
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_track_transaction.c 
b/src/exchange/taler-exchange-httpd_deposits_get.c
similarity index 80%
rename from src/exchange/taler-exchange-httpd_track_transaction.c
rename to src/exchange/taler-exchange-httpd_deposits_get.c
index e8143213..5e91bc4f 100644
--- a/src/exchange/taler-exchange-httpd_track_transaction.c
+++ b/src/exchange/taler-exchange-httpd_deposits_get.c
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_track_transaction.c
+ * @file taler-exchange-httpd_deposits_get.c
  * @brief Handle wire transfer tracking-related requests
  * @author Christian Grothoff
  */
@@ -27,7 +27,7 @@
 #include "taler_mhd_lib.h"
 #include "taler_signatures.h"
 #include "taler-exchange-httpd_keystate.h"
-#include "taler-exchange-httpd_track_transaction.h"
+#include "taler-exchange-httpd_deposits_get.h"
 #include "taler-exchange-httpd_responses.h"
 
 
@@ -336,63 +336,87 @@ check_and_handle_track_transaction_request (struct 
MHD_Connection *connection,
 
 
 /**
- * Handle a "/track/transaction" request.
+ * Handle a "/deposits/$H_WIRE/$MERCHANT_PUB/$H_CONTRACT_TERMS/$COIN_PUB"
+ * request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 4, contains:
+ *      h_wire, merchant_pub, h_contract_terms and coin_pub)
  * @return MHD result code
- */
+  */
 int
-TEH_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
+TEH_TRACKING_handler_track_transaction (const struct TEH_RequestHandler *rh,
                                         struct MHD_Connection *connection,
-                                        void **connection_cls,
-                                        const char *upload_data,
-                                        size_t *upload_data_size)
+                                        const char *const args[4])
 {
   int res;
-  json_t *json;
   struct TALER_DepositTrackPS tps;
   struct TALER_MerchantSignatureP merchant_sig;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("h_wire", &tps.h_wire),
-    GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &tps.h_contract_terms),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &tps.coin_pub),
-    GNUNET_JSON_spec_fixed_auto ("merchant_pub", &tps.merchant),
-    GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig),
-    GNUNET_JSON_spec_end ()
-  };
-
-  (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &json);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == json) )
-    return MHD_YES;
-  res = TALER_MHD_parse_json_data (connection,
-                                   json,
-                                   spec);
-  if (GNUNET_OK != res)
+
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &tps.h_wire,
+                                     sizeof (tps.h_wire)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_DEPOSITS_INVALID_H_WIRE,
+                                       "wire hash malformed");
+  }
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[1],
+                                     strlen (args[1]),
+                                     &tps.merchant,
+                                     sizeof (tps.merchant)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_DEPOSITS_INVALID_MERCHANT_PUB,
+                                       "merchant public key malformed");
+  }
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[2],
+                                     strlen (args[2]),
+                                     &tps.h_contract_terms,
+                                     sizeof (tps.h_contract_terms)))
   {
-    json_decref (json);
-    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       
TALER_EC_DEPOSITS_INVALID_H_CONTRACT_TERMS,
+                                       "contract terms hash malformed");
   }
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[3],
+                                     strlen (args[3]),
+                                     &tps.coin_pub,
+                                     sizeof (tps.coin_pub)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_DEPOSITS_INVALID_COIN_PUB,
+                                       "coin public key malformed");
+  }
+  res = TALER_MHD_parse_request_arg_data (connection,
+                                          "merchant_sig",
+                                          &merchant_sig,
+                                          sizeof (merchant_sig));
+  if (GNUNET_SYSERR == res)
+    return MHD_NO; /* internal error */
+  if (GNUNET_NO == res)
+    return MHD_YES; /* parse error */
   tps.purpose.size = htonl (sizeof (struct TALER_DepositTrackPS));
   tps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION);
-  res = check_and_handle_track_transaction_request (connection,
-                                                    &tps,
-                                                    &tps.merchant,
-                                                    &merchant_sig);
-  GNUNET_JSON_parse_free (spec);
-  json_decref (json);
-  return res;
+  return check_and_handle_track_transaction_request (connection,
+                                                     &tps,
+                                                     &tps.merchant,
+                                                     &merchant_sig);
 }
 
 
-/* end of taler-exchange-httpd_track_transaction.c */
+/* end of taler-exchange-httpd_deposits_get.c */
diff --git a/src/exchange/taler-exchange-httpd_track_transaction.h 
b/src/exchange/taler-exchange-httpd_deposits_get.h
similarity index 69%
rename from src/exchange/taler-exchange-httpd_track_transaction.h
rename to src/exchange/taler-exchange-httpd_deposits_get.h
index 929ee638..27f57e67 100644
--- a/src/exchange/taler-exchange-httpd_track_transaction.h
+++ b/src/exchange/taler-exchange-httpd_deposits_get.h
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_track_transaction.h
+ * @file taler-exchange-httpd_deposits_get.h
  * @brief Handle wire transfer tracking-related requests
  * @author Christian Grothoff
  */
@@ -27,21 +27,19 @@
 
 
 /**
- * Handle a "/track/transaction" request.
+ * Handle a "/deposits/$H_WIRE/$MERCHANT_PUB/$H_CONTRACT_TERMS/$COIN_PUB"
+ * request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 4, contains:
+ *      h_wire, merchant_pub, h_contract_terms and coin_pub)
  * @return MHD result code
   */
 int
-TEH_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
+TEH_TRACKING_handler_track_transaction (const struct TEH_RequestHandler *rh,
                                         struct MHD_Connection *connection,
-                                        void **connection_cls,
-                                        const char *upload_data,
-                                        size_t *upload_data_size);
+                                        const char *const args[4]);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index 27f22925..f0ab2a0d 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -2381,17 +2381,13 @@ krd_search_comparator (const void *key,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
+TEH_KS_handler_keys (const struct TEH_RequestHandler *rh,
                      struct MHD_Connection *connection,
-                     void **connection_cls,
-                     const char *upload_data,
-                     size_t *upload_data_size)
+                     const char *const args[])
 {
   int ret;
   const char *have_cherrypick;
@@ -2400,9 +2396,8 @@ TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
   struct GNUNET_TIME_Absolute now;
   const struct KeysResponseData *krd;
 
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
+  (void) rh;
+  (void) args;
   have_cherrypick = MHD_lookup_connection_value (connection,
                                                  MHD_GET_ARGUMENT_KIND,
                                                  "last_issue_date");
@@ -2493,7 +2488,7 @@ TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
                                          "no key response found");
     }
     ret = MHD_queue_response (connection,
-                              rh->response_code,
+                              MHD_HTTP_OK,
                               (MHD_YES == TALER_MHD_can_compress (connection))
                               ? krd->response_compressed
                               : krd->response_uncompressed);
diff --git a/src/exchange/taler-exchange-httpd_keystate.h 
b/src/exchange/taler-exchange-httpd_keystate.h
index ebcefa08..a6906096 100644
--- a/src/exchange/taler-exchange-httpd_keystate.h
+++ b/src/exchange/taler-exchange-httpd_keystate.h
@@ -188,17 +188,13 @@ TEH_KS_sign (const struct 
GNUNET_CRYPTO_EccSignaturePurpose *purpose,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
-  */
+ */
 int
-TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
+TEH_KS_handler_keys (const struct TEH_RequestHandler *rh,
                      struct MHD_Connection *connection,
-                     void **connection_cls,
-                     const char *upload_data,
-                     size_t *upload_data_size);
+                     const char *const args[]);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c 
b/src/exchange/taler-exchange-httpd_link.c
similarity index 84%
rename from src/exchange/taler-exchange-httpd_refresh_link.c
rename to src/exchange/taler-exchange-httpd_link.c
index 5e436091..83d7f6a0 100644
--- a/src/exchange/taler-exchange-httpd_refresh_link.c
+++ b/src/exchange/taler-exchange-httpd_link.c
@@ -26,7 +26,7 @@
 #include <microhttpd.h>
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_link.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -172,43 +172,37 @@ refresh_link_transaction (void *cls,
 
 
 /**
- * Handle a "/refresh/link" request.  Note that for "/refresh/link"
- * we do use a simple HTTP GET, and a HTTP POST!
+ * Handle a "/coins/$COIN_PUB/link" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 2, first is the coin_pub, 
second must be "link")
  * @return MHD result code
   */
 int
-TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size)
+TEH_REFRESH_handler_link (const struct TEH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          const char *const args[2])
 {
-  int mhd_ret;
-  int res;
   struct HTD_Context ctx;
+  int mhd_ret;
 
   (void) rh;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
   memset (&ctx,
           0,
           sizeof (ctx));
-  res = TALER_MHD_parse_request_arg_data (connection,
-                                          "coin_pub",
-                                          &ctx.coin_pub,
-                                          sizeof (struct
-                                                  TALER_CoinSpendPublicKeyP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if (GNUNET_OK != res)
-    return MHD_YES;
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &ctx.coin_pub,
+                                     sizeof (ctx.coin_pub)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_COINS_INVALID_COIN_PUB,
+                                       "coin public key malformed");
+  }
   ctx.mlist = json_array ();
   if (GNUNET_OK !=
       TEH_DB_run_transaction (connection,
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.h 
b/src/exchange/taler-exchange-httpd_link.h
similarity index 69%
rename from src/exchange/taler-exchange-httpd_refresh_link.h
rename to src/exchange/taler-exchange-httpd_link.h
index d0fcff33..9469c471 100644
--- a/src/exchange/taler-exchange-httpd_refresh_link.h
+++ b/src/exchange/taler-exchange-httpd_link.h
@@ -29,21 +29,17 @@
 
 
 /**
- * Handle a "/refresh/link" request
+ * Handle a "/coins/$COIN_PUB/link" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 2, first is the coin_pub, 
second must be "link")
  * @return MHD result code
   */
 int
-TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size);
+TEH_REFRESH_handler_link (const struct TEH_RequestHandler *rh,
+                          struct MHD_Connection *connection,
+                          const char *const args[2]);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c 
b/src/exchange/taler-exchange-httpd_melt.c
similarity index 85%
rename from src/exchange/taler-exchange-httpd_refresh_melt.c
rename to src/exchange/taler-exchange-httpd_melt.c
index c7dc700f..035fc48f 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh_melt.c
+ * @file taler-exchange-httpd_melt.c
  * @brief Handle /refresh/melt requests
  * @author Florian Dold
  * @author Benedikt Mueller
@@ -27,7 +27,7 @@
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_melt.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -48,14 +48,14 @@
  * @return a MHD result code
  */
 static int
-reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
-                                       const struct
-                                       TALER_CoinSpendPublicKeyP *coin_pub,
-                                       const struct TALER_Amount *coin_value,
-                                       struct TALER_EXCHANGEDB_TransactionList 
*
-                                       tl,
-                                       const struct TALER_Amount *requested,
-                                       const struct TALER_Amount *residual)
+reply_melt_insufficient_funds (struct MHD_Connection *connection,
+                               const struct
+                               TALER_CoinSpendPublicKeyP *coin_pub,
+                               const struct TALER_Amount *coin_value,
+                               struct TALER_EXCHANGEDB_TransactionList *
+                               tl,
+                               const struct TALER_Amount *requested,
+                               const struct TALER_Amount *residual)
 {
   json_t *history;
 
@@ -96,9 +96,9 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection 
*connection,
  * @return a MHD status code
  */
 static int
-reply_refresh_melt_success (struct MHD_Connection *connection,
-                            const struct TALER_RefreshCommitmentP *rc,
-                            uint32_t noreveal_index)
+reply_melt_success (struct MHD_Connection *connection,
+                    const struct TALER_RefreshCommitmentP *rc,
+                    uint32_t noreveal_index)
 {
   struct TALER_RefreshMeltConfirmationPS body;
   struct TALER_ExchangePublicKeyP pub;
@@ -139,7 +139,7 @@ struct RefreshMeltContext
 
   /**
    * noreveal_index is only initialized during
-   * #refresh_melt_transaction().
+   * #melt_transaction().
    */
   struct TALER_EXCHANGEDB_RefreshSession refresh_session;
 
@@ -253,14 +253,14 @@ refresh_check_melt (struct MHD_Connection *connection,
                    TALER_amount_subtract (&coin_residual,
                                           &spent,
                                           
&rmc->refresh_session.amount_with_fee));
-    *mhd_ret = reply_refresh_melt_insufficient_funds (connection,
-                                                      
&rmc->refresh_session.coin
-                                                      .coin_pub,
-                                                      &rmc->coin_value,
-                                                      tl,
-                                                      &rmc->refresh_session.
-                                                      amount_with_fee,
-                                                      &coin_residual);
+    *mhd_ret = reply_melt_insufficient_funds (connection,
+                                              &rmc->refresh_session.coin
+                                              .coin_pub,
+                                              &rmc->coin_value,
+                                              tl,
+                                              &rmc->refresh_session.
+                                              amount_with_fee,
+                                              &coin_residual);
     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
                                             tl);
     return GNUNET_DB_STATUS_HARD_ERROR;
@@ -294,10 +294,10 @@ refresh_check_melt (struct MHD_Connection *connection,
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
-refresh_melt_transaction (void *cls,
-                          struct MHD_Connection *connection,
-                          struct TALER_EXCHANGEDB_Session *session,
-                          int *mhd_ret)
+melt_transaction (void *cls,
+                  struct MHD_Connection *connection,
+                  struct TALER_EXCHANGEDB_Session *session,
+                  int *mhd_ret)
 {
   struct RefreshMeltContext *rmc = cls;
   enum GNUNET_DB_QueryStatus qs;
@@ -311,9 +311,9 @@ refresh_melt_transaction (void *cls,
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
     TALER_LOG_DEBUG ("Found already-melted coin\n");
-    *mhd_ret = reply_refresh_melt_success (connection,
-                                           &rmc->refresh_session.rc,
-                                           noreveal_index);
+    *mhd_ret = reply_melt_success (connection,
+                                   &rmc->refresh_session.rc,
+                                   noreveal_index);
     /* Note: we return "hard error" to ensure the wrapper
        does not retry the transaction, and to also not generate
        a "fresh" response (as we would on "success") */
@@ -366,15 +366,15 @@ refresh_melt_transaction (void *cls,
  * happened.  We now need to validate the coins being melted and the
  * session signature and then hand things of to execute the melt
  * operation.  This function parses the JSON arrays and then passes
- * processing on to #refresh_melt_transaction().
+ * processing on to #melt_transaction().
  *
  * @param connection the MHD connection to handle
  * @param[in,out] rmc details about the melt request
  * @return MHD result code
  */
 static int
-handle_refresh_melt (struct MHD_Connection *connection,
-                     struct RefreshMeltContext *rmc)
+handle_melt (struct MHD_Connection *connection,
+             struct RefreshMeltContext *rmc)
 {
 
   /* verify signature of coin for melt operation */
@@ -415,15 +415,15 @@ handle_refresh_melt (struct MHD_Connection *connection,
         TEH_DB_run_transaction (connection,
                                 "run melt",
                                 &mhd_ret,
-                                &refresh_melt_transaction,
+                                &melt_transaction,
                                 rmc))
       return mhd_ret;
   }
 
   /* generate ordinary response */
-  return reply_refresh_melt_success (connection,
-                                     &rmc->refresh_session.rc,
-                                     rmc->refresh_session.noreveal_index);
+  return reply_melt_success (connection,
+                             &rmc->refresh_session.rc,
+                             rmc->refresh_session.noreveal_index);
 }
 
 
@@ -571,37 +571,30 @@ check_for_denomination_key (struct MHD_Connection 
*connection,
                                        
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
                                        "melt amount smaller than melting fee");
   }
-  return handle_refresh_melt (connection,
-                              rmc);
+  return handle_melt (connection,
+                      rmc);
 }
 
 
 /**
- * Handle a "/refresh/melt" request.  Parses the request into the JSON
- * components and then hands things of to #check_for_denomination_key()
- * to validate the melted coins, the signature and execute the melt
- * using handle_refresh_melt().
- *
- * @param rh context of the handler
+ * Handle a "/coins/$COIN_PUB/melt" request.  Parses the request into the JSON
+ * components and then hands things of to #check_for_denomination_key() to
+ * validate the melted coins, the signature and execute the melt using
+ * handle_melt().
+
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
  */
 int
-TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size)
+TEH_REFRESH_handler_melt (struct MHD_Connection *connection,
+                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                          const json_t *root)
 {
-  json_t *root;
   struct RefreshMeltContext rmc;
   int res;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                 &rmc.refresh_session.coin.coin_pub),
     TALER_JSON_spec_denomination_signature ("denom_sig",
                                             
&rmc.refresh_session.coin.denom_sig),
     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
@@ -615,25 +608,13 @@ TEH_REFRESH_handler_refresh_melt (struct 
TEH_RequestHandler *rh,
     GNUNET_JSON_spec_end ()
   };
 
-  (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) ||
-       (NULL == root) )
-    return MHD_YES;
-
   memset (&rmc,
           0,
           sizeof (rmc));
+  rmc.refresh_session.coin.coin_pub = *coin_pub;
   res = TALER_MHD_parse_json_data (connection,
                                    root,
                                    spec);
-  json_decref (root);
   if (GNUNET_OK != res)
     return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
 
@@ -644,4 +625,4 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler 
*rh,
 }
 
 
-/* end of taler-exchange-httpd_refresh_melt.c */
+/* end of taler-exchange-httpd_melt.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.h 
b/src/exchange/taler-exchange-httpd_melt.h
similarity index 55%
rename from src/exchange/taler-exchange-httpd_refresh_melt.h
rename to src/exchange/taler-exchange-httpd_melt.h
index c50fdcb4..daf8ffc8 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.h
+++ b/src/exchange/taler-exchange-httpd_melt.h
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh_melt.h
+ * @file taler-exchange-httpd_melt.h
  * @brief Handle /refresh/melt requests
  * @author Florian Dold
  * @author Benedikt Mueller
@@ -29,25 +29,20 @@
 
 
 /**
- * Handle a "/refresh/melt" request after the first parsing has
- * happened.  We now need to validate the coins being melted and the
- * session signature and then hand things of to execute the melt
- * operation.  This function parses the JSON arrays and then passes
- * processing on to #refresh_melt_transaction().
- *
- * @param rh context of the handler
+ * Handle a "/coins/$COIN_PUB/melt" request.  Parses the request into the JSON
+ * components and then hands things of to #check_for_denomination_key() to
+ * validate the melted coins, the signature and execute the melt using
+ * handle_melt().
+
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
  */
 int
-TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size);
+TEH_REFRESH_handler_melt (struct MHD_Connection *connection,
+                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                          const json_t *root);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_mhd.c 
b/src/exchange/taler-exchange-httpd_mhd.c
index 0f2ce033..0d59fad1 100644
--- a/src/exchange/taler-exchange-httpd_mhd.c
+++ b/src/exchange/taler-exchange-httpd_mhd.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -39,27 +39,23 @@
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_MHD_handler_static_response (struct TEH_RequestHandler *rh,
+TEH_MHD_handler_static_response (const struct TEH_RequestHandler *rh,
                                  struct MHD_Connection *connection,
-                                 void **connection_cls,
-                                 const char *upload_data,
-                                 size_t *upload_data_size)
+                                 const char *const args[])
 {
   struct MHD_Response *response;
   int ret;
+  size_t dlen;
 
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
-  if (0 == rh->data_size)
-    rh->data_size = strlen ((const char *) rh->data);
-  response = MHD_create_response_from_buffer (rh->data_size,
+  (void) args;
+  dlen = (0 == rh->data_size)
+         ? strlen ((const char *) rh->data)
+         : rh->data_size;
+  response = MHD_create_response_from_buffer (dlen,
                                               (void *) rh->data,
                                               MHD_RESPMEM_PERSISTENT);
   if (NULL == response)
@@ -86,22 +82,16 @@ TEH_MHD_handler_static_response (struct TEH_RequestHandler 
*rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_MHD_handler_agpl_redirect (struct TEH_RequestHandler *rh,
+TEH_MHD_handler_agpl_redirect (const struct TEH_RequestHandler *rh,
                                struct MHD_Connection *connection,
-                               void **connection_cls,
-                               const char *upload_data,
-                               size_t *upload_data_size)
+                               const char *const args[])
 {
   (void) rh;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
+  (void) args;
   return TALER_MHD_reply_agpl (connection,
                                "http://www.git.taler.net/?p=exchange.git";);
 }
@@ -113,21 +103,15 @@ TEH_MHD_handler_agpl_redirect (struct TEH_RequestHandler 
*rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_MHD_handler_send_json_pack_error (struct TEH_RequestHandler *rh,
+TEH_MHD_handler_send_json_pack_error (const struct TEH_RequestHandler *rh,
                                       struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size)
+                                      const char *const args[])
 {
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
+  (void) args;
   return TALER_MHD_reply_with_error (connection,
                                      rh->response_code,
                                      TALER_EC_METHOD_INVALID,
diff --git a/src/exchange/taler-exchange-httpd_mhd.h 
b/src/exchange/taler-exchange-httpd_mhd.h
index 16fc5a01..0364f046 100644
--- a/src/exchange/taler-exchange-httpd_mhd.h
+++ b/src/exchange/taler-exchange-httpd_mhd.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -34,17 +34,13 @@
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_MHD_handler_static_response (struct TEH_RequestHandler *rh,
+TEH_MHD_handler_static_response (const struct TEH_RequestHandler *rh,
                                  struct MHD_Connection *connection,
-                                 void **connection_cls,
-                                 const char *upload_data,
-                                 size_t *upload_data_size);
+                                 const char *const args[]);
 
 
 /**
@@ -53,40 +49,13 @@ TEH_MHD_handler_static_response (struct TEH_RequestHandler 
*rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_MHD_handler_agpl_redirect (struct TEH_RequestHandler *rh,
+TEH_MHD_handler_agpl_redirect (const struct TEH_RequestHandler *rh,
                                struct MHD_Connection *connection,
-                               void **connection_cls,
-                               const char *upload_data,
-                               size_t *upload_data_size);
-
-
-/**
- * Function to call to handle the request by building a JSON
- * reply from varargs.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param response_code HTTP response code to use
- * @param do_cache can the response be cached? (0: no, 1: yes)
- * @param fmt format string for pack
- * @param ... varargs
- * @return MHD result code
- */
-int
-TEH_MHD_helper_send_json_pack (struct TEH_RequestHandler *rh,
-                               struct MHD_Connection *connection,
-                               void *connection_cls,
-                               int response_code,
-                               int do_cache,
-                               const char *fmt,
-                               ...);
+                               const char *const args[]);
 
 
 /**
@@ -95,17 +64,13 @@ TEH_MHD_helper_send_json_pack (struct TEH_RequestHandler 
*rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_MHD_handler_send_json_pack_error (struct TEH_RequestHandler *rh,
+TEH_MHD_handler_send_json_pack_error (const struct TEH_RequestHandler *rh,
                                       struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size);
+                                      const char *const args[]);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_recoup.c 
b/src/exchange/taler-exchange-httpd_recoup.c
index 26bd65b4..f4cd9915 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -562,27 +562,21 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
 
 
 /**
- * Handle a "/recoup" request.  Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_recoup() to
- * further check the details of the operation specified.  If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/coins/$COIN_PUB/recoup" request.  Parses the JSON, and, if
+ * successful, passes the JSON data to #verify_and_execute_recoup() to further
+ * check the details of the operation specified.  If everything checks out,
+ * this will ultimately lead to the refund being executed, or rejected.
  *
- * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
   */
 int
-TEH_RECOUP_handler_recoup (struct TEH_RequestHandler *rh,
-                           struct MHD_Connection *connection,
-                           void **connection_cls,
-                           const char *upload_data,
-                           size_t *upload_data_size)
+TEH_RECOUP_handler_recoup (struct MHD_Connection *connection,
+                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           const json_t *root)
 {
-  json_t *json;
   int res;
   struct TALER_CoinPublicInfo coin;
   struct TALER_DenominationBlindingKeyP coin_bks;
@@ -593,8 +587,6 @@ TEH_RECOUP_handler_recoup (struct TEH_RequestHandler *rh,
                                  &coin.denom_pub_hash),
     TALER_JSON_spec_denomination_signature ("denom_sig",
                                             &coin.denom_sig),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                 &coin.coin_pub),
     GNUNET_JSON_spec_fixed_auto ("coin_blind_key_secret",
                                  &coin_bks),
     GNUNET_JSON_spec_fixed_auto ("coin_sig",
@@ -605,20 +597,10 @@ TEH_RECOUP_handler_recoup (struct TEH_RequestHandler *rh,
     GNUNET_JSON_spec_end ()
   };
 
-  (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &json);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == json) )
-    return MHD_YES;
+  coin.coin_pub = *coin_pub;
   res = TALER_MHD_parse_json_data (connection,
-                                   json,
+                                   root,
                                    spec);
-  json_decref (json);
   if (GNUNET_SYSERR == res)
     return MHD_NO; /* hard failure */
   if (GNUNET_NO == res)
diff --git a/src/exchange/taler-exchange-httpd_recoup.h 
b/src/exchange/taler-exchange-httpd_recoup.h
index 1baefc8e..f86bf60e 100644
--- a/src/exchange/taler-exchange-httpd_recoup.h
+++ b/src/exchange/taler-exchange-httpd_recoup.h
@@ -27,25 +27,20 @@
 
 
 /**
- * Handle a "/recoup" request.  Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_recoup() to
- * further check the details of the operation specified.  If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/coins/$COIN_PUB/recoup" request.  Parses the JSON, and, if
+ * successful, passes the JSON data to #verify_and_execute_recoup() to further
+ * check the details of the operation specified.  If everything checks out,
+ * this will ultimately lead to the refund being executed, or rejected.
  *
- * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
   */
 int
-TEH_RECOUP_handler_recoup (struct TEH_RequestHandler *rh,
-                           struct MHD_Connection *connection,
-                           void **connection_cls,
-                           const char *upload_data,
-                           size_t *upload_data_size);
+TEH_RECOUP_handler_recoup (struct MHD_Connection *connection,
+                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           const json_t *root);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
similarity index 93%
rename from src/exchange/taler-exchange-httpd_refresh_reveal.c
rename to src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 1e03c8e7..e7e5b97d 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh_reveal.c
- * @brief Handle /refresh/reveal requests
+ * @file taler-exchange-httpd_refreshes_reveal.c
+ * @brief Handle /refreshes/$RCH/reveal requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
@@ -26,7 +26,7 @@
 #include <microhttpd.h>
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_refreshes_reveal.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -299,7 +299,7 @@ refresh_reveal_transaction (void *cls,
                             int *mhd_ret)
 {
   struct RevealContext *rctx = cls;
-  struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
+  struct TALER_EXCHANGEDB_RefreshMelt melt;
   enum GNUNET_DB_QueryStatus qs;
 
   /* Obtain basic information about the refresh operation and what
@@ -307,7 +307,7 @@ refresh_reveal_transaction (void *cls,
   qs = TEH_plugin->get_melt (TEH_plugin->cls,
                              session,
                              &rctx->rc,
-                             &refresh_melt);
+                             &melt);
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -319,7 +319,7 @@ refresh_reveal_transaction (void *cls,
   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     return qs;
   if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
-       (refresh_melt.session.noreveal_index >= TALER_CNC_KAPPA) )
+       (melt.session.noreveal_index >= TALER_CNC_KAPPA) )
   {
     GNUNET_break (0);
     *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -331,7 +331,7 @@ refresh_reveal_transaction (void *cls,
 
   /* Verify commitment */
   {
-    /* Note that the contents of rcs[refresh_melt.session.noreveal_index]
+    /* Note that the contents of rcs[melt.session.noreveal_index]
        will be aliased and are *not* allocated (or deallocated) in
        this function -- in contrast to the other offsets! */
     struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
@@ -343,7 +343,7 @@ refresh_reveal_transaction (void *cls,
     {
       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-      if (i == refresh_melt.session.noreveal_index)
+      if (i == melt.session.noreveal_index)
       {
         /* Take these coin envelopes from the client */
         rce->transfer_pub = rctx->gamma_tp;
@@ -360,7 +360,7 @@ refresh_reveal_transaction (void *cls,
         GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
                                             &rce->transfer_pub.ecdhe_pub);
         TALER_link_reveal_transfer_secret (tpriv,
-                                           &refresh_melt.session.coin.coin_pub,
+                                           &melt.session.coin.coin_pub,
                                            &ts);
         rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
                                            struct TALER_RefreshCoinData);
@@ -387,15 +387,15 @@ refresh_reveal_transaction (void *cls,
                                   TALER_CNC_KAPPA,
                                   rctx->num_fresh_coins,
                                   rcs,
-                                  &refresh_melt.session.coin.coin_pub,
-                                  &refresh_melt.session.amount_with_fee);
+                                  &melt.session.coin.coin_pub,
+                                  &melt.session.amount_with_fee);
 
     /* Free resources allocated above */
     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     {
       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-      if (i == refresh_melt.session.noreveal_index)
+      if (i == melt.session.noreveal_index)
         continue; /* This offset is special... */
       for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
       {
@@ -421,7 +421,7 @@ refresh_reveal_transaction (void *cls,
   {
     struct TALER_Amount refresh_cost;
 
-    refresh_cost = refresh_melt.melt_fee;
+    refresh_cost = melt.melt_fee;
     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     {
       struct TALER_Amount fee_withdraw;
@@ -450,7 +450,7 @@ refresh_reveal_transaction (void *cls,
       }
     }
     if (0 < TALER_amount_cmp (&refresh_cost,
-                              &refresh_melt.session.amount_with_fee))
+                              &melt.session.amount_with_fee))
     {
       GNUNET_break_op (0);
       *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -542,7 +542,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
   struct GNUNET_HashCode dki_h[num_fresh_coins];
   struct TALER_RefreshCoinData rcds[num_fresh_coins];
   struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
-  struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
+  struct TALER_EXCHANGEDB_RefreshMelt melt;
   int res;
 
   /* Parse denomination key hashes */
@@ -613,7 +613,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
         (qs = TEH_plugin->get_melt (TEH_plugin->cls,
                                     NULL,
                                     &rctx->rc,
-                                    &refresh_melt)))
+                                    &melt)))
     {
       switch (qs)
       {
@@ -663,7 +663,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
       ldp.purpose.size = htonl (sizeof (ldp));
       ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
       ldp.h_denom_pub = dki_h[i];
-      ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
+      ldp.old_coin_pub = melt.session.coin.coin_pub;
       ldp.transfer_pub = rctx->gamma_tp;
       GNUNET_CRYPTO_hash (rcds[i].coin_ev,
                           rcds[i].coin_ev_size,
@@ -672,7 +672,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
                                       &ldp.purpose,
                                       &link_sigs[i].eddsa_signature,
-                                      &refresh_melt.session.coin.coin_pub.
+                                      &melt.session.coin.coin_pub.
                                       eddsa_pub))
       {
         GNUNET_break_op (0);
@@ -884,37 +884,33 @@ handle_refresh_reveal_json (struct MHD_Connection 
*connection,
 
 
 /**
- * Handle a "/refresh/reveal" request. This time, the client reveals the
+ * Handle a "/refreshes/$RCH/reveal" request. This time, the client reveals the
  * private transfer keys except for the cut-and-choose value returned from
- * "/refresh/melt".  This function parses the revealed keys and secrets and
+ * "/coins/$COIN_PUB/melt".  This function parses the revealed keys and 
secrets and
  * ultimately passes everything to #resolve_refresh_reveal_denominations()
  * which will verify that the revealed information is valid then runs the
  * transaction in #refresh_reveal_transaction() and finally returns the signed
  * refreshed coins.
  *
  * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
+ * @param args array of additional options (length: 2, session hash and the 
string "reveal")
  * @return MHD result code
-  */
+ */
 int
-TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size)
+TEH_REFRESH_handler_reveal (const struct TEH_RequestHandler *rh,
+                            struct MHD_Connection *connection,
+                            const json_t *root,
+                            const char *const args[2])
 {
   int res;
-  json_t *root;
   json_t *coin_evs;
   json_t *transfer_privs;
   json_t *link_sigs;
   json_t *new_denoms_h;
   struct RevealContext rctx;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc),
     GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
     GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
     GNUNET_JSON_spec_json ("link_sigs", &link_sigs),
@@ -924,24 +920,34 @@ TEH_REFRESH_handler_refresh_reveal (struct 
TEH_RequestHandler *rh,
   };
 
   (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) ||
-       (NULL == root) )
-    return MHD_YES;
-
   memset (&rctx,
           0,
           sizeof (rctx));
+
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &rctx.rc,
+                                     sizeof (rctx.rc)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_REFRESHES_INVALID_RCH,
+                                       "refresh commitment hash malformed");
+  }
+  if (0 != strcmp (args[1],
+                   "reveal"))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_OPERATION_INVALID,
+                                       "expected 'reveal' operation");
+  }
   res = TALER_MHD_parse_json_data (connection,
                                    root,
                                    spec);
-  json_decref (root);
   if (GNUNET_OK != res)
   {
     GNUNET_break_op (0);
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.h 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.h
similarity index 67%
rename from src/exchange/taler-exchange-httpd_refresh_reveal.h
rename to src/exchange/taler-exchange-httpd_refreshes_reveal.h
index 0b0c29b7..afc9adce 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.h
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.h
@@ -29,26 +29,25 @@
 
 
 /**
- * Handle a "/refresh/reveal" request. This time, the client reveals the
+ * Handle a "/refreshes/$RCH/reveal" request. This time, the client reveals the
  * private transfer keys except for the cut-and-choose value returned from
- * "/refresh/melt".  This function parses the revealed keys and secrets and
+ * "/coins/$COIN_PUB/melt".  This function parses the revealed keys and 
secrets and
  * ultimately passes everything to #resolve_refresh_reveal_denominations()
  * which will verify that the revealed information is valid then runs the
  * transaction in #refresh_reveal_transaction() and finally returns the signed
  * refreshed coins.
  *
  * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
+ * @param args array of additional options (length: 2, session hash and the 
string "reveal")
  * @return MHD result code
-  */
+ */
 int
-TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size);
+TEH_REFRESH_handler_reveal (const struct TEH_RequestHandler *rh,
+                            struct MHD_Connection *connection,
+                            const json_t *root,
+                            const char *const args[2]);
+
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index 8e24b9b4..e7e34e0b 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -532,27 +532,21 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
 
 
 /**
- * Handle a "/refund" request.  Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_refund() to
- * further check the details of the operation specified.  If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/coins/$COIN_PUB/refund" request.  Parses the JSON, and, if
+ * successful, passes the JSON data to #verify_and_execute_refund() to further
+ * check the details of the operation specified.  If everything checks out,
+ * this will ultimately lead to the refund being executed, or rejected.
  *
- * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
   */
 int
-TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
-                           struct MHD_Connection *connection,
-                           void **connection_cls,
-                           const char *upload_data,
-                           size_t *upload_data_size)
+TEH_REFUND_handler_refund (struct MHD_Connection *connection,
+                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           const json_t *root)
 {
-  json_t *json;
   int res;
   struct TALER_EXCHANGEDB_Refund refund;
   struct GNUNET_JSON_Specification spec[] = {
@@ -560,7 +554,6 @@ TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
     TALER_JSON_spec_amount ("refund_fee", &refund.details.refund_fee),
     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
                                  &refund.details.h_contract_terms),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &refund.coin.coin_pub),
     GNUNET_JSON_spec_fixed_auto ("merchant_pub", &refund.details.merchant_pub),
     GNUNET_JSON_spec_uint64 ("rtransaction_id",
                              &refund.details.rtransaction_id),
@@ -568,20 +561,10 @@ TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
     GNUNET_JSON_spec_end ()
   };
 
-  (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &json);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == json) )
-    return MHD_YES;
+  refund.coin.coin_pub = *coin_pub;
   res = TALER_MHD_parse_json_data (connection,
-                                   json,
+                                   root,
                                    spec);
-  json_decref (json);
   if (GNUNET_SYSERR == res)
     return MHD_NO; /* hard failure */
   if (GNUNET_NO == res)
diff --git a/src/exchange/taler-exchange-httpd_refund.h 
b/src/exchange/taler-exchange-httpd_refund.h
index 4f2b868e..b79419f1 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_refund.h
@@ -29,24 +29,19 @@
 
 
 /**
- * Handle a "/refund" request.  Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_refund() to
- * further check the details of the operation specified.  If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * Handle a "/coins/$COIN_PUB/refund" request.  Parses the JSON, and, if
+ * successful, passes the JSON data to #verify_and_execute_refund() to further
+ * check the details of the operation specified.  If everything checks out,
+ * this will ultimately lead to the refund being executed, or rejected.
  *
- * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param coin_pub public key of the coin
+ * @param root uploaded JSON data
  * @return MHD result code
   */
 int
-TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
-                           struct MHD_Connection *connection,
-                           void **connection_cls,
-                           const char *upload_data,
-                           size_t *upload_data_size);
+TEH_REFUND_handler_refund (struct MHD_Connection *connection,
+                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           const json_t *root);
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_reserve_status.c 
b/src/exchange/taler-exchange-httpd_reserves_get.c
similarity index 80%
rename from src/exchange/taler-exchange-httpd_reserve_status.c
rename to src/exchange/taler-exchange-httpd_reserves_get.c
index e2d35aae..d06a871e 100644
--- a/src/exchange/taler-exchange-httpd_reserve_status.c
+++ b/src/exchange/taler-exchange-httpd_reserves_get.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_reserve_status.c
- * @brief Handle /reserve/status requests
+ * @file taler-exchange-httpd_reserves_get.c
+ * @brief Handle /reserves/$RESERVE_PUB GET requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
@@ -25,7 +25,7 @@
 #include <jansson.h>
 #include "taler_mhd_lib.h"
 #include "taler_json_lib.h"
-#include "taler-exchange-httpd_reserve_status.h"
+#include "taler-exchange-httpd_reserves_get.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -114,42 +114,37 @@ reserve_status_transaction (void *cls,
 
 
 /**
- * Handle a "/reserve/status" request.  Parses the
- * given "reserve_pub" argument (which should contain the
+ * Handle a GET "/reserves/" request.  Parses the
+ * given "reserve_pub" in @a args (which should contain the
  * EdDSA public key of a reserve) and then respond with the
  * status of the reserve.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 1, just the reserve_pub)
  * @return MHD result code
  */
 int
-TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
+TEH_RESERVE_handler_reserve_status (const struct TEH_RequestHandler *rh,
                                     struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size)
+                                    const char *const args[1])
 {
   struct ReserveStatusContext rsc;
-  int res;
   int mhd_ret;
 
   (void) rh;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
-  res = TALER_MHD_parse_request_arg_data (connection,
-                                          "reserve_pub",
-                                          &rsc.reserve_pub,
-                                          sizeof (struct
-                                                  TALER_ReservePublicKeyP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO; /* internal error */
-  if (GNUNET_NO == res)
-    return MHD_YES; /* parse error */
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &rsc.reserve_pub,
+                                     sizeof (rsc.reserve_pub)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_RESERVES_INVALID_RESERVE_PUB,
+                                       "reserve public key malformed");
+  }
   rsc.rh = NULL;
   if (GNUNET_OK !=
       TEH_DB_run_transaction (connection,
@@ -177,4 +172,4 @@ TEH_RESERVE_handler_reserve_status (struct 
TEH_RequestHandler *rh,
 }
 
 
-/* end of taler-exchange-httpd_reserve_status.c */
+/* end of taler-exchange-httpd_reserves_get.c */
diff --git a/src/exchange/taler-exchange-httpd_reserve_status.h 
b/src/exchange/taler-exchange-httpd_reserves_get.h
similarity index 63%
rename from src/exchange/taler-exchange-httpd_reserve_status.h
rename to src/exchange/taler-exchange-httpd_reserves_get.h
index 67eba230..bc697dcd 100644
--- a/src/exchange/taler-exchange-httpd_reserve_status.h
+++ b/src/exchange/taler-exchange-httpd_reserves_get.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_reserve_status.h
- * @brief Handle /reserve/status requests
+ * @file taler-exchange-httpd_reserves_get.h
+ * @brief Handle /reserves/$RESERVE_PUB GET requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
@@ -26,24 +26,21 @@
 #include <microhttpd.h>
 #include "taler-exchange-httpd.h"
 
+
 /**
- * Handle a "/reserve/status" request.  Parses the
- * given "reserve_pub" argument (which should contain the
+ * Handle a GET "/reserves/" request.  Parses the
+ * given "reserve_pub" in @a args (which should contain the
  * EdDSA public key of a reserve) and then respond with the
  * status of the reserve.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 1, just the reserve_pub)
  * @return MHD result code
-  */
+ */
 int
-TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
+TEH_RESERVE_handler_reserve_status (const struct TEH_RequestHandler *rh,
                                     struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size);
+                                    const char *const args[1]);
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_terms.c 
b/src/exchange/taler-exchange-httpd_terms.c
index 47905f60..121e1c78 100644
--- a/src/exchange/taler-exchange-httpd_terms.c
+++ b/src/exchange/taler-exchange-httpd_terms.c
@@ -43,22 +43,16 @@ static struct TALER_MHD_Legal *pp;
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_handler_terms (struct TEH_RequestHandler *rh,
+TEH_handler_terms (const struct TEH_RequestHandler *rh,
                    struct MHD_Connection *connection,
-                   void **connection_cls,
-                   const char *upload_data,
-                   size_t *upload_data_size)
+                   const char *const args[])
 {
   (void) rh;
-  (void) upload_data;
-  (void) upload_data_size;
-  (void) connection_cls;
+  (void) args;
   return TALER_MHD_reply_legal (connection,
                                 tos);
 }
@@ -69,22 +63,16 @@ TEH_handler_terms (struct TEH_RequestHandler *rh,
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_handler_privacy (struct TEH_RequestHandler *rh,
+TEH_handler_privacy (const struct TEH_RequestHandler *rh,
                      struct MHD_Connection *connection,
-                     void **connection_cls,
-                     const char *upload_data,
-                     size_t *upload_data_size)
+                     const char *const args[])
 {
   (void) rh;
-  (void) upload_data;
-  (void) upload_data_size;
-  (void) connection_cls;
+  (void) args;
   return TALER_MHD_reply_legal (connection,
                                 pp);
 }
diff --git a/src/exchange/taler-exchange-httpd_terms.h 
b/src/exchange/taler-exchange-httpd_terms.h
index 75909df9..7fe7774a 100644
--- a/src/exchange/taler-exchange-httpd_terms.h
+++ b/src/exchange/taler-exchange-httpd_terms.h
@@ -34,34 +34,27 @@
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_handler_terms (struct TEH_RequestHandler *rh,
+TEH_handler_terms (const struct TEH_RequestHandler *rh,
                    struct MHD_Connection *connection,
-                   void **connection_cls,
-                   const char *upload_data,
-                   size_t *upload_data_size);
+                   const char *const args[]);
+
 
 /**
  * Handle a "/privacy" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
  */
 int
-TEH_handler_privacy (struct TEH_RequestHandler *rh,
+TEH_handler_privacy (const struct TEH_RequestHandler *rh,
                      struct MHD_Connection *connection,
-                     void **connection_cls,
-                     const char *upload_data,
-                     size_t *upload_data_size);
+                     const char *const args[]);
 
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c 
b/src/exchange/taler-exchange-httpd_transfers_get.c
similarity index 93%
rename from src/exchange/taler-exchange-httpd_track_transfer.c
rename to src/exchange/taler-exchange-httpd_transfers_get.c
index 1a780c06..713b43a5 100644
--- a/src/exchange/taler-exchange-httpd_track_transfer.c
+++ b/src/exchange/taler-exchange-httpd_transfers_get.c
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_track_transfer.c
+ * @file taler-exchange-httpd_transfers_get.c
  * @brief Handle wire transfer /track/transfer requests
  * @author Christian Grothoff
  */
@@ -25,7 +25,7 @@
 #include <pthread.h>
 #include "taler_signatures.h"
 #include "taler-exchange-httpd_keystate.h"
-#include "taler-exchange-httpd_track_transfer.h"
+#include "taler-exchange-httpd_transfers_get.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
@@ -482,40 +482,37 @@ free_ctx (struct WtidTransactionContext *ctx)
 
 
 /**
- * Handle a "/track/transfer" request.
+ * Handle a GET "/transfers/$WTID" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 1, just the wtid)
  * @return MHD result code
  */
 int
-TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
+TEH_TRACKING_handler_track_transfer (const struct TEH_RequestHandler *rh,
                                      struct MHD_Connection *connection,
-                                     void **connection_cls,
-                                     const char *upload_data,
-                                     size_t *upload_data_size)
+                                     const char *const args[1])
 {
   struct WtidTransactionContext ctx;
-  int res;
   int mhd_ret;
 
   (void) rh;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
-  memset (&ctx, 0, sizeof (ctx));
-  res = TALER_MHD_parse_request_arg_data (connection,
-                                          "wtid",
-                                          &ctx.wtid,
-                                          sizeof (struct
-                                                  
TALER_WireTransferIdentifierRawP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO; /* internal error */
-  if (GNUNET_NO == res)
-    return MHD_YES; /* parse error */
+  memset (&ctx,
+          0,
+          sizeof (ctx));
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &ctx.wtid,
+                                     sizeof (ctx.wtid)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_TRANSFERS_INVALID_WTID,
+                                       "wire transfer identifier malformed");
+  }
   if (GNUNET_OK !=
       TEH_DB_run_transaction (connection,
                               "run track transfer",
@@ -538,4 +535,4 @@ TEH_TRACKING_handler_track_transfer (struct 
TEH_RequestHandler *rh,
 }
 
 
-/* end of taler-exchange-httpd_track_transfer.c */
+/* end of taler-exchange-httpd_transfers_get.c */
diff --git a/src/exchange/taler-exchange-httpd_track_transfer.h 
b/src/exchange/taler-exchange-httpd_transfers_get.h
similarity index 69%
rename from src/exchange/taler-exchange-httpd_track_transfer.h
rename to src/exchange/taler-exchange-httpd_transfers_get.h
index c68cb288..0f9e21a5 100644
--- a/src/exchange/taler-exchange-httpd_track_transfer.h
+++ b/src/exchange/taler-exchange-httpd_transfers_get.h
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_track_transfer.h
+ * @file taler-exchange-httpd_transfers_get.h
  * @brief Handle wire transfer tracking-related requests
  * @author Christian Grothoff
  */
@@ -27,20 +27,17 @@
 
 
 /**
- * Handle a "/track/transfer" request.
+ * Handle a GET "/transfers/$WTID" request.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (length: 1, just the wtid)
  * @return MHD result code
  */
 int
-TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
+TEH_TRACKING_handler_track_transfer (const struct TEH_RequestHandler *rh,
                                      struct MHD_Connection *connection,
-                                     void **connection_cls,
-                                     const char *upload_data,
-                                     size_t *upload_data_size);
+                                     const char *const args[1]);
+
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_validation.c 
b/src/exchange/taler-exchange-httpd_validation.c
index e3dd8e86..e55100e1 100644
--- a/src/exchange/taler-exchange-httpd_validation.c
+++ b/src/exchange/taler-exchange-httpd_validation.c
@@ -25,7 +25,6 @@
 #include "taler-exchange-httpd_validation.h"
 #include "taler-exchange-httpd_wire.h"
 #include "taler_exchangedb_lib.h"
-#include "taler_json_lib.h"
 
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_wire.c 
b/src/exchange/taler-exchange-httpd_wire.c
index e4bcbec5..de4e2db4 100644
--- a/src/exchange/taler-exchange-httpd_wire.c
+++ b/src/exchange/taler-exchange-httpd_wire.c
@@ -124,22 +124,16 @@ TEH_WIRE_get_fees (const char *method)
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
   */
 int
-TEH_WIRE_handler_wire (struct TEH_RequestHandler *rh,
+TEH_WIRE_handler_wire (const struct TEH_RequestHandler *rh,
                        struct MHD_Connection *connection,
-                       void **connection_cls,
-                       const char *upload_data,
-                       size_t *upload_data_size)
+                       const char *const args[])
 {
   (void) rh;
-  (void) connection_cls;
-  (void) upload_data;
-  (void) upload_data_size;
+  (void) args;
   GNUNET_assert (NULL != wire_methods);
   return TALER_MHD_reply_json (connection,
                                wire_methods,
diff --git a/src/exchange/taler-exchange-httpd_wire.h 
b/src/exchange/taler-exchange-httpd_wire.h
index 75c60353..ac4ea39c 100644
--- a/src/exchange/taler-exchange-httpd_wire.h
+++ b/src/exchange/taler-exchange-httpd_wire.h
@@ -51,17 +51,13 @@ TEH_WIRE_get_fees (const char *method);
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param args array of additional options (must be empty for this function)
  * @return MHD result code
-  */
+ */
 int
-TEH_WIRE_handler_wire (struct TEH_RequestHandler *rh,
+TEH_WIRE_handler_wire (const struct TEH_RequestHandler *rh,
                        struct MHD_Connection *connection,
-                       void **connection_cls,
-                       const char *upload_data,
-                       size_t *upload_data_size);
+                       const char *const args[]);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
similarity index 92%
rename from src/exchange/taler-exchange-httpd_reserve_withdraw.c
rename to src/exchange/taler-exchange-httpd_withdraw.c
index 9daad0a0..33308dce 100644
--- a/src/exchange/taler-exchange-httpd_reserve_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -17,7 +17,7 @@
   see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_reserve_withdraw.c
+ * @file taler-exchange-httpd_withdraw.c
  * @brief Handle /reserve/withdraw requests
  * @author Florian Dold
  * @author Benedikt Mueller
@@ -28,7 +28,7 @@
 #include <jansson.h>
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
-#include "taler-exchange-httpd_reserve_withdraw.h"
+#include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -335,30 +335,27 @@ withdraw_transaction (void *cls,
 
 
 /**
- * Handle a "/reserve/withdraw" request.  Parses the "reserve_pub"
- * EdDSA key of the reserve and the requested "denom_pub" which
- * specifies the key/value of the coin to be withdrawn, and checks
- * that the signature "reserve_sig" makes this a valid withdrawal
- * request from the specified reserve.  If so, the envelope
- * with the blinded coin "coin_ev" is passed down to execute the
- * withdrawl operation.
+ * Handle a "/reserves/$RESERVE_PUB/withdraw" request.  Parses the
+ * "reserve_pub" EdDSA key of the reserve and the requested "denom_pub" which
+ * specifies the key/value of the coin to be withdrawn, and checks that the
+ * signature "reserve_sig" makes this a valid withdrawal request from the
+ * specified reserve.  If so, the envelope with the blinded coin "coin_ev" is
+ * passed down to execute the withdrawl operation.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param root uploaded JSON data
+ * @param args array of additional options (first must be the
+ *         reserve public key, the second one should be "withdraw")
  * @return MHD result code
  */
 int
-TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh,
+TEH_RESERVE_handler_reserve_withdraw (const struct TEH_RequestHandler *rh,
                                       struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size)
+                                      const json_t *root,
+                                      const char *const args[2])
 {
   struct WithdrawContext wc;
-  json_t *root;
   int res;
   int mhd_ret;
   unsigned int hc;
@@ -369,8 +366,6 @@ TEH_RESERVE_handler_reserve_withdraw (struct 
TEH_RequestHandler *rh,
     GNUNET_JSON_spec_varsize ("coin_ev",
                               (void **) &wc.blinded_msg,
                               &wc.blinded_msg_len),
-    GNUNET_JSON_spec_fixed_auto ("reserve_pub",
-                                 &wc.wsrd.reserve_pub),
     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
                                  &wc.signature),
     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
@@ -379,19 +374,22 @@ TEH_RESERVE_handler_reserve_withdraw (struct 
TEH_RequestHandler *rh,
   };
 
   (void) rh;
-  res = TALER_MHD_parse_post_json (connection,
-                                   connection_cls,
-                                   upload_data,
-                                   upload_data_size,
-                                   &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == root) )
-    return MHD_YES;
+  if (GNUNET_OK !=
+      GNUNET_STRINGS_string_to_data (args[0],
+                                     strlen (args[0]),
+                                     &wc.wsrd.reserve_pub,
+                                     sizeof (wc.wsrd.reserve_pub)))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_BAD_REQUEST,
+                                       TALER_EC_RESERVES_INVALID_RESERVE_PUB,
+                                       "reserve public key malformed");
+  }
+
   res = TALER_MHD_parse_json_data (connection,
                                    root,
                                    spec);
-  json_decref (root);
   if (GNUNET_OK != res)
     return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
   wc.key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
@@ -508,4 +506,4 @@ TEH_RESERVE_handler_reserve_withdraw (struct 
TEH_RequestHandler *rh,
 }
 
 
-/* end of taler-exchange-httpd_reserve_withdraw.c */
+/* end of taler-exchange-httpd_withdraw.c */
diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.h 
b/src/exchange/taler-exchange-httpd_withdraw.h
similarity index 58%
rename from src/exchange/taler-exchange-httpd_reserve_withdraw.h
rename to src/exchange/taler-exchange-httpd_withdraw.h
index 67b4cad0..cc820283 100644
--- a/src/exchange/taler-exchange-httpd_reserve_withdraw.h
+++ b/src/exchange/taler-exchange-httpd_withdraw.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_reserve_withdraw.h
+ * @file taler-exchange-httpd_withdraw.h
  * @brief Handle /reserve/withdraw requests
  * @author Florian Dold
  * @author Benedikt Mueller
@@ -28,26 +28,24 @@
 
 
 /**
- * Handle a "/reserve/withdraw" request.  Parses the "reserve_pub"
- * EdDSA key of the reserve and the requested "denom_pub" which
- * specifies the key/value of the coin to be withdrawn, and checks
- * that the signature "reserve_sig" makes this a valid withdrawl
- * request from the specified reserve.  If so, the envelope
- * with the blinded coin "coin_ev" is passed down to execute the
- * withdrawl operation.
+ * Handle a "/reserves/$RESERVE_PUB/withdraw" request.  Parses the
+ * "reserve_pub" EdDSA key of the reserve and the requested "denom_pub" which
+ * specifies the key/value of the coin to be withdrawn, and checks that the
+ * signature "reserve_sig" makes this a valid withdrawl request from the
+ * specified reserve.  If so, the envelope with the blinded coin "coin_ev" is
+ * passed down to execute the withdrawl operation.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @param root uploaded JSON data
+ * @param args array of additional options (first must be the
+ *         reserve public key, the second one should be "withdraw")
  * @return MHD result code
   */
 int
-TEH_RESERVE_handler_reserve_withdraw (struct TEH_RequestHandler *rh,
+TEH_RESERVE_handler_reserve_withdraw (const struct TEH_RequestHandler *rh,
                                       struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size);
+                                      const json_t *root,
+                                      const char *const args[2]);
 
 #endif
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 9df2fe77..f8aea50c 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -3389,8 +3389,8 @@ postgres_select_refunds_by_coin (void *cls,
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database handle to use, NULL if not run in any transaction
  * @param rc commitment hash to use to locate the operation
- * @param[out] refresh_melt where to store the result; note that
- *             refresh_melt->session.coin.denom_sig will be set to NULL
+ * @param[out] melt where to store the result; note that
+ *             melt->session.coin.denom_sig will be set to NULL
  *             and is not fetched by this routine (as it is not needed by the 
client)
  * @return transaction status
  */
@@ -3398,7 +3398,7 @@ static enum GNUNET_DB_QueryStatus
 postgres_get_melt (void *cls,
                    struct TALER_EXCHANGEDB_Session *session,
                    const struct TALER_RefreshCommitmentP *rc,
-                   struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
+                   struct TALER_EXCHANGEDB_RefreshMelt *melt)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
@@ -3407,30 +3407,30 @@ postgres_get_melt (void *cls,
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                          &refresh_melt->session.coin.
+                                          &melt->session.coin.
                                           denom_pub_hash),
     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
-                                 &refresh_melt->melt_fee),
+                                 &melt->melt_fee),
     GNUNET_PQ_result_spec_uint32 ("noreveal_index",
-                                  &refresh_melt->session.noreveal_index),
+                                  &melt->session.noreveal_index),
     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
-                                          
&refresh_melt->session.coin.coin_pub),
+                                          &melt->session.coin.coin_pub),
     GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                          &refresh_melt->session.coin_sig),
+                                          &melt->session.coin_sig),
     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                 &refresh_melt->session.amount_with_fee),
+                                 &melt->session.amount_with_fee),
     GNUNET_PQ_result_spec_end
   };
   enum GNUNET_DB_QueryStatus qs;
 
-  refresh_melt->session.coin.denom_sig.rsa_signature = NULL;
+  melt->session.coin.denom_sig.rsa_signature = NULL;
   if (NULL == session)
     session = postgres_get_session (pg);
   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
                                                  "get_melt",
                                                  params,
                                                  rs);
-  refresh_melt->session.rc = *rc;
+  melt->session.rc = *rc;
   return qs;
 }
 
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 917ac36d..1c48fe33 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -85,6 +85,32 @@ enum TALER_ErrorCode
    */
   TALER_EC_METHOD_INVALID = 8,
 
+  /**
+   * Operation specified invalid for this URL (resulting in a "NOT
+   * FOUND" for the overall response).
+   */
+  TALER_EC_OPERATION_INVALID = 9,
+
+  /**
+   * There is no endpoint defined for the URL provided by the client
+   * (returned together with a MHD_HTTP_NOT FOUND status code).
+   */
+  TALER_EC_ENDPOINT_UNKNOWN = 10,
+
+  /**
+   * The URI is longer than the longest URI the HTTP server is willing
+   * to parse. Returned together with an HTTP status code of
+   * MHD_HTTP_URI_TOO_LONG.
+   */
+  TALER_EC_URI_TOO_LONG = 11,
+
+  /**
+   * The number of segments included in the URI does not match the
+   * number of segments expected by the endpoint. (returned together
+   * with a MHD_HTTP_NOT FOUND status code).
+   */
+  TALER_EC_WRONG_NUMBER_OF_SEGMENTS = 12,
+
   /**
    * The exchange failed to even just initialize its connection to the
    * database.  This response is provided with HTTP status code
@@ -181,6 +207,50 @@ enum TALER_ErrorCode
    */
   TALER_EC_DB_COIN_HISTORY_STORE_ERROR = 1014,
 
+  /**
+   * The public key of given to a /coins/ handler was malformed.
+   */
+  TALER_EC_COINS_INVALID_COIN_PUB = 1050,
+
+  /**
+   * The public key of given to a /reserves/ handler was malformed.
+   */
+  TALER_EC_RESERVES_INVALID_RESERVE_PUB = 1051,
+
+  /**
+   * The public key of given to a /transfers/ handler was malformed.
+   */
+  TALER_EC_TRANSFERS_INVALID_WTID = 1052,
+
+  /**
+   * The hash of the wire details of given to a /deposits/ handler was
+   * malformed.
+   */
+  TALER_EC_DEPOSITS_INVALID_H_WIRE = 1053,
+
+  /**
+   * The merchant public key given to a /deposits/ handler was
+   * malformed.
+   */
+  TALER_EC_DEPOSITS_INVALID_MERCHANT_PUB = 1054,
+
+  /**
+   * The hash of the contract given to a /deposits/ handler was
+   * malformed.
+   */
+  TALER_EC_DEPOSITS_INVALID_H_CONTRACT_TERMS = 1055,
+
+  /**
+   * The coin public key given to a /deposits/ handler was malformed.
+   */
+  TALER_EC_DEPOSITS_INVALID_COIN_PUB = 1056,
+
+  /**
+   * The hash of the refresh commitment given to a /refreshes/ handler
+   * was malformed.
+   */
+  TALER_EC_REFRESHES_INVALID_RCH = 1057,
+
   /**
    * The given reserve does not have sufficient funds to admit the
    * requested withdraw operation at this time.  The response includes
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 09eca8ed..4e917df8 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2018 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -493,19 +493,6 @@ TALER_EXCHANGE_test_signing_key (const struct 
TALER_EXCHANGE_Keys *keys,
                                  const struct TALER_ExchangePublicKeyP *pub);
 
 
-/**
- * Lookup the given @a pub in @a keys.
- *
- * @param keys the exchange's key set
- * @param pub claimed current online signing key for the exchange
- * @return NULL if @a pub was not found
- */
-const struct TALER_EXCHANGE_SigningPublicKey *
-TALER_EXCHANGE_get_signing_key_details (const struct TALER_EXCHANGE_Keys *keys,
-                                        const struct
-                                        TALER_ExchangePublicKeyP *pub);
-
-
 /**
  * Get exchange's base URL.
  *
@@ -574,11 +561,11 @@ TALER_EXCHANGE_get_denomination_key_by_hash (const struct
  * @return NULL on error (@a exchange_pub not known)
  */
 const struct TALER_EXCHANGE_SigningPublicKey *
-TALER_EXCHANGE_get_exchange_signing_key_info (const struct
-                                              TALER_EXCHANGE_Keys *keys,
-                                              const struct
-                                              TALER_ExchangePublicKeyP *
-                                              exchange_pub);
+TALER_EXCHANGE_get_signing_key_info (const struct
+                                     TALER_EXCHANGE_Keys *keys,
+                                     const struct
+                                     TALER_ExchangePublicKeyP *
+                                     exchange_pub);
 
 
 /* *********************  /wire *********************** */
@@ -661,12 +648,12 @@ struct TALER_EXCHANGE_WireAccount
  * @param accounts list of wire accounts of the exchange, NULL on error
  */
 typedef void
-(*TALER_EXCHANGE_WireResultCallback) (void *cls,
-                                      unsigned int http_status,
-                                      enum TALER_ErrorCode ec,
-                                      unsigned int accounts_len,
-                                      const struct
-                                      TALER_EXCHANGE_WireAccount *accounts);
+(*TALER_EXCHANGE_WireCallback) (void *cls,
+                                unsigned int http_status,
+                                enum TALER_ErrorCode ec,
+                                unsigned int accounts_len,
+                                const struct
+                                TALER_EXCHANGE_WireAccount *accounts);
 
 
 /**
@@ -696,7 +683,7 @@ struct TALER_EXCHANGE_WireHandle;
  */
 struct TALER_EXCHANGE_WireHandle *
 TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
-                     TALER_EXCHANGE_WireResultCallback wire_cb,
+                     TALER_EXCHANGE_WireCallback wire_cb,
                      void *wire_cb_cls);
 
 
@@ -710,7 +697,7 @@ void
 TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh);
 
 
-/* *********************  /deposit *********************** */
+/* *********************  /coins/$COIN_PUB/deposit *********************** */
 
 
 /**
@@ -812,7 +799,7 @@ void
 TALER_EXCHANGE_deposit_cancel (struct TALER_EXCHANGE_DepositHandle *deposit);
 
 
-/* *********************  /refund *********************** */
+/* *********************  /coins/$COIN_PUB/refund *********************** */
 
 /**
  * @brief A Refund Handle
@@ -833,12 +820,12 @@ struct TALER_EXCHANGE_RefundHandle;
  *            be forwarded to the customer)
  */
 typedef void
-(*TALER_EXCHANGE_RefundResultCallback) (void *cls,
-                                        unsigned int http_status,
-                                        enum TALER_ErrorCode ec,
-                                        const struct
-                                        TALER_ExchangePublicKeyP *sign_key,
-                                        const json_t *obj);
+(*TALER_EXCHANGE_RefundCallback) (void *cls,
+                                  unsigned int http_status,
+                                  enum TALER_ErrorCode ec,
+                                  const struct
+                                  TALER_ExchangePublicKeyP *sign_key,
+                                  const json_t *obj);
 
 
 /**
@@ -879,7 +866,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
                        uint64_t rtransaction_id,
                        const struct TALER_MerchantPrivateKeyP *merchant_priv,
-                       TALER_EXCHANGE_RefundResultCallback cb,
+                       TALER_EXCHANGE_RefundCallback cb,
                        void *cb_cls);
 
 
@@ -923,7 +910,7 @@ TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle 
*exchange,
                         uint64_t rtransaction_id,
                         const struct TALER_MerchantPublicKeyP *merchant_pub,
                         const struct TALER_MerchantSignatureP *merchant_sig,
-                        TALER_EXCHANGE_RefundResultCallback cb,
+                        TALER_EXCHANGE_RefundCallback cb,
                         void *cb_cls);
 
 
@@ -939,13 +926,13 @@ void
 TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund);
 
 
-/* ********************* /reserve/status *********************** */
+/* ********************* GET /reserves/$RESERVE_PUB *********************** */
 
 
 /**
  * @brief A /reserve/status Handle
  */
-struct TALER_EXCHANGE_ReserveStatusHandle;
+struct TALER_EXCHANGE_ReservesGetHandle;
 
 
 /**
@@ -1116,16 +1103,16 @@ struct TALER_EXCHANGE_ReserveHistory
  * @param history detailed transaction history, NULL on error
  */
 typedef void
-(*TALER_EXCHANGE_ReserveStatusResultCallback) (void *cls,
-                                               unsigned int http_status,
-                                               enum TALER_ErrorCode ec,
-                                               const json_t *json,
-                                               const struct
-                                               TALER_Amount *balance,
-                                               unsigned int history_length,
-                                               const struct
-                                               TALER_EXCHANGE_ReserveHistory *
-                                               history);
+(*TALER_EXCHANGE_ReservesGetCallback) (void *cls,
+                                       unsigned int http_status,
+                                       enum TALER_ErrorCode ec,
+                                       const json_t *json,
+                                       const struct
+                                       TALER_Amount *balance,
+                                       unsigned int history_length,
+                                       const struct
+                                       TALER_EXCHANGE_ReserveHistory *
+                                       history);
 
 
 /**
@@ -1144,12 +1131,12 @@ typedef void
  * @return a handle for this request; NULL if the inputs are invalid (i.e.
  *         signatures fail to verify).  In this case, the callback is not 
called.
  */
-struct TALER_EXCHANGE_ReserveStatusHandle *
-TALER_EXCHANGE_reserve_status (struct TALER_EXCHANGE_Handle *exchange,
-                               const struct
-                               TALER_ReservePublicKeyP *reserve_pub,
-                               TALER_EXCHANGE_ReserveStatusResultCallback cb,
-                               void *cb_cls);
+struct TALER_EXCHANGE_ReservesGetHandle *
+TALER_EXCHANGE_reserves_get (struct TALER_EXCHANGE_Handle *exchange,
+                             const struct
+                             TALER_ReservePublicKeyP *reserve_pub,
+                             TALER_EXCHANGE_ReservesGetCallback cb,
+                             void *cb_cls);
 
 
 /**
@@ -1159,17 +1146,17 @@ TALER_EXCHANGE_reserve_status (struct 
TALER_EXCHANGE_Handle *exchange,
  * @param rsh the reserve status request handle
  */
 void
-TALER_EXCHANGE_reserve_status_cancel (struct
-                                      TALER_EXCHANGE_ReserveStatusHandle *rsh);
+TALER_EXCHANGE_reserves_get_cancel (struct
+                                    TALER_EXCHANGE_ReservesGetHandle *rhh);
 
 
-/* ********************* /reserve/withdraw *********************** */
+/* ********************* POST /reserves/$RESERVE_PUB/withdraw 
*********************** */
 
 
 /**
- * @brief A /reserve/withdraw Handle
+ * @brief A /reserves/$RESERVE_PUB/withdraw Handle
  */
-struct TALER_EXCHANGE_ReserveWithdrawHandle;
+struct TALER_EXCHANGE_WithdrawHandle;
 
 
 /**
@@ -1184,17 +1171,16 @@ struct TALER_EXCHANGE_ReserveWithdrawHandle;
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_ReserveWithdrawResultCallback) (void *cls,
-                                                 unsigned int http_status,
-                                                 enum TALER_ErrorCode ec,
-                                                 const struct
-                                                 TALER_DenominationSignature *
-                                                 sig,
-                                                 const json_t *full_response);
+(*TALER_EXCHANGE_WithdrawCallback) (void *cls,
+                                    unsigned int http_status,
+                                    enum TALER_ErrorCode ec,
+                                    const struct
+                                    TALER_DenominationSignature *sig,
+                                    const json_t *full_response);
 
 
 /**
- * Withdraw a coin from the exchange using a /reserve/withdraw
+ * Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
  * request.  This API is typically used by a wallet to withdraw from a
  * reserve.
  *
@@ -1214,19 +1200,19 @@ typedef void
  *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
  *         In this case, the callback is not called.
  */
-struct TALER_EXCHANGE_ReserveWithdrawHandle *
-TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange,
-                                 const struct TALER_EXCHANGE_DenomPublicKey 
*pk,
-                                 const struct
-                                 TALER_ReservePrivateKeyP *reserve_priv,
-                                 const struct TALER_PlanchetSecretsP *ps,
-                                 TALER_EXCHANGE_ReserveWithdrawResultCallback
-                                 res_cb,
-                                 void *res_cb_cls);
+struct TALER_EXCHANGE_WithdrawHandle *
+TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle *exchange,
+                         const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                         const struct
+                         TALER_ReservePrivateKeyP *reserve_priv,
+                         const struct TALER_PlanchetSecretsP *ps,
+                         TALER_EXCHANGE_WithdrawCallback
+                         res_cb,
+                         void *res_cb_cls);
 
 
 /**
- * Withdraw a coin from the exchange using a /reserve/withdraw
+ * Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
  * request.  This API is typically used by a wallet to withdraw a tip
  * where the reserve's signature was created by the merchant already.
  *
@@ -1247,30 +1233,28 @@ TALER_EXCHANGE_reserve_withdraw (struct 
TALER_EXCHANGE_Handle *exchange,
  *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
  *         In this case, the callback is not called.
  */
-struct TALER_EXCHANGE_ReserveWithdrawHandle *
-TALER_EXCHANGE_reserve_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
-                                  const struct
-                                  TALER_EXCHANGE_DenomPublicKey *pk,
-                                  const struct
-                                  TALER_ReserveSignatureP *reserve_sig,
-                                  const struct
-                                  TALER_ReservePublicKeyP *reserve_pub,
-                                  const struct TALER_PlanchetSecretsP *ps,
-                                  TALER_EXCHANGE_ReserveWithdrawResultCallback
-                                  res_cb,
-                                  void *res_cb_cls);
+struct TALER_EXCHANGE_WithdrawHandle *
+TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
+                          const struct
+                          TALER_EXCHANGE_DenomPublicKey *pk,
+                          const struct
+                          TALER_ReserveSignatureP *reserve_sig,
+                          const struct
+                          TALER_ReservePublicKeyP *reserve_pub,
+                          const struct TALER_PlanchetSecretsP *ps,
+                          TALER_EXCHANGE_WithdrawCallback
+                          res_cb,
+                          void *res_cb_cls);
 
 
 /**
  * Cancel a withdraw status request.  This function cannot be used
  * on a request handle if a response is already served for it.
  *
- * @param sign the withdraw sign request handle
+ * @param wh the withdraw handle
  */
 void
-TALER_EXCHANGE_reserve_withdraw_cancel (struct
-                                        TALER_EXCHANGE_ReserveWithdrawHandle *
-                                        sign);
+TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh);
 
 
 /* ********************* /refresh/melt+reveal ***************************** */
@@ -1286,7 +1270,7 @@ TALER_EXCHANGE_reserve_withdraw_cancel (struct
  * no money is lost in case of hardware failures, is operation does
  * not actually initiate the request. Instead, it generates a buffer
  * which the caller must store before proceeding with the actual call
- * to #TALER_EXCHANGE_refresh_melt() that will generate the request.
+ * to #TALER_EXCHANGE_melt() that will generate the request.
  *
  * This function does verify that the given request data is internally
  * consistent.  However, the @a melts_sigs are NOT verified.
@@ -1311,7 +1295,7 @@ TALER_EXCHANGE_reserve_withdraw_cancel (struct
  * @return NULL
  *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
  *         Otherwise, pointer to a buffer of @a res_size to store persistently
- *         before proceeding to #TALER_EXCHANGE_refresh_melt().
+ *         before proceeding to #TALER_EXCHANGE_melt().
  *         Non-null results should be freed using GNUNET_free().
  */
 char *
@@ -1328,18 +1312,19 @@ TALER_EXCHANGE_refresh_prepare (const struct
                                 size_t *res_size);
 
 
-/* ********************* /refresh/melt ***************************** */
+/* ********************* /coins/$COIN_PUB/melt ***************************** */
 
 /**
- * @brief A /refresh/melt Handle
+ * @brief A /coins/$COIN_PUB/melt Handle
  */
-struct TALER_EXCHANGE_RefreshMeltHandle;
+struct TALER_EXCHANGE_MeltHandle;
 
 
 /**
- * Callbacks of this type are used to notify the application about the
- * result of the /refresh/melt stage.  If successful, the @a noreveal_index
- * should be committed to disk prior to proceeding 
#TALER_EXCHANGE_refresh_reveal().
+ * Callbacks of this type are used to notify the application about the result
+ * of the /coins/$COIN_PUB/melt stage.  If successful, the @a noreveal_index
+ * should be committed to disk prior to proceeding
+ * #TALER_EXCHANGE_refreshes_reveal().
  *
  * @param cls closure
  * @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for 
successful intermediate response this callback is skipped.
@@ -1351,17 +1336,16 @@ struct TALER_EXCHANGE_RefreshMeltHandle;
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_RefreshMeltCallback) (void *cls,
-                                       unsigned int http_status,
-                                       enum TALER_ErrorCode ec,
-                                       uint32_t noreveal_index,
-                                       const struct
-                                       TALER_ExchangePublicKeyP *sign_key,
-                                       const json_t *full_response);
+(*TALER_EXCHANGE_MeltCallback) (void *cls,
+                                unsigned int http_status,
+                                enum TALER_ErrorCode ec,
+                                uint32_t noreveal_index,
+                                const struct TALER_ExchangePublicKeyP 
*sign_key,
+                                const json_t *full_response);
 
 
 /**
- * Submit a refresh melt request to the exchange and get the exchange's
+ * Submit a melt request to the exchange and get the exchange's
  * response.
  *
  * This API is typically used by a wallet.  Note that to ensure that
@@ -1380,26 +1364,25 @@ typedef void
  * @return a handle for this request; NULL if the argument was invalid.
  *         In this case, neither callback will be called.
  */
-struct TALER_EXCHANGE_RefreshMeltHandle *
-TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange,
-                             size_t refresh_data_length,
-                             const char *refresh_data,
-                             TALER_EXCHANGE_RefreshMeltCallback melt_cb,
-                             void *melt_cb_cls);
+struct TALER_EXCHANGE_MeltHandle *
+TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
+                     size_t refresh_data_length,
+                     const char *refresh_data,
+                     TALER_EXCHANGE_MeltCallback melt_cb,
+                     void *melt_cb_cls);
 
 
 /**
- * Cancel a refresh melt request.  This function cannot be used
+ * Cancel a melt request.  This function cannot be used
  * on a request handle if the callback was already invoked.
  *
- * @param rmh the refresh handle
+ * @param mh the melt handle
  */
 void
-TALER_EXCHANGE_refresh_melt_cancel (struct
-                                    TALER_EXCHANGE_RefreshMeltHandle *rmh);
+TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh);
 
 
-/* ********************* /refresh/reveal ***************************** */
+/* ********************* /refreshes/$RCH/reveal ***************************** 
*/
 
 
 /**
@@ -1420,25 +1403,25 @@ TALER_EXCHANGE_refresh_melt_cancel (struct
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_RefreshRevealCallback) (void *cls,
-                                         unsigned int http_status,
-                                         enum TALER_ErrorCode ec,
-                                         unsigned int num_coins,
-                                         const struct
-                                         TALER_PlanchetSecretsP *coin_privs,
-                                         const struct
-                                         TALER_DenominationSignature *sigs,
-                                         const json_t *full_response);
+(*TALER_EXCHANGE_RefreshesRevealCallback)(void *cls,
+                                          unsigned int http_status,
+                                          enum TALER_ErrorCode ec,
+                                          unsigned int num_coins,
+                                          const struct
+                                          TALER_PlanchetSecretsP *coin_privs,
+                                          const struct
+                                          TALER_DenominationSignature *sigs,
+                                          const json_t *full_response);
 
 
 /**
- * @brief A /refresh/reveal Handle
+ * @brief A /refreshes/$RCH/reveal Handle
  */
-struct TALER_EXCHANGE_RefreshRevealHandle;
+struct TALER_EXCHANGE_RefreshesRevealHandle;
 
 
 /**
- * Submit a /refresh/reval request to the exchange and get the exchange's
+ * Submit a /refreshes/$RCH/reval request to the exchange and get the 
exchange's
  * response.
  *
  * This API is typically used by a wallet.  Note that to ensure that
@@ -1452,20 +1435,21 @@ struct TALER_EXCHANGE_RefreshRevealHandle;
  * @param refresh_data the refresh data as returned from
           #TALER_EXCHANGE_refresh_prepare())
  * @param noreveal_index response from the exchange to the
- *        #TALER_EXCHANGE_refresh_melt() invocation
+ *        #TALER_EXCHANGE_melt() invocation
  * @param reveal_cb the callback to call with the final result of the
  *        refresh operation
  * @param reveal_cb_cls closure for the above callback
  * @return a handle for this request; NULL if the argument was invalid.
  *         In this case, neither callback will be called.
  */
-struct TALER_EXCHANGE_RefreshRevealHandle *
-TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
-                               size_t refresh_data_length,
-                               const char *refresh_data,
-                               uint32_t noreveal_index,
-                               TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
-                               void *reveal_cb_cls);
+struct TALER_EXCHANGE_RefreshesRevealHandle *
+TALER_EXCHANGE_refreshes_reveal (struct TALER_EXCHANGE_Handle *exchange,
+                                 size_t refresh_data_length,
+                                 const char *refresh_data,
+                                 uint32_t noreveal_index,
+                                 TALER_EXCHANGE_RefreshesRevealCallback
+                                 reveal_cb,
+                                 void *reveal_cb_cls);
 
 
 /**
@@ -1475,24 +1459,25 @@ TALER_EXCHANGE_refresh_reveal (struct 
TALER_EXCHANGE_Handle *exchange,
  * @param rrh the refresh reval handle
  */
 void
-TALER_EXCHANGE_refresh_reveal_cancel (struct
-                                      TALER_EXCHANGE_RefreshRevealHandle *rrh);
+TALER_EXCHANGE_refreshes_reveal_cancel (struct
+                                        TALER_EXCHANGE_RefreshesRevealHandle *
+                                        rrh);
 
 
-/* ********************* /refresh/link ***************************** */
+/* ********************* /coins/$COIN_PUB/link ***************************** */
 
 
 /**
- * @brief A /refresh/link Handle
+ * @brief A /coins/$COIN_PUB/link Handle
  */
-struct TALER_EXCHANGE_RefreshLinkHandle;
+struct TALER_EXCHANGE_LinkHandle;
 
 
 /**
- * Callbacks of this type are used to return the final result of
- * submitting a /refresh/link request to a exchange.  If the operation was
- * successful, this function returns the signatures over the coins
- * that were created when the original coin was melted.
+ * Callbacks of this type are used to return the final result of submitting a
+ * /coins/$COIN_PUB/link request to a exchange.  If the operation was
+ * successful, this function returns the signatures over the coins that were
+ * created when the original coin was melted.
  *
  * @param cls closure
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
@@ -1505,26 +1490,24 @@ struct TALER_EXCHANGE_RefreshLinkHandle;
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_RefreshLinkCallback) (void *cls,
-                                       unsigned int http_status,
-                                       enum TALER_ErrorCode ec,
-                                       unsigned int num_coins,
-                                       const struct
-                                       TALER_CoinSpendPrivateKeyP *coin_privs,
-                                       const struct
-                                       TALER_DenominationSignature *sigs,
-                                       const struct
-                                       TALER_DenominationPublicKey *pubs,
-                                       const json_t *full_response);
+(*TALER_EXCHANGE_LinkCallback) (void *cls,
+                                unsigned int http_status,
+                                enum TALER_ErrorCode ec,
+                                unsigned int num_coins,
+                                const struct
+                                TALER_CoinSpendPrivateKeyP *coin_privs,
+                                const struct
+                                TALER_DenominationSignature *sigs,
+                                const struct
+                                TALER_DenominationPublicKey *pubs,
+                                const json_t *full_response);
 
 
 /**
- * Submit a refresh link request to the exchange and get the
- * exchange's response.
+ * Submit a link request to the exchange and get the exchange's response.
  *
- * This API is typically not used by anyone, it is more a threat
- * against those trying to receive a funds transfer by abusing the
- * /refresh protocol.
+ * This API is typically not used by anyone, it is more a threat against those
+ * trying to receive a funds transfer by abusing the refresh protocol.
  *
  * @param exchange the exchange handle; the exchange must be ready to operate
  * @param coin_priv private key to request link data for
@@ -1533,30 +1516,29 @@ typedef void
  * @param link_cb_cls closure for @a link_cb
  * @return a handle for this request
  */
-struct TALER_EXCHANGE_RefreshLinkHandle *
-TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
-                             const struct TALER_CoinSpendPrivateKeyP 
*coin_priv,
-                             TALER_EXCHANGE_RefreshLinkCallback link_cb,
-                             void *link_cb_cls);
+struct TALER_EXCHANGE_LinkHandle *
+TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
+                     const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                     TALER_EXCHANGE_LinkCallback link_cb,
+                     void *link_cb_cls);
 
 
 /**
- * Cancel a refresh link request.  This function cannot be used
+ * Cancel a link request.  This function cannot be used
  * on a request handle if the callback was already invoked.
  *
- * @param rlh the refresh link handle
+ * @param lh the link handle
  */
 void
-TALER_EXCHANGE_refresh_link_cancel (struct
-                                    TALER_EXCHANGE_RefreshLinkHandle *rlh);
+TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh);
 
 
-/* ********************* /track/transfer *********************** */
+/* ********************* /transfers/$WTID *********************** */
 
 /**
- * @brief A /track/transfer Handle
+ * @brief A /transfers/$WTID Handle
  */
-struct TALER_EXCHANGE_TrackTransferHandle;
+struct TALER_EXCHANGE_TransfersGetHandle;
 
 
 /**
@@ -1578,20 +1560,20 @@ struct TALER_EXCHANGE_TrackTransferHandle;
  * @param details array with details about the combined transactions
  */
 typedef void
-(*TALER_EXCHANGE_TrackTransferCallback)(void *cls,
-                                        unsigned int http_status,
-                                        enum TALER_ErrorCode ec,
-                                        const struct
-                                        TALER_ExchangePublicKeyP *sign_key,
-                                        const json_t *json,
-                                        const struct GNUNET_HashCode *h_wire,
-                                        struct GNUNET_TIME_Absolute
-                                        execution_time,
-                                        const struct TALER_Amount 
*total_amount,
-                                        const struct TALER_Amount *wire_fee,
-                                        unsigned int details_length,
-                                        const struct
-                                        TALER_TrackTransferDetails *details);
+(*TALER_EXCHANGE_TransfersGetCallback)(void *cls,
+                                       unsigned int http_status,
+                                       enum TALER_ErrorCode ec,
+                                       const struct
+                                       TALER_ExchangePublicKeyP *sign_key,
+                                       const json_t *json,
+                                       const struct GNUNET_HashCode *h_wire,
+                                       struct GNUNET_TIME_Absolute
+                                       execution_time,
+                                       const struct TALER_Amount *total_amount,
+                                       const struct TALER_Amount *wire_fee,
+                                       unsigned int details_length,
+                                       const struct
+                                       TALER_TrackTransferDetails *details);
 
 
 /**
@@ -1604,12 +1586,12 @@ typedef void
  * @param cb_cls closure for @a cb
  * @return handle to cancel operation
  */
-struct TALER_EXCHANGE_TrackTransferHandle *
-TALER_EXCHANGE_track_transfer (struct TALER_EXCHANGE_Handle *exchange,
-                               const struct
-                               TALER_WireTransferIdentifierRawP *wtid,
-                               TALER_EXCHANGE_TrackTransferCallback cb,
-                               void *cb_cls);
+struct TALER_EXCHANGE_TransfersGetHandle *
+TALER_EXCHANGE_transfers_get (struct TALER_EXCHANGE_Handle *exchange,
+                              const struct
+                              TALER_WireTransferIdentifierRawP *wtid,
+                              TALER_EXCHANGE_TransfersGetCallback cb,
+                              void *cb_cls);
 
 
 /**
@@ -1619,17 +1601,17 @@ TALER_EXCHANGE_track_transfer (struct 
TALER_EXCHANGE_Handle *exchange,
  * @param wdh the wire deposits request handle
  */
 void
-TALER_EXCHANGE_track_transfer_cancel (struct
-                                      TALER_EXCHANGE_TrackTransferHandle *wdh);
+TALER_EXCHANGE_transfers_get_cancel (struct
+                                     TALER_EXCHANGE_TransfersGetHandle *wdh);
 
 
-/* ********************* /track/transaction *********************** */
+/* ********************* GET /deposits/ *********************** */
 
 
 /**
- * @brief A /track/transaction Handle
+ * @brief A /deposits/ GET Handle
  */
-struct TALER_EXCHANGE_TrackTransactionHandle;
+struct TALER_EXCHANGE_DepositGetHandle;
 
 
 /**
@@ -1647,19 +1629,19 @@ struct TALER_EXCHANGE_TrackTransactionHandle;
  * @param coin_contribution contribution to the total amount by this coin (can 
be NULL)
  */
 typedef void
-(*TALER_EXCHANGE_TrackTransactionCallback)(void *cls,
-                                           unsigned int http_status,
-                                           enum TALER_ErrorCode ec,
-                                           const struct
-                                           TALER_ExchangePublicKeyP *sign_key,
-                                           const json_t *json,
-                                           const struct
-                                           TALER_WireTransferIdentifierRawP *
-                                           wtid,
-                                           struct GNUNET_TIME_Absolute
-                                           execution_time,
-                                           const struct
-                                           TALER_Amount *coin_contribution);
+(*TALER_EXCHANGE_DepositGetCallback)(void *cls,
+                                     unsigned int http_status,
+                                     enum TALER_ErrorCode ec,
+                                     const struct
+                                     TALER_ExchangePublicKeyP *sign_key,
+                                     const json_t *json,
+                                     const struct
+                                     TALER_WireTransferIdentifierRawP *
+                                     wtid,
+                                     struct GNUNET_TIME_Absolute
+                                     execution_time,
+                                     const struct
+                                     TALER_Amount *coin_contribution);
 
 
 /**
@@ -1676,17 +1658,17 @@ typedef void
  * @param cb_cls closure for @a cb
  * @return handle to abort request
  */
-struct TALER_EXCHANGE_TrackTransactionHandle *
-TALER_EXCHANGE_track_transaction (struct TALER_EXCHANGE_Handle *exchange,
-                                  const struct
-                                  TALER_MerchantPrivateKeyP *merchant_priv,
-                                  const struct GNUNET_HashCode *h_wire,
-                                  const struct
-                                  GNUNET_HashCode *h_contract_terms,
-                                  const struct
-                                  TALER_CoinSpendPublicKeyP *coin_pub,
-                                  TALER_EXCHANGE_TrackTransactionCallback cb,
-                                  void *cb_cls);
+struct TALER_EXCHANGE_DepositGetHandle *
+TALER_EXCHANGE_deposits_get (struct TALER_EXCHANGE_Handle *exchange,
+                             const struct
+                             TALER_MerchantPrivateKeyP *merchant_priv,
+                             const struct GNUNET_HashCode *h_wire,
+                             const struct
+                             GNUNET_HashCode *h_contract_terms,
+                             const struct
+                             TALER_CoinSpendPublicKeyP *coin_pub,
+                             TALER_EXCHANGE_DepositGetCallback cb,
+                             void *cb_cls);
 
 
 /**
@@ -1696,9 +1678,9 @@ TALER_EXCHANGE_track_transaction (struct 
TALER_EXCHANGE_Handle *exchange,
  * @param dwh the wire deposits request handle
  */
 void
-TALER_EXCHANGE_track_transaction_cancel (struct
-                                         TALER_EXCHANGE_TrackTransactionHandle 
*
-                                         dwh);
+TALER_EXCHANGE_deposits_get_cancel (struct
+                                    TALER_EXCHANGE_DepositGetHandle *
+                                    dwh);
 
 
 /**
@@ -1722,6 +1704,46 @@ TALER_EXCHANGE_verify_coin_history (const struct
                                     struct TALER_Amount *total);
 
 
+/**
+ * Parse history given in JSON format and return it in binary
+ * format.
+ *
+ * @param exchange connection to the exchange we can use
+ * @param history JSON array with the history
+ * @param reserve_pub public key of the reserve to inspect
+ * @param currency currency we expect the balance to be in
+ * @param[out] balance final balance
+ * @param history_length number of entries in @a history
+ * @param[out] rhistory array of length @a history_length, set to the
+ *             parsed history entries
+ * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
+ *         were set,
+ *         #GNUNET_SYSERR if there was a protocol violation in @a history
+ */
+int
+TALER_EXCHANGE_parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
+                                      const json_t *history,
+                                      const struct
+                                      TALER_ReservePublicKeyP *reserve_pub,
+                                      const char *currency,
+                                      struct TALER_Amount *balance,
+                                      unsigned int history_length,
+                                      struct TALER_EXCHANGE_ReserveHistory *
+                                      rhistory);
+
+
+/**
+ * Free memory (potentially) allocated by 
#TALER_EXCHANGE_parse_reserve_history().
+ *
+ * @param rhistory result to free
+ * @param len number of entries in @a rhistory
+ */
+void
+TALER_EXCHANGE_free_reserve_history (struct
+                                     TALER_EXCHANGE_ReserveHistory *rhistory,
+                                     unsigned int len);
+
+
 /* ********************* /recoup *********************** */
 
 
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 4db76dd7..754ac1ef 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -2087,8 +2087,8 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session database handle to use
    * @param rc commitment to use for the lookup
-   * @param[out] refresh_melt where to store the result; note that
-   *             refresh_melt->session.coin.denom_sig will be set to NULL
+   * @param[out] melt where to store the result; note that
+   *             melt->session.coin.denom_sig will be set to NULL
    *             and is not fetched by this routine (as it is not needed by 
the client)
    * @return transaction status
    */
@@ -2096,7 +2096,7 @@ struct TALER_EXCHANGEDB_Plugin
   (*get_melt)(void *cls,
               struct TALER_EXCHANGEDB_Session *session,
               const struct TALER_RefreshCommitmentP *rc,
-              struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt);
+              struct TALER_EXCHANGEDB_RefreshMelt *melt);
 
 
   /**
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 5aa50155..7dadc1f4 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -1307,10 +1307,10 @@ TALER_TESTING_cmd_deposit_with_retry (struct 
TALER_TESTING_Command cmd);
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt (const char *label,
-                                const char *coin_reference,
-                                unsigned int expected_response_code,
-                                ...);
+TALER_TESTING_cmd_melt (const char *label,
+                        const char *coin_reference,
+                        unsigned int expected_response_code,
+                        ...);
 
 
 /**
@@ -1326,10 +1326,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_double (const char *label,
-                                       const char *coin_reference,
-                                       unsigned int expected_response_code,
-                                       ...);
+TALER_TESTING_cmd_melt_double (const char *label,
+                               const char *coin_reference,
+                               unsigned int expected_response_code,
+                               ...);
 
 
 /**
@@ -1339,7 +1339,7 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
  * @return modified command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd);
+TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd);
 
 
 /**
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index f03522a6..87f34309 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -25,13 +25,16 @@ libtalerexchange_la_SOURCES = \
   exchange_api_common.c \
   exchange_api_handle.c exchange_api_handle.h \
   exchange_api_deposit.c \
+  exchange_api_deposits_get.c \
+  exchange_api_link.c \
+  exchange_api_melt.c \
   exchange_api_recoup.c \
-  exchange_api_refresh.c \
-  exchange_api_refresh_link.c \
+  exchange_api_refresh_common.c exchange_api_refresh_common.h \
+  exchange_api_refreshes_reveal.c \
   exchange_api_refund.c \
-  exchange_api_reserve.c \
-  exchange_api_track_transaction.c \
-  exchange_api_track_transfer.c \
+  exchange_api_reserves_get.c \
+  exchange_api_transfers_get.c \
+  exchange_api_withdraw.c \
   exchange_api_wire.c
 libtalerexchange_la_LIBADD = \
   libtalerauditor.la \
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index ae02b3db..6b0d638e 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -26,6 +26,422 @@
 #include "taler_signatures.h"
 
 
+/**
+ * Parse history given in JSON format and return it in binary
+ * format.
+ *
+ * @param exchange connection to the exchange we can use
+ * @param history JSON array with the history
+ * @param reserve_pub public key of the reserve to inspect
+ * @param currency currency we expect the balance to be in
+ * @param[out] balance final balance
+ * @param history_length number of entries in @a history
+ * @param[out] rhistory array of length @a history_length, set to the
+ *             parsed history entries
+ * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
+ *         were set,
+ *         #GNUNET_SYSERR if there was a protocol violation in @a history
+ */
+int
+TALER_EXCHANGE_parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
+                                      const json_t *history,
+                                      const struct
+                                      TALER_ReservePublicKeyP *reserve_pub,
+                                      const char *currency,
+                                      struct TALER_Amount *balance,
+                                      unsigned int history_length,
+                                      struct TALER_EXCHANGE_ReserveHistory *
+                                      rhistory)
+{
+  struct GNUNET_HashCode uuid[history_length];
+  unsigned int uuid_off;
+  struct TALER_Amount total_in;
+  struct TALER_Amount total_out;
+  size_t off;
+
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &total_in));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &total_out));
+  uuid_off = 0;
+  for (off = 0; off<history_length; off++)
+  {
+    json_t *transaction;
+    struct TALER_Amount amount;
+    const char *type;
+    struct GNUNET_JSON_Specification hist_spec[] = {
+      GNUNET_JSON_spec_string ("type", &type),
+      TALER_JSON_spec_amount ("amount",
+                              &amount),
+      /* 'wire' and 'signature' are optional depending on 'type'! */
+      GNUNET_JSON_spec_end ()
+    };
+
+    transaction = json_array_get (history,
+                                  off);
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (transaction,
+                           hist_spec,
+                           NULL, NULL))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    rhistory[off].amount = amount;
+
+    if (0 == strcasecmp (type,
+                         "DEPOSIT"))
+    {
+      const char *wire_url;
+      void *wire_reference;
+      size_t wire_reference_size;
+      struct GNUNET_TIME_Absolute timestamp;
+
+      struct GNUNET_JSON_Specification withdraw_spec[] = {
+        GNUNET_JSON_spec_varsize ("wire_reference",
+                                  &wire_reference,
+                                  &wire_reference_size),
+        GNUNET_JSON_spec_absolute_time ("timestamp",
+                                        &timestamp),
+        GNUNET_JSON_spec_string ("sender_account_url",
+                                 &wire_url),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_in,
+                            &total_in,
+                            &amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             withdraw_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url);
+      rhistory[off].details.in_details.wire_reference = wire_reference;
+      rhistory[off].details.in_details.wire_reference_size =
+        wire_reference_size;
+      rhistory[off].details.in_details.timestamp = timestamp;
+      /* end type==DEPOSIT */
+    }
+    else if (0 == strcasecmp (type,
+                              "WITHDRAW"))
+    {
+      struct TALER_ReserveSignatureP sig;
+      struct TALER_WithdrawRequestPS withdraw_purpose;
+      struct GNUNET_JSON_Specification withdraw_spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+                                     &sig),
+        TALER_JSON_spec_amount_nbo ("withdraw_fee",
+                                    &withdraw_purpose.withdraw_fee),
+        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                     &withdraw_purpose.h_denomination_pub),
+        GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
+                                     &withdraw_purpose.h_coin_envelope),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_WITHDRAWAL;
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             withdraw_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      withdraw_purpose.purpose.size
+        = htonl (sizeof (withdraw_purpose));
+      withdraw_purpose.purpose.purpose
+        = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+      withdraw_purpose.reserve_pub = *reserve_pub;
+      TALER_amount_hton (&withdraw_purpose.amount_with_fee,
+                         &amount);
+      /* Check that the signature is a valid withdraw request */
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
+                                      &withdraw_purpose.purpose,
+                                      &sig.eddsa_signature,
+                                      &reserve_pub->eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        GNUNET_JSON_parse_free (withdraw_spec);
+        return GNUNET_SYSERR;
+      }
+      /* check that withdraw fee matches expectations! */
+      {
+        const struct TALER_EXCHANGE_Keys *key_state;
+        const struct TALER_EXCHANGE_DenomPublicKey *dki;
+        struct TALER_Amount fee;
+
+        key_state = TALER_EXCHANGE_get_keys (exchange);
+        dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
+                                                           &withdraw_purpose.
+                                                           h_denomination_pub);
+        TALER_amount_ntoh (&fee,
+                           &withdraw_purpose.withdraw_fee);
+        if ( (GNUNET_YES !=
+              TALER_amount_cmp_currency (&fee,
+                                         &dki->fee_withdraw)) ||
+             (0 !=
+              TALER_amount_cmp (&fee,
+                                &dki->fee_withdraw)) )
+        {
+          GNUNET_break_op (0);
+          GNUNET_JSON_parse_free (withdraw_spec);
+          return GNUNET_SYSERR;
+        }
+      }
+      rhistory[off].details.out_authorization_sig
+        = json_object_get (transaction,
+                           "signature");
+      /* Check check that the same withdraw transaction
+         isn't listed twice by the exchange. We use the
+         "uuid" array to remember the hashes of all
+         purposes, and compare the hashes to find
+         duplicates. *///
+      GNUNET_CRYPTO_hash (&withdraw_purpose,
+                          ntohl (withdraw_purpose.purpose.size),
+                          &uuid[uuid_off]);
+      for (unsigned int i = 0; i<uuid_off; i++)
+      {
+        if (0 == GNUNET_memcmp (&uuid[uuid_off],
+                                &uuid[i]))
+        {
+          GNUNET_break_op (0);
+          GNUNET_JSON_parse_free (withdraw_spec);
+          return GNUNET_SYSERR;
+        }
+      }
+      uuid_off++;
+
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_out,
+                            &total_out,
+                            &amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        GNUNET_JSON_parse_free (withdraw_spec);
+        return GNUNET_SYSERR;
+      }
+      /* end type==WITHDRAW */
+    }
+    else if (0 == strcasecmp (type,
+                              "RECOUP"))
+    {
+      struct TALER_RecoupConfirmationPS pc;
+      struct GNUNET_TIME_Absolute timestamp;
+      const struct TALER_EXCHANGE_Keys *key_state;
+      struct GNUNET_JSON_Specification recoup_spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                     &pc.coin_pub),
+        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+                                     &rhistory[off].details.recoup_details.
+                                     exchange_sig),
+        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+                                     &rhistory[off].details.recoup_details.
+                                     exchange_pub),
+        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
+                                            &pc.timestamp),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_RECOUP;
+      rhistory[off].amount = amount;
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             recoup_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      rhistory[off].details.recoup_details.coin_pub = pc.coin_pub;
+      TALER_amount_hton (&pc.recoup_amount,
+                         &amount);
+      pc.purpose.size = htonl (sizeof (pc));
+      pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP);
+      pc.reserve_pub = *reserve_pub;
+      timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
+      rhistory[off].details.recoup_details.timestamp = timestamp;
+
+      key_state = TALER_EXCHANGE_get_keys (exchange);
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_test_signing_key (key_state,
+                                           &rhistory[off].details.
+                                           recoup_details.exchange_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
+                                      &pc.purpose,
+                                      &rhistory[off].details.recoup_details.
+                                      exchange_sig.eddsa_signature,
+                                      &rhistory[off].details.recoup_details.
+                                      exchange_pub.eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_in,
+                            &total_in,
+                            &rhistory[off].amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      /* end type==RECOUP */
+    }
+    else if (0 == strcasecmp (type,
+                              "CLOSING"))
+    {
+      const struct TALER_EXCHANGE_Keys *key_state;
+      struct TALER_ReserveCloseConfirmationPS rcc;
+      struct GNUNET_TIME_Absolute timestamp;
+      struct GNUNET_JSON_Specification closing_spec[] = {
+        GNUNET_JSON_spec_string ("receiver_account_details",
+                                 &rhistory[off].details.close_details.
+                                 receiver_account_details),
+        GNUNET_JSON_spec_fixed_auto ("wtid",
+                                     
&rhistory[off].details.close_details.wtid),
+        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+                                     &rhistory[off].details.close_details.
+                                     exchange_sig),
+        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+                                     &rhistory[off].details.close_details.
+                                     exchange_pub),
+        TALER_JSON_spec_amount_nbo ("closing_fee",
+                                    &rcc.closing_fee),
+        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
+                                            &rcc.timestamp),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_CLOSE;
+      rhistory[off].amount = amount;
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             closing_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      TALER_amount_hton (&rcc.closing_amount,
+                         &amount);
+      GNUNET_CRYPTO_hash (
+        rhistory[off].details.close_details.receiver_account_details,
+        strlen (
+          rhistory[off].details.close_details.receiver_account_details) + 1,
+        &rcc.h_wire);
+      rcc.wtid = rhistory[off].details.close_details.wtid;
+      rcc.purpose.size = htonl (sizeof (rcc));
+      rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
+      rcc.reserve_pub = *reserve_pub;
+      timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
+      rhistory[off].details.close_details.timestamp = timestamp;
+
+      key_state = TALER_EXCHANGE_get_keys (exchange);
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_test_signing_key (key_state,
+                                           
&rhistory[off].details.close_details.
+                                           exchange_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
+                                      &rcc.purpose,
+                                      &rhistory[off].details.close_details.
+                                      exchange_sig.eddsa_signature,
+                                      &rhistory[off].details.close_details.
+                                      exchange_pub.eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_out,
+                            &total_out,
+                            &rhistory[off].amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      /* end type==CLOSING */
+    }
+    else
+    {
+      /* unexpected 'type', protocol incompatibility, complain! */
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  }
+
+  /* check balance = total_in - total_out < withdraw-amount */
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (balance,
+                             &total_in,
+                             &total_out))
+  {
+    /* total_in < total_out, why did the exchange ever allow this!? */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Free memory (potentially) allocated by 
#TALER_EXCHANGE_parse_reserve_history().
+ *
+ * @param rhistory result to free
+ * @param len number of entries in @a rhistory
+ */
+void
+TALER_EXCHANGE_free_reserve_history (struct
+                                     TALER_EXCHANGE_ReserveHistory *rhistory,
+                                     unsigned int len)
+{
+  for (unsigned int i = 0; i<len; i++)
+  {
+    switch (rhistory[i].type)
+    {
+    case TALER_EXCHANGE_RTT_DEPOSIT:
+      GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
+      GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
+      break;
+    case TALER_EXCHANGE_RTT_WITHDRAWAL:
+      break;
+    case TALER_EXCHANGE_RTT_RECOUP:
+      break;
+    case TALER_EXCHANGE_RTT_CLOSE:
+      /* FIXME: should we free "receiver_account_details" ? */
+      break;
+    }
+  }
+  GNUNET_free (rhistory);
+}
+
+
 /**
  * Verify a coins transaction history as returned by the exchange.
  *
@@ -382,17 +798,17 @@ TALER_EXCHANGE_verify_coin_history (const struct
  * @return NULL on error (@a exchange_pub not known)
  */
 const struct TALER_EXCHANGE_SigningPublicKey *
-TALER_EXCHANGE_get_exchange_signing_key_info (const struct
-                                              TALER_EXCHANGE_Keys *keys,
-                                              const struct
-                                              TALER_ExchangePublicKeyP *
-                                              exchange_pub)
+TALER_EXCHANGE_get_signing_key_info (const struct
+                                     TALER_EXCHANGE_Keys *keys,
+                                     const struct
+                                     TALER_ExchangePublicKeyP *
+                                     exchange_pub)
 {
   for (unsigned int i = 0; i<keys->num_sign_keys; i++)
   {
-    const struct TALER_EXCHANGE_SigningPublicKey *spk;
+    const struct TALER_EXCHANGE_SigningPublicKey *spk
+      = &keys->sign_keys[i];
 
-    spk = &keys->sign_keys[i];
     if (0 == GNUNET_memcmp (exchange_pub,
                             &spk->key))
       return spk;
diff --git a/src/lib/exchange_api_curl_defaults.c 
b/src/lib/exchange_api_curl_defaults.c
index d1e84f95..26c1ac7d 100644
--- a/src/lib/exchange_api_curl_defaults.c
+++ b/src/lib/exchange_api_curl_defaults.c
@@ -30,7 +30,7 @@
  * @param url URL to query
  */
 CURL *
-TEL_curl_easy_get (const char *url)
+TALER_EXCHANGE_curl_easy_get_ (const char *url)
 {
   CURL *eh;
 
@@ -43,6 +43,12 @@ TEL_curl_easy_get (const char *url)
                  curl_easy_setopt (eh,
                                    CURLOPT_FOLLOWLOCATION,
                                    1L));
+  /* limit MAXREDIRS to 5 as a simple security measure against
+     a potential infinite loop caused by a malicious target */
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_MAXREDIRS,
+                                   5L));
   GNUNET_assert (CURLE_OK ==
                  curl_easy_setopt (eh,
                                    CURLOPT_TCP_FASTOPEN,
diff --git a/src/lib/exchange_api_curl_defaults.h 
b/src/lib/exchange_api_curl_defaults.h
index 7ca1d2e3..009d72ab 100644
--- a/src/lib/exchange_api_curl_defaults.h
+++ b/src/lib/exchange_api_curl_defaults.h
@@ -36,6 +36,6 @@
  * @param url URL to query
  */
 CURL *
-TEL_curl_easy_get (const char *url);
+TALER_EXCHANGE_curl_easy_get_ (const char *url);
 
 #endif /* _TALER_CURL_DEFAULTS_H */
diff --git a/src/lib/exchange_api_deposit.c b/src/lib/exchange_api_deposit.c
index 20a87c33..24b9f6fe 100644
--- a/src/lib/exchange_api_deposit.c
+++ b/src/lib/exchange_api_deposit.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2018, 2019 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -146,8 +146,8 @@ auditor_cb (void *cls,
               "Will provide deposit confirmation to auditor `%s'\n",
               TALER_B2S (auditor_pub));
   key_state = TALER_EXCHANGE_get_keys (dh->exchange);
-  spk = TALER_EXCHANGE_get_signing_key_details (key_state,
-                                                &dh->exchange_pub);
+  spk = TALER_EXCHANGE_get_signing_key_info (key_state,
+                                             &dh->exchange_pub);
   GNUNET_assert (NULL != spk);
   TALER_amount_ntoh (&amount_without_fee,
                      &dh->depconf.amount_without_fee);
@@ -513,7 +513,23 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle 
*exchange,
   struct GNUNET_HashCode h_wire;
   struct GNUNET_HashCode denom_pub_hash;
   struct TALER_Amount amount_without_fee;
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
 
+  {
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/coins/%s/deposit",
+                     pub_str);
+  }
   (void) GNUNET_TIME_round_abs (&wire_deadline);
   (void) GNUNET_TIME_round_abs (&refund_deadline);
   GNUNET_assert (refund_deadline.abs_value_us <= wire_deadline.abs_value_us);
@@ -557,7 +573,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle 
*exchange,
 
   deposit_obj = json_pack ("{s:o, s:O," /* f/wire */
                            " s:o, s:o," /* h_wire, h_contract_terms */
-                           " s:o, s:o," /* coin_pub, denom_pub */
+                           " s:o," /* denom_pub */
                            " s:o, s:o," /* ub_sig, timestamp */
                            " s:o," /* merchant_pub */
                            " s:o, s:o," /* refund_deadline, wire_deadline */
@@ -567,7 +583,6 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle 
*exchange,
                            "h_wire", GNUNET_JSON_from_data_auto (&h_wire),
                            "h_contract_terms", GNUNET_JSON_from_data_auto (
                              h_contract_terms),
-                           "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
                            "denom_pub_hash", GNUNET_JSON_from_data_auto (
                              &denom_pub_hash),
                            "ub_sig", GNUNET_JSON_from_rsa_signature (
@@ -592,7 +607,8 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle 
*exchange,
   dh->exchange = exchange;
   dh->cb = cb;
   dh->cb_cls = cb_cls;
-  dh->url = TEAH_path_to_url (exchange, "/deposit");
+  dh->url = TEAH_path_to_url (exchange,
+                              arg_str);
   dh->depconf.purpose.size = htonl (sizeof (struct
                                             TALER_DepositConfirmationPS));
   dh->depconf.purpose.purpose = htonl (
@@ -610,7 +626,7 @@ TALER_EXCHANGE_deposit (struct TALER_EXCHANGE_Handle 
*exchange,
   dh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
                                         not copy the pointer */
 
-  eh = TEL_curl_easy_get (dh->url);
+  eh = TALER_EXCHANGE_curl_easy_get_ (dh->url);
   if (GNUNET_OK !=
       TALER_curl_easy_post (&dh->ctx,
                             eh,
diff --git a/src/lib/exchange_api_track_transaction.c 
b/src/lib/exchange_api_deposits_get.c
similarity index 68%
rename from src/lib/exchange_api_track_transaction.c
rename to src/lib/exchange_api_deposits_get.c
index adf9373b..40d86401 100644
--- a/src/lib/exchange_api_track_transaction.c
+++ b/src/lib/exchange_api_deposits_get.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/exchange_api_track_transaction.c
- * @brief Implementation of the /track/transaction request of the exchange's 
HTTP API
+ * @file lib/exchange_api_deposits_get.c
+ * @brief Implementation of the /deposits/ GET request
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -35,7 +35,7 @@
 /**
  * @brief A Deposit Wtid Handle
  */
-struct TALER_EXCHANGE_TrackTransactionHandle
+struct TALER_EXCHANGE_DepositGetHandle
 {
 
   /**
@@ -62,7 +62,7 @@ struct TALER_EXCHANGE_TrackTransactionHandle
   /**
    * Function to call with the result.
    */
-  TALER_EXCHANGE_TrackTransactionCallback cb;
+  TALER_EXCHANGE_DepositGetCallback cb;
 
   /**
    * Closure for @a cb.
@@ -89,7 +89,7 @@ struct TALER_EXCHANGE_TrackTransactionHandle
  */
 static int
 verify_deposit_wtid_signature_ok (const struct
-                                  TALER_EXCHANGE_TrackTransactionHandle *dwh,
+                                  TALER_EXCHANGE_DepositGetHandle *dwh,
                                   const json_t *json,
                                   struct TALER_ExchangePublicKeyP 
*exchange_pub)
 {
@@ -134,7 +134,7 @@ verify_deposit_wtid_signature_ok (const struct
  * Function called when we're done processing the
  * HTTP /track/transaction request.
  *
- * @param cls the `struct TALER_EXCHANGE_TrackTransactionHandle`
+ * @param cls the `struct TALER_EXCHANGE_DepositGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response parsed JSON result, NULL on error
  */
@@ -143,7 +143,7 @@ handle_deposit_wtid_finished (void *cls,
                               long response_code,
                               const void *response)
 {
-  struct TALER_EXCHANGE_TrackTransactionHandle *dwh = cls;
+  struct TALER_EXCHANGE_DepositGetHandle *dwh = cls;
   const struct TALER_WireTransferIdentifierRawP *wtid = NULL;
   struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS;
   const struct TALER_Amount *coin_contribution = NULL;
@@ -247,7 +247,7 @@ handle_deposit_wtid_finished (void *cls,
            wtid,
            execution_time,
            coin_contribution);
-  TALER_EXCHANGE_track_transaction_cancel (dwh);
+  TALER_EXCHANGE_deposits_get_cancel (dwh);
 }
 
 
@@ -264,24 +264,28 @@ handle_deposit_wtid_finished (void *cls,
  * @param cb_cls closure for @a cb
  * @return handle to abort request
  */
-struct TALER_EXCHANGE_TrackTransactionHandle *
-TALER_EXCHANGE_track_transaction (struct TALER_EXCHANGE_Handle *exchange,
-                                  const struct
-                                  TALER_MerchantPrivateKeyP *merchant_priv,
-                                  const struct GNUNET_HashCode *h_wire,
-                                  const struct
-                                  GNUNET_HashCode *h_contract_terms,
-                                  const struct
-                                  TALER_CoinSpendPublicKeyP *coin_pub,
-                                  TALER_EXCHANGE_TrackTransactionCallback cb,
-                                  void *cb_cls)
+struct TALER_EXCHANGE_DepositGetHandle *
+TALER_EXCHANGE_deposits_get (struct TALER_EXCHANGE_Handle *exchange,
+                             const struct
+                             TALER_MerchantPrivateKeyP *merchant_priv,
+                             const struct GNUNET_HashCode *h_wire,
+                             const struct
+                             GNUNET_HashCode *h_contract_terms,
+                             const struct
+                             TALER_CoinSpendPublicKeyP *coin_pub,
+                             TALER_EXCHANGE_DepositGetCallback cb,
+                             void *cb_cls)
 {
   struct TALER_DepositTrackPS dtp;
   struct TALER_MerchantSignatureP merchant_sig;
-  struct TALER_EXCHANGE_TrackTransactionHandle *dwh;
+  struct TALER_EXCHANGE_DepositGetHandle *dwh;
   struct GNUNET_CURL_Context *ctx;
-  json_t *deposit_wtid_obj;
   CURL *eh;
+  char arg_str[(sizeof (struct TALER_CoinSpendPublicKeyP)
+                + sizeof (struct GNUNET_HashCode)
+                + sizeof (struct TALER_MerchantPublicKeyP)
+                + sizeof (struct GNUNET_HashCode)
+                + sizeof (struct TALER_MerchantSignatureP)) * 2 + 48];
 
   if (GNUNET_YES !=
       TEAH_handle_is_ready (exchange))
@@ -301,69 +305,86 @@ TALER_EXCHANGE_track_transaction (struct 
TALER_EXCHANGE_Handle *exchange,
                  GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
                                            &dtp.purpose,
                                            &merchant_sig.eddsa_sig));
-  deposit_wtid_obj = json_pack ("{s:o, s:o," /* h_wire, h_contract_terms */
-                                " s:o," /* coin_pub */
-                                " s:o, s:o}", /* merchant_pub, merchant_sig */
-                                "h_wire", GNUNET_JSON_from_data_auto (h_wire),
-                                "h_contract_terms", GNUNET_JSON_from_data_auto 
(
-                                  h_contract_terms),
-                                "coin_pub", GNUNET_JSON_from_data_auto (
-                                  coin_pub),
-                                "merchant_pub", GNUNET_JSON_from_data_auto (
-                                  &dtp.merchant),
-                                "merchant_sig", GNUNET_JSON_from_data_auto (
-                                  &merchant_sig));
-  if (NULL == deposit_wtid_obj)
   {
-    GNUNET_break (0);
-    return NULL;
+    char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2];
+    char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
+    char chash_str[sizeof (struct GNUNET_HashCode) * 2];
+    char whash_str[sizeof (struct GNUNET_HashCode) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (h_wire,
+                                         sizeof (struct
+                                                 GNUNET_HashCode),
+                                         whash_str,
+                                         sizeof (whash_str));
+    *end = '\0';
+    end = GNUNET_STRINGS_data_to_string (&dtp.merchant,
+                                         sizeof (struct
+                                                 TALER_MerchantPublicKeyP),
+                                         mpub_str,
+                                         sizeof (mpub_str));
+    *end = '\0';
+    end = GNUNET_STRINGS_data_to_string (h_contract_terms,
+                                         sizeof (struct
+                                                 GNUNET_HashCode),
+                                         chash_str,
+                                         sizeof (chash_str));
+    *end = '\0';
+    end = GNUNET_STRINGS_data_to_string (coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         cpub_str,
+                                         sizeof (cpub_str));
+    *end = '\0';
+    end = GNUNET_STRINGS_data_to_string (&merchant_sig,
+                                         sizeof (struct
+                                                 TALER_MerchantSignatureP),
+                                         msig_str,
+                                         sizeof (msig_str));
+    *end = '\0';
+
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/deposits/%s/%s/%s/%s?merchant_sig=%s",
+                     whash_str,
+                     mpub_str,
+                     chash_str,
+                     cpub_str,
+                     msig_str);
   }
 
-  dwh = GNUNET_new (struct TALER_EXCHANGE_TrackTransactionHandle);
+  dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle);
   dwh->exchange = exchange;
   dwh->cb = cb;
   dwh->cb_cls = cb_cls;
-  dwh->url = TEAH_path_to_url (exchange, "/track/transaction");
+  dwh->url = TEAH_path_to_url (exchange,
+                               arg_str);
   dwh->depconf.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
   dwh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE);
   dwh->depconf.h_wire = *h_wire;
   dwh->depconf.h_contract_terms = *h_contract_terms;
   dwh->depconf.coin_pub = *coin_pub;
 
-  eh = TEL_curl_easy_get (dwh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&dwh->ctx,
-                            eh,
-                            deposit_wtid_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (deposit_wtid_obj);
-    GNUNET_free (dwh->url);
-    GNUNET_free (dwh);
-    return NULL;
-  }
-  json_decref (deposit_wtid_obj);
+  eh = TALER_EXCHANGE_curl_easy_get_ (dwh->url);
   ctx = TEAH_handle_to_context (exchange);
-  dwh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   dwh->ctx.headers,
-                                   &handle_deposit_wtid_finished,
-                                   dwh);
+  dwh->job = GNUNET_CURL_job_add (ctx,
+                                  eh,
+                                  GNUNET_NO,
+                                  &handle_deposit_wtid_finished,
+                                  dwh);
   return dwh;
 }
 
 
 /**
- * Cancel deposit wtid request.  This function cannot be used on a request
+ * Cancel /deposits/$WTID request.  This function cannot be used on a request
  * handle if a response is already served for it.
  *
  * @param dwh the wire deposits request handle
  */
 void
-TALER_EXCHANGE_track_transaction_cancel (struct
-                                         TALER_EXCHANGE_TrackTransactionHandle 
*
-                                         dwh)
+TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle 
*dwh)
 {
   if (NULL != dwh->job)
   {
@@ -376,4 +397,4 @@ TALER_EXCHANGE_track_transaction_cancel (struct
 }
 
 
-/* end of exchange_api_track_transaction.c */
+/* end of exchange_api_deposits_get.c */
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index 783cddea..5d9551c5 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -1953,7 +1953,7 @@ request_keys (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Requesting keys with URL `%s'.\n",
               kr->url);
-  eh = TEL_curl_easy_get (kr->url);
+  eh = TALER_EXCHANGE_curl_easy_get_ (kr->url);
   GNUNET_assert (CURLE_OK ==
                  curl_easy_setopt (eh,
                                    CURLOPT_VERBOSE,
@@ -2037,30 +2037,6 @@ TALER_EXCHANGE_disconnect (struct TALER_EXCHANGE_Handle 
*exchange)
 }
 
 
-/**
- * Lookup the given @a pub in @a keys.
- *
- * @param keys the exchange's key set
- * @param pub claimed current online signing key for the exchange
- * @return NULL if @a pub was not found
- */
-const struct TALER_EXCHANGE_SigningPublicKey *
-TALER_EXCHANGE_get_signing_key_details (const struct TALER_EXCHANGE_Keys *keys,
-                                        const struct
-                                        TALER_ExchangePublicKeyP *pub)
-{
-  for (unsigned int i = 0; i<keys->num_sign_keys; i++)
-  {
-    struct TALER_EXCHANGE_SigningPublicKey *spk = &keys->sign_keys[i];
-
-    if (0 == GNUNET_memcmp (pub,
-                            &spk->key))
-      return spk;
-  }
-  return NULL;
-}
-
-
 /**
  * Test if the given @a pub is a the current signing key from the exchange
  * according to @a keys.
diff --git a/src/lib/exchange_api_refresh_link.c b/src/lib/exchange_api_link.c
similarity index 71%
rename from src/lib/exchange_api_refresh_link.c
rename to src/lib/exchange_api_link.c
index 6a747d1b..e659a41c 100644
--- a/src/lib/exchange_api_refresh_link.c
+++ b/src/lib/exchange_api_link.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015, 2016, 2019 Taler Systems SA
+  Copyright (C) 2015-2020 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
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/exchange_api_refresh_link.c
- * @brief Implementation of the /refresh/link request of the exchange's HTTP 
API
+ * @file lib/exchange_api_link.c
+ * @brief Implementation of the /coins/$COIN_PUB/link request
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -31,9 +31,9 @@
 
 
 /**
- * @brief A /refresh/link Handle
+ * @brief A /coins/$COIN_PUB/link Handle
  */
-struct TALER_EXCHANGE_RefreshLinkHandle
+struct TALER_EXCHANGE_LinkHandle
 {
 
   /**
@@ -54,7 +54,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
   /**
    * Function to call with the result.
    */
-  TALER_EXCHANGE_RefreshLinkCallback link_cb;
+  TALER_EXCHANGE_LinkCallback link_cb;
 
   /**
    * Closure for @e cb.
@@ -73,7 +73,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
  * Parse the provided linkage data from the "200 OK" response
  * for one of the coins.
  *
- * @param rlh refresh link handle
+ * @param lh link handle
  * @param json json reply with the data for one coin
  * @param coin_num number of the coin to decode
  * @param trans_pub our transfer public key
@@ -83,13 +83,13 @@ struct TALER_EXCHANGE_RefreshLinkHandle
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
-parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
-                         const json_t *json,
-                         unsigned int coin_num,
-                         const struct TALER_TransferPublicKeyP *trans_pub,
-                         struct TALER_CoinSpendPrivateKeyP *coin_priv,
-                         struct TALER_DenominationSignature *sig,
-                         struct TALER_DenominationPublicKey *pub)
+parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
+                 const json_t *json,
+                 unsigned int coin_num,
+                 const struct TALER_TransferPublicKeyP *trans_pub,
+                 struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                 struct TALER_DenominationSignature *sig,
+                 struct TALER_DenominationPublicKey *pub)
 {
   struct GNUNET_CRYPTO_RsaSignature *bsig;
   struct GNUNET_CRYPTO_RsaPublicKey *rpub;
@@ -114,7 +114,7 @@ parse_refresh_link_coin (const struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
   }
 
   TALER_link_recover_transfer_secret (trans_pub,
-                                      &rlh->coin_priv,
+                                      &lh->coin_priv,
                                       &secret);
   TALER_planchet_setup_refresh (&secret,
                                 coin_num,
@@ -133,7 +133,7 @@ parse_refresh_link_coin (const struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
 
     ldp.purpose.size = htonl (sizeof (ldp));
     ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
-    GNUNET_CRYPTO_eddsa_key_get_public (&rlh->coin_priv.eddsa_priv,
+    GNUNET_CRYPTO_eddsa_key_get_public (&lh->coin_priv.eddsa_priv,
                                         &ldp.old_coin_pub.eddsa_pub);
     ldp.transfer_pub = *trans_pub;
     pub->rsa_public_key = rpub;
@@ -175,13 +175,13 @@ parse_refresh_link_coin (const struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
  * Parse the provided linkage data from the "200 OK" response
  * for one of the coins.
  *
- * @param[in,out] rlh refresh link handle (callback may be zero'ed out)
+ * @param[in,out] lh link handle (callback may be zero'ed out)
  * @param json json reply with the data for one coin
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
-parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
-                       const json_t *json)
+parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
+               const json_t *json)
 {
   unsigned int session;
   unsigned int num_coins;
@@ -277,14 +277,14 @@ parse_refresh_link_ok (struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
       {
         GNUNET_assert (i + off_coin < num_coins);
         if (GNUNET_OK !=
-            parse_refresh_link_coin (rlh,
-                                     json_array_get (jsona,
-                                                     i),
-                                     i,
-                                     &trans_pub,
-                                     &coin_privs[i + off_coin],
-                                     &sigs[i + off_coin],
-                                     &pubs[i + off_coin]))
+            parse_link_coin (lh,
+                             json_array_get (jsona,
+                                             i),
+                             i,
+                             &trans_pub,
+                             &coin_privs[i + off_coin],
+                             &sigs[i + off_coin],
+                             &pubs[i + off_coin]))
         {
           GNUNET_break_op (0);
           break;
@@ -304,15 +304,15 @@ parse_refresh_link_ok (struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
 
     if (off_coin == num_coins)
     {
-      rlh->link_cb (rlh->link_cb_cls,
-                    MHD_HTTP_OK,
-                    TALER_EC_NONE,
-                    num_coins,
-                    coin_privs,
-                    sigs,
-                    pubs,
-                    json);
-      rlh->link_cb = NULL;
+      lh->link_cb (lh->link_cb_cls,
+                   MHD_HTTP_OK,
+                   TALER_EC_NONE,
+                   num_coins,
+                   coin_privs,
+                   sigs,
+                   pubs,
+                   json);
+      lh->link_cb = NULL;
       ret = GNUNET_OK;
     }
     else
@@ -337,29 +337,29 @@ parse_refresh_link_ok (struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
 
 /**
  * Function called when we're done processing the
- * HTTP /refresh/link request.
+ * HTTP /coins/$COIN_PUB/link request.
  *
- * @param cls the `struct TALER_EXCHANGE_RefreshLinkHandle`
+ * @param cls the `struct TALER_EXCHANGE_LinkHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response parsed JSON result, NULL on error
  */
 static void
-handle_refresh_link_finished (void *cls,
-                              long response_code,
-                              const void *response)
+handle_link_finished (void *cls,
+                      long response_code,
+                      const void *response)
 {
-  struct TALER_EXCHANGE_RefreshLinkHandle *rlh = cls;
+  struct TALER_EXCHANGE_LinkHandle *lh = cls;
   const json_t *j = response;
 
-  rlh->job = NULL;
+  lh->job = NULL;
   switch (response_code)
   {
   case 0:
     break;
   case MHD_HTTP_OK:
     if (GNUNET_OK !=
-        parse_refresh_link_ok (rlh,
-                               j))
+        parse_link_ok (lh,
+                       j))
     {
       GNUNET_break_op (0);
       response_code = 0;
@@ -386,25 +386,24 @@ handle_refresh_link_finished (void *cls,
     response_code = 0;
     break;
   }
-  if (NULL != rlh->link_cb)
-    rlh->link_cb (rlh->link_cb_cls,
-                  response_code,
-                  TALER_JSON_get_error_code (j),
-                  0,
-                  NULL,
-                  NULL,
-                  NULL,
-                  j);
-  TALER_EXCHANGE_refresh_link_cancel (rlh);
+  if (NULL != lh->link_cb)
+    lh->link_cb (lh->link_cb_cls,
+                 response_code,
+                 TALER_JSON_get_error_code (j),
+                 0,
+                 NULL,
+                 NULL,
+                 NULL,
+                 j);
+  TALER_EXCHANGE_link_cancel (lh);
 }
 
 
 /**
  * Submit a link request to the exchange and get the exchange's response.
  *
- * This API is typically not used by anyone, it is more a threat
- * against those trying to receive a funds transfer by abusing the
- * /refresh protocol.
+ * This API is typically not used by anyone, it is more a threat against those
+ * trying to receive a funds transfer by abusing the refresh protocol.
  *
  * @param exchange the exchange handle; the exchange must be ready to operate
  * @param coin_priv private key to request link data for
@@ -413,18 +412,17 @@ handle_refresh_link_finished (void *cls,
  * @param link_cb_cls closure for @a link_cb
  * @return a handle for this request
  */
-struct TALER_EXCHANGE_RefreshLinkHandle *
-TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
-                             const struct TALER_CoinSpendPrivateKeyP 
*coin_priv,
-                             TALER_EXCHANGE_RefreshLinkCallback link_cb,
-                             void *link_cb_cls)
+struct TALER_EXCHANGE_LinkHandle *
+TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
+                     const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                     TALER_EXCHANGE_LinkCallback link_cb,
+                     void *link_cb_cls)
 {
-  struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
+  struct TALER_EXCHANGE_LinkHandle *lh;
   CURL *eh;
   struct GNUNET_CURL_Context *ctx;
   struct TALER_CoinSpendPublicKeyP coin_pub;
-  char *pub_str;
-  char *arg_str;
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
 
   if (GNUNET_YES !=
       TEAH_handle_is_ready (exchange))
@@ -435,52 +433,56 @@ TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle 
*exchange,
 
   GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
                                       &coin_pub.eddsa_pub);
-  pub_str = GNUNET_STRINGS_data_to_string_alloc (&coin_pub,
-                                                 sizeof (struct
-                                                         
TALER_CoinSpendPublicKeyP));
-  GNUNET_asprintf (&arg_str,
-                   "/refresh/link?coin_pub=%s",
-                   pub_str);
-  GNUNET_free (pub_str);
-
-  rlh = GNUNET_new (struct TALER_EXCHANGE_RefreshLinkHandle);
-  rlh->exchange = exchange;
-  rlh->link_cb = link_cb;
-  rlh->link_cb_cls = link_cb_cls;
-  rlh->coin_priv = *coin_priv;
-  rlh->url = TEAH_path_to_url (exchange, arg_str);
-  GNUNET_free (arg_str);
-
-
-  eh = TEL_curl_easy_get (rlh->url);
+  {
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (&coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/coins/%s/link",
+                     pub_str);
+  }
+  lh = GNUNET_new (struct TALER_EXCHANGE_LinkHandle);
+  lh->exchange = exchange;
+  lh->link_cb = link_cb;
+  lh->link_cb_cls = link_cb_cls;
+  lh->coin_priv = *coin_priv;
+  lh->url = TEAH_path_to_url (exchange,
+                              arg_str);
+  eh = TALER_EXCHANGE_curl_easy_get_ (lh->url);
   ctx = TEAH_handle_to_context (exchange);
-  rlh->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_refresh_link_finished,
-                                  rlh);
-  return rlh;
+  lh->job = GNUNET_CURL_job_add (ctx,
+                                 eh,
+                                 GNUNET_YES,
+                                 &handle_link_finished,
+                                 lh);
+  return lh;
 }
 
 
 /**
- * Cancel a refresh link request.  This function cannot be used
+ * Cancel a link request.  This function cannot be used
  * on a request handle if the callback was already invoked.
  *
- * @param rlh the refresh link handle
+ * @param lh the link handle
  */
 void
-TALER_EXCHANGE_refresh_link_cancel (struct
-                                    TALER_EXCHANGE_RefreshLinkHandle *rlh)
+TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh)
 {
-  if (NULL != rlh->job)
+  if (NULL != lh->job)
   {
-    GNUNET_CURL_job_cancel (rlh->job);
-    rlh->job = NULL;
+    GNUNET_CURL_job_cancel (lh->job);
+    lh->job = NULL;
   }
-  GNUNET_free (rlh->url);
-  GNUNET_free (rlh);
+  GNUNET_free (lh->url);
+  GNUNET_free (lh);
 }
 
 
-/* end of exchange_api_refresh_link.c */
+/* end of exchange_api_link.c */
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
new file mode 100644
index 00000000..5a3abba8
--- /dev/null
+++ b/src/lib/exchange_api_melt.c
@@ -0,0 +1,505 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_melt.c
+ * @brief Implementation of the /coins/$COIN_PUB/melt request
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+#include "exchange_api_refresh_common.h"
+
+
+/**
+ * @brief A /coins/$COIN_PUB/melt Handle
+ */
+struct TALER_EXCHANGE_MeltHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext ctx;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with refresh melt failure results.
+   */
+  TALER_EXCHANGE_MeltCallback melt_cb;
+
+  /**
+   * Closure for @e result_cb and @e melt_failure_cb.
+   */
+  void *melt_cb_cls;
+
+  /**
+   * Actual information about the melt operation.
+   */
+  struct MeltData *md;
+
+  /**
+   * @brief Public information about the coin's denomination key
+   */
+  struct TALER_EXCHANGE_DenomPublicKey dki;
+};
+
+
+/**
+ * Verify that the signature on the "200 OK" response
+ * from the exchange is valid.
+ *
+ * @param mh melt handle
+ * @param json json reply with the signature
+ * @param[out] exchange_pub public key of the exchange used for the signature
+ * @param[out] noreveal_index set to the noreveal index selected by the 
exchange
+ * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
+                          const json_t *json,
+                          struct TALER_ExchangePublicKeyP *exchange_pub,
+                          uint32_t *noreveal_index)
+{
+  struct TALER_ExchangeSignatureP exchange_sig;
+  const struct TALER_EXCHANGE_Keys *key_state;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
+    GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
+    GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index),
+    GNUNET_JSON_spec_end ()
+  };
+  struct TALER_RefreshMeltConfirmationPS confirm;
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* check that exchange signing key is permitted */
+  key_state = TALER_EXCHANGE_get_keys (mh->exchange);
+  if (GNUNET_OK !=
+      TALER_EXCHANGE_test_signing_key (key_state,
+                                       exchange_pub))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* check that noreveal index is in permitted range */
+  if (TALER_CNC_KAPPA <= *noreveal_index)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* verify signature by exchange */
+  confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
+  confirm.purpose.size = htonl (sizeof (struct
+                                        TALER_RefreshMeltConfirmationPS));
+  confirm.rc = mh->md->rc;
+  confirm.noreveal_index = htonl (*noreveal_index);
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
+                                  &confirm.purpose,
+                                  &exchange_sig.eddsa_signature,
+                                  &exchange_pub->eddsa_pub))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Verify that the signatures on the "409 CONFLICT" response from the
+ * exchange demonstrating customer double-spending are valid.
+ *
+ * @param mh melt handle
+ * @param json json reply with the signature(s) and transaction history
+ * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
+                                const json_t *json)
+{
+  json_t *history;
+  struct TALER_Amount original_value;
+  struct TALER_Amount melt_value_with_fee;
+  struct TALER_Amount total;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("history", &history),
+    GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
+    TALER_JSON_spec_amount ("original_value", &original_value),
+    TALER_JSON_spec_amount ("requested_value", &melt_value_with_fee),
+    GNUNET_JSON_spec_end ()
+  };
+  const struct MeltedCoin *mc;
+
+  /* parse JSON reply */
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* Find out which coin was deemed problematic by the exchange */
+  mc = &mh->md->melted_coin;
+
+  /* check basic coin properties */
+  if (0 != TALER_amount_cmp (&original_value,
+                             &mc->original_value))
+  {
+    /* We disagree on the value of the coin */
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+  if (0 != TALER_amount_cmp (&melt_value_with_fee,
+                             &mc->melt_amount_with_fee))
+  {
+    /* We disagree on the value of the coin */
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+
+  /* verify coin history */
+  history = json_object_get (json,
+                             "history");
+  if (GNUNET_OK !=
+      TALER_EXCHANGE_verify_coin_history (&mh->dki,
+                                          original_value.currency,
+                                          &coin_pub,
+                                          history,
+                                          &total))
+  {
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+  json_decref (history);
+
+  /* check if melt operation was really too expensive given history */
+  if (GNUNET_OK !=
+      TALER_amount_add (&total,
+                        &total,
+                        &melt_value_with_fee))
+  {
+    /* clearly not OK if our transaction would have caused
+       the overflow... */
+    return GNUNET_OK;
+  }
+
+  if (0 >= TALER_amount_cmp (&total,
+                             &original_value))
+  {
+    /* transaction should have still fit */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* everything OK, valid proof of double-spending was provided */
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /coins/$COIN_PUB/melt request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_MeltHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_melt_finished (void *cls,
+                      long response_code,
+                      const void *response)
+{
+  struct TALER_EXCHANGE_MeltHandle *mh = cls;
+  uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+  const json_t *j = response;
+
+  mh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        verify_melt_signature_ok (mh,
+                                  j,
+                                  &exchange_pub,
+                                  &noreveal_index))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    if (NULL != mh->melt_cb)
+    {
+      mh->melt_cb (mh->melt_cb_cls,
+                   response_code,
+                   TALER_JSON_get_error_code (j),
+                   noreveal_index,
+                   (0 == response_code) ? NULL : &exchange_pub,
+                   j);
+      mh->melt_cb = NULL;
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_CONFLICT:
+    /* Double spending; check signatures on transaction history */
+    if (GNUNET_OK !=
+        verify_melt_signature_conflict (mh,
+                                        j))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* Nothing really to verify, exchange says one of the signatures is
+       invalid; assuming we checked them, this should never happen, we
+       should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != mh->melt_cb)
+    mh->melt_cb (mh->melt_cb_cls,
+                 response_code,
+                 TALER_JSON_get_error_code (j),
+                 UINT32_MAX,
+                 NULL,
+                 j);
+  TALER_EXCHANGE_melt_cancel (mh);
+}
+
+
+/**
+ * Submit a melt request to the exchange and get the exchange's
+ * response.
+ *
+ * This API is typically used by a wallet.  Note that to ensure that
+ * no money is lost in case of hardware failures, the provided
+ * argument should have been constructed using
+ * #TALER_EXCHANGE_refresh_prepare and committed to persistent storage
+ * prior to calling this function.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param refresh_data_length size of the @a refresh_data (returned
+ *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
+ * @param refresh_data the refresh data as returned from
+          #TALER_EXCHANGE_refresh_prepare())
+ * @param melt_cb the callback to call with the result
+ * @param melt_cb_cls closure for @a melt_cb
+ * @return a handle for this request; NULL if the argument was invalid.
+ *         In this case, neither callback will be called.
+ */
+struct TALER_EXCHANGE_MeltHandle *
+TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
+                     size_t refresh_data_length,
+                     const char *refresh_data,
+                     TALER_EXCHANGE_MeltCallback melt_cb,
+                     void *melt_cb_cls)
+{
+  const struct TALER_EXCHANGE_Keys *key_state;
+  const struct TALER_EXCHANGE_DenomPublicKey *dki;
+  json_t *melt_obj;
+  struct TALER_EXCHANGE_MeltHandle *mh;
+  CURL *eh;
+  struct GNUNET_CURL_Context *ctx;
+  struct MeltData *md;
+  struct TALER_CoinSpendSignatureP confirm_sig;
+  struct TALER_RefreshMeltCoinAffirmationPS melt;
+  struct GNUNET_HashCode h_denom_pub;
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
+
+  GNUNET_assert (GNUNET_YES ==
+                 TEAH_handle_is_ready (exchange));
+  md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
+                                              refresh_data_length);
+  if (NULL == md)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+  melt.purpose.size = htonl (sizeof (struct
+                                     TALER_RefreshMeltCoinAffirmationPS));
+  melt.rc = md->rc;
+  TALER_amount_hton (&melt.amount_with_fee,
+                     &md->melted_coin.melt_amount_with_fee);
+  TALER_amount_hton (&melt.melt_fee,
+                     &md->melted_coin.fee_melt);
+  GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
+                                      &melt.coin_pub.eddsa_pub);
+  GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv,
+                            &melt.purpose,
+                            &confirm_sig.eddsa_signature);
+  GNUNET_CRYPTO_rsa_public_key_hash (md->melted_coin.pub_key.rsa_public_key,
+                                     &h_denom_pub);
+  melt_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
+                        "coin_pub",
+                        GNUNET_JSON_from_data_auto (&melt.coin_pub),
+                        "denom_pub_hash",
+                        GNUNET_JSON_from_data_auto (&h_denom_pub),
+                        "denom_sig",
+                        GNUNET_JSON_from_rsa_signature (
+                          md->melted_coin.sig.rsa_signature),
+                        "confirm_sig",
+                        GNUNET_JSON_from_data_auto (&confirm_sig),
+                        "value_with_fee",
+                        TALER_JSON_from_amount (
+                          &md->melted_coin.melt_amount_with_fee),
+                        "rc",
+                        GNUNET_JSON_from_data_auto (&melt.rc));
+  if (NULL == melt_obj)
+  {
+    GNUNET_break (0);
+    TALER_EXCHANGE_free_melt_data_ (md);
+    return NULL;
+  }
+  {
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (&melt.coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/coins/%s/melt",
+                     pub_str);
+  }
+
+  key_state = TALER_EXCHANGE_get_keys (exchange);
+  dki = TALER_EXCHANGE_get_denomination_key (key_state,
+                                             &md->melted_coin.pub_key);
+
+  /* and now we can at last begin the actual request handling */
+  mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
+  mh->exchange = exchange;
+  mh->dki = *dki;
+  mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
+                                         not copy the pointer */
+  mh->melt_cb = melt_cb;
+  mh->melt_cb_cls = melt_cb_cls;
+  mh->md = md;
+  mh->url = TEAH_path_to_url (exchange,
+                              arg_str);
+  eh = TALER_EXCHANGE_curl_easy_get_ (mh->url);
+  if (GNUNET_OK !=
+      TALER_curl_easy_post (&mh->ctx,
+                            eh,
+                            melt_obj))
+  {
+    GNUNET_break (0);
+    curl_easy_cleanup (eh);
+    json_decref (melt_obj);
+    GNUNET_free (mh->url);
+    GNUNET_free (mh);
+    return NULL;
+  }
+  json_decref (melt_obj);
+  ctx = TEAH_handle_to_context (exchange);
+  mh->job = GNUNET_CURL_job_add2 (ctx,
+                                  eh,
+                                  mh->ctx.headers,
+                                  &handle_melt_finished,
+                                  mh);
+  return mh;
+}
+
+
+/**
+ * Cancel a melt request.  This function cannot be used
+ * on a request handle if either callback was already invoked.
+ *
+ * @param mh the refresh melt handle
+ */
+void
+TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh)
+{
+  if (NULL != mh->job)
+  {
+    GNUNET_CURL_job_cancel (mh->job);
+    mh->job = NULL;
+  }
+  TALER_EXCHANGE_free_melt_data_ (mh->md); /* does not free 'md' itself */
+  GNUNET_free (mh->md);
+  GNUNET_free (mh->url);
+  TALER_curl_easy_post_finished (&mh->ctx);
+  GNUNET_free (mh);
+}
+
+
+/* end of exchange_api_melt.c */
diff --git a/src/lib/exchange_api_recoup.c b/src/lib/exchange_api_recoup.c
index 1a332ad3..013d480b 100644
--- a/src/lib/exchange_api_recoup.c
+++ b/src/lib/exchange_api_recoup.c
@@ -328,6 +328,7 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
   struct GNUNET_HashCode h_denom_pub;
   json_t *recoup_obj;
   CURL *eh;
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
 
   GNUNET_assert (GNUNET_YES ==
                  TEAH_handle_is_ready (exchange));
@@ -345,14 +346,12 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
                                            &coin_sig.eddsa_signature));
 
   recoup_obj = json_pack ("{s:o, s:o," /* denom pub/sig */
-                          " s:o, s:o,"  /* coin pub/sig */
+                          " s:o,"  /* sig */
                           " s:o, s:o}",  /* coin_bks */
                           "denom_pub_hash", GNUNET_JSON_from_data_auto (
                             &h_denom_pub),
                           "denom_sig", GNUNET_JSON_from_rsa_signature (
                             denom_sig->rsa_signature),
-                          "coin_pub", GNUNET_JSON_from_data_auto (
-                            &pr.coin_pub),
                           "coin_sig", GNUNET_JSON_from_data_auto (&coin_sig),
                           "coin_blind_key_secret", GNUNET_JSON_from_data_auto (
                             &ps->blinding_key),
@@ -364,6 +363,22 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
     return NULL;
   }
 
+  {
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (&pr.coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/coins/%s/recoup",
+                     pub_str);
+  }
+
   ph = GNUNET_new (struct TALER_EXCHANGE_RecoupHandle);
   ph->coin_pub = pr.coin_pub;
   ph->exchange = exchange;
@@ -371,9 +386,10 @@ TALER_EXCHANGE_recoup (struct TALER_EXCHANGE_Handle 
*exchange,
   ph->pk.key.rsa_public_key = NULL; /* zero out, as lifetime cannot be 
warranted */
   ph->cb = recoup_cb;
   ph->cb_cls = recoup_cb_cls;
-  ph->url = TEAH_path_to_url (exchange, "/recoup");
+  ph->url = TEAH_path_to_url (exchange,
+                              arg_str);
   ph->was_refreshed = was_refreshed;
-  eh = TEL_curl_easy_get (ph->url);
+  eh = TALER_EXCHANGE_curl_easy_get_ (ph->url);
   if (GNUNET_OK !=
       TALER_curl_easy_post (&ph->ctx,
                             eh,
diff --git a/src/lib/exchange_api_refresh.c b/src/lib/exchange_api_refresh.c
deleted file mode 100644
index e097ee3f..00000000
--- a/src/lib/exchange_api_refresh.c
+++ /dev/null
@@ -1,1740 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2015, 2016, 2017, 2019 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 lib/exchange_api_refresh.c
- * @brief Implementation of the /refresh/melt+reveal requests of the 
exchange's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_json_lib.h"
-#include "taler_exchange_service.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/* ********************* /refresh/ common ***************************** */
-
-/* structures for committing refresh data to disk before doing the
-   network interaction(s) */
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-/**
- * Header of serialized information about a coin we are melting.
- */
-struct MeltedCoinP
-{
-  /**
-   * Private key of the coin.
-   */
-  struct TALER_CoinSpendPrivateKeyP coin_priv;
-
-  /**
-   * Amount this coin contributes to the melt, including fee.
-   */
-  struct TALER_AmountNBO melt_amount_with_fee;
-
-  /**
-   * The applicable fee for withdrawing a coin of this denomination
-   */
-  struct TALER_AmountNBO fee_melt;
-
-  /**
-   * The original value of the coin.
-   */
-  struct TALER_AmountNBO original_value;
-
-  /**
-   * Transfer private keys for each cut-and-choose dimension.
-   */
-  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
-
-  /**
-   * Timestamp indicating when coins of this denomination become invalid.
-   */
-  struct GNUNET_TIME_AbsoluteNBO expire_deposit;
-
-  /**
-   * Size of the encoded public key that follows.
-   */
-  uint16_t pbuf_size;
-
-  /**
-   * Size of the encoded signature that follows.
-   */
-  uint16_t sbuf_size;
-
-  /* Followed by serializations of:
-     1) struct TALER_DenominationPublicKey pub_key;
-     2) struct TALER_DenominationSignature sig;
-  */
-};
-
-
-/**
- * Header of serialized data about a melt operation, suitable for
- * persisting it on disk.
- */
-struct MeltDataP
-{
-
-  /**
-   * Hash over the melting session.
-   */
-  struct TALER_RefreshCommitmentP rc;
-
-  /**
-   * Number of coins we are melting, in NBO
-   */
-  uint16_t num_melted_coins GNUNET_PACKED;
-
-  /**
-   * Number of coins we are creating, in NBO
-   */
-  uint16_t num_fresh_coins GNUNET_PACKED;
-
-  /* Followed by serializations of:
-     1) struct MeltedCoinP melted_coins[num_melted_coins];
-     2) struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins];
-     3) TALER_CNC_KAPPA times:
-        3a) struct TALER_PlanchetSecretsP fresh_coins[num_fresh_coins];
-  */
-};
-
-
-GNUNET_NETWORK_STRUCT_END
-
-
-/**
- * Information about a coin we are melting.
- */
-struct MeltedCoin
-{
-  /**
-   * Private key of the coin.
-   */
-  struct TALER_CoinSpendPrivateKeyP coin_priv;
-
-  /**
-   * Amount this coin contributes to the melt, including fee.
-   */
-  struct TALER_Amount melt_amount_with_fee;
-
-  /**
-   * The applicable fee for melting a coin of this denomination
-   */
-  struct TALER_Amount fee_melt;
-
-  /**
-   * The original value of the coin.
-   */
-  struct TALER_Amount original_value;
-
-  /**
-   * Transfer private keys for each cut-and-choose dimension.
-   */
-  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
-
-  /**
-   * Timestamp indicating when coins of this denomination become invalid.
-   */
-  struct GNUNET_TIME_Absolute expire_deposit;
-
-  /**
-   * Denomination key of the original coin.
-   */
-  struct TALER_DenominationPublicKey pub_key;
-
-  /**
-   * Exchange's signature over the coin.
-   */
-  struct TALER_DenominationSignature sig;
-
-};
-
-
-/**
- * Melt data in non-serialized format for convenient processing.
- */
-struct MeltData
-{
-
-  /**
-   * Hash over the committed data during refresh operation.
-   */
-  struct TALER_RefreshCommitmentP rc;
-
-  /**
-   * Number of coins we are creating
-   */
-  uint16_t num_fresh_coins;
-
-  /**
-   * Information about the melted coin.
-   */
-  struct MeltedCoin melted_coin;
-
-  /**
-   * Array of @e num_fresh_coins denomination keys for the coins to be
-   * freshly exchangeed.
-   */
-  struct TALER_DenominationPublicKey *fresh_pks;
-
-  /**
-   * Arrays of @e num_fresh_coins with information about the fresh
-   * coins to be created, for each cut-and-choose dimension.
-   */
-  struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
-};
-
-
-/**
- * Free all information associated with a melted coin session.
- *
- * @param mc melted coin to release, the pointer itself is NOT
- *           freed (as it is typically not allocated by itself)
- */
-static void
-free_melted_coin (struct MeltedCoin *mc)
-{
-  if (NULL != mc->pub_key.rsa_public_key)
-    GNUNET_CRYPTO_rsa_public_key_free (mc->pub_key.rsa_public_key);
-  if (NULL != mc->sig.rsa_signature)
-    GNUNET_CRYPTO_rsa_signature_free (mc->sig.rsa_signature);
-}
-
-
-/**
- * Free all information associated with a melting session.  Note
- * that we allow the melting session to be only partially initialized,
- * as we use this function also when freeing melt data that was not
- * fully initialized (i.e. due to failures in #deserialize_melt_data()).
- *
- * @param md melting data to release, the pointer itself is NOT
- *           freed (as it is typically not allocated by itself)
- */
-static void
-free_melt_data (struct MeltData *md)
-{
-  free_melted_coin (&md->melted_coin);
-  if (NULL != md->fresh_pks)
-  {
-    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
-      if (NULL != md->fresh_pks[i].rsa_public_key)
-        GNUNET_CRYPTO_rsa_public_key_free (md->fresh_pks[i].rsa_public_key);
-    GNUNET_free (md->fresh_pks);
-  }
-
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-    GNUNET_free_non_null (md->fresh_coins[i]);
-  /* Finally, clean up a bit...
-     (NOTE: compilers might optimize this away, so this is
-     not providing any strong assurances that the key material
-     is purged.) */
-  memset (md,
-          0,
-          sizeof (struct MeltData));
-}
-
-
-/**
- * Serialize information about a coin we are melting.
- *
- * @param mc information to serialize
- * @param buf buffer to write data in, NULL to just compute
- *            required size
- * @param off offeset at @a buf to use
- * @return number of bytes written to @a buf at @a off, or if
- *        @a buf is NULL, number of bytes required; 0 on error
- */
-static size_t
-serialize_melted_coin (const struct MeltedCoin *mc,
-                       char *buf,
-                       size_t off)
-{
-  struct MeltedCoinP mcp;
-  unsigned int i;
-  char *pbuf;
-  size_t pbuf_size;
-  char *sbuf;
-  size_t sbuf_size;
-
-  sbuf_size = GNUNET_CRYPTO_rsa_signature_encode (mc->sig.rsa_signature,
-                                                  &sbuf);
-  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (mc->pub_key.rsa_public_key,
-                                                   &pbuf);
-  if (NULL == buf)
-  {
-    GNUNET_free (sbuf);
-    GNUNET_free (pbuf);
-    return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
-  }
-  if ( (sbuf_size > UINT16_MAX) ||
-       (pbuf_size > UINT16_MAX) )
-  {
-    GNUNET_break (0);
-    return 0;
-  }
-  mcp.coin_priv = mc->coin_priv;
-  TALER_amount_hton (&mcp.melt_amount_with_fee,
-                     &mc->melt_amount_with_fee);
-  TALER_amount_hton (&mcp.fee_melt,
-                     &mc->fee_melt);
-  TALER_amount_hton (&mcp.original_value,
-                     &mc->original_value);
-  for (i = 0; i<TALER_CNC_KAPPA; i++)
-    mcp.transfer_priv[i] = mc->transfer_priv[i];
-  mcp.expire_deposit = GNUNET_TIME_absolute_hton (mc->expire_deposit);
-  mcp.pbuf_size = htons ((uint16_t) pbuf_size);
-  mcp.sbuf_size = htons ((uint16_t) sbuf_size);
-  memcpy (&buf[off],
-          &mcp,
-          sizeof (struct MeltedCoinP));
-  memcpy (&buf[off + sizeof (struct MeltedCoinP)],
-          pbuf,
-          pbuf_size);
-  memcpy (&buf[off + sizeof (struct MeltedCoinP) + pbuf_size],
-          sbuf,
-          sbuf_size);
-  GNUNET_free (sbuf);
-  GNUNET_free (pbuf);
-  return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
-}
-
-
-/**
- * Deserialize information about a coin we are melting.
- *
- * @param[out] mc information to deserialize
- * @param buf buffer to read data from
- * @param size number of bytes available at @a buf to use
- * @param[out] ok set to #GNUNET_NO to report errors
- * @return number of bytes read from @a buf, 0 on error
- */
-static size_t
-deserialize_melted_coin (struct MeltedCoin *mc,
-                         const char *buf,
-                         size_t size,
-                         int *ok)
-{
-  struct MeltedCoinP mcp;
-  unsigned int i;
-  size_t pbuf_size;
-  size_t sbuf_size;
-  size_t off;
-
-  if (size < sizeof (struct MeltedCoinP))
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  memcpy (&mcp,
-          buf,
-          sizeof (struct MeltedCoinP));
-  pbuf_size = ntohs (mcp.pbuf_size);
-  sbuf_size = ntohs (mcp.sbuf_size);
-  if (size < sizeof (struct MeltedCoinP) + pbuf_size + sbuf_size)
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  off = sizeof (struct MeltedCoinP);
-  mc->pub_key.rsa_public_key
-    = GNUNET_CRYPTO_rsa_public_key_decode (&buf[off],
-                                           pbuf_size);
-  off += pbuf_size;
-  mc->sig.rsa_signature
-    = GNUNET_CRYPTO_rsa_signature_decode (&buf[off],
-                                          sbuf_size);
-  off += sbuf_size;
-  if ( (NULL == mc->pub_key.rsa_public_key) ||
-       (NULL == mc->sig.rsa_signature) )
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-
-  mc->coin_priv = mcp.coin_priv;
-  TALER_amount_ntoh (&mc->melt_amount_with_fee,
-                     &mcp.melt_amount_with_fee);
-  TALER_amount_ntoh (&mc->fee_melt,
-                     &mcp.fee_melt);
-  TALER_amount_ntoh (&mc->original_value,
-                     &mcp.original_value);
-  for (i = 0; i<TALER_CNC_KAPPA; i++)
-    mc->transfer_priv[i] = mcp.transfer_priv[i];
-  mc->expire_deposit = GNUNET_TIME_absolute_ntoh (mcp.expire_deposit);
-  return off;
-}
-
-
-/**
- * Serialize information about a denomination key.
- *
- * @param dk information to serialize
- * @param buf buffer to write data in, NULL to just compute
- *            required size
- * @param off offeset at @a buf to use
- * @return number of bytes written to @a buf at @a off, or if
- *        @a buf is NULL, number of bytes required
- */
-static size_t
-serialize_denomination_key (const struct TALER_DenominationPublicKey *dk,
-                            char *buf,
-                            size_t off)
-{
-  char *pbuf;
-  size_t pbuf_size;
-  uint32_t be;
-
-  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (dk->rsa_public_key,
-                                                   &pbuf);
-  if (NULL == buf)
-  {
-    GNUNET_free (pbuf);
-    return pbuf_size + sizeof (uint32_t);
-  }
-  be = htonl ((uint32_t) pbuf_size);
-  memcpy (&buf[off],
-          &be,
-          sizeof (uint32_t));
-  memcpy (&buf[off + sizeof (uint32_t)],
-          pbuf,
-          pbuf_size);
-  GNUNET_free (pbuf);
-  return pbuf_size + sizeof (uint32_t);
-}
-
-
-/**
- * Deserialize information about a denomination key.
- *
- * @param[out] dk information to deserialize
- * @param buf buffer to read data from
- * @param size number of bytes available at @a buf to use
- * @param[out] ok set to #GNUNET_NO to report errors
- * @return number of bytes read from @a buf, 0 on error
- */
-static size_t
-deserialize_denomination_key (struct TALER_DenominationPublicKey *dk,
-                              const char *buf,
-                              size_t size,
-                              int *ok)
-{
-  size_t pbuf_size;
-  uint32_t be;
-
-  if (size < sizeof (uint32_t))
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  memcpy (&be,
-          buf,
-          sizeof (uint32_t));
-  pbuf_size = ntohl (be);
-  if (size < sizeof (uint32_t) + pbuf_size)
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  dk->rsa_public_key
-    = GNUNET_CRYPTO_rsa_public_key_decode (&buf[sizeof (uint32_t)],
-                                           pbuf_size);
-  if (NULL == dk->rsa_public_key)
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  return sizeof (uint32_t) + pbuf_size;
-}
-
-
-/**
- * Serialize information about a fresh coin we are generating.
- *
- * @param fc information to serialize
- * @param buf buffer to write data in, NULL to just compute
- *            required size
- * @param off offeset at @a buf to use
- * @return number of bytes written to @a buf at @a off, or if
- *        @a buf is NULL, number of bytes required
- */
-static size_t
-serialize_fresh_coin (const struct TALER_PlanchetSecretsP *fc,
-                      char *buf,
-                      size_t off)
-{
-  if (NULL != buf)
-    memcpy (&buf[off],
-            fc,
-            sizeof (struct TALER_PlanchetSecretsP));
-  return sizeof (struct TALER_PlanchetSecretsP);
-}
-
-
-/**
- * Deserialize information about a fresh coin we are generating.
- *
- * @param[out] fc information to deserialize
- * @param buf buffer to read data from
- * @param size number of bytes available at @a buf to use
- * @param[out] ok set to #GNUNET_NO to report errors
- * @return number of bytes read from @a buf, 0 on error
- */
-static size_t
-deserialize_fresh_coin (struct TALER_PlanchetSecretsP *fc,
-                        const char *buf,
-                        size_t size,
-                        int *ok)
-{
-  if (size < sizeof (struct TALER_PlanchetSecretsP))
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  memcpy (fc,
-          buf,
-          sizeof (struct TALER_PlanchetSecretsP));
-  return sizeof (struct TALER_PlanchetSecretsP);
-}
-
-
-/**
- * Serialize melt data.
- *
- * @param md data to serialize
- * @param[out] res_size size of buffer returned
- * @return serialized melt data
- */
-static char *
-serialize_melt_data (const struct MeltData *md,
-                     size_t *res_size)
-{
-  size_t size;
-  size_t asize;
-  char *buf;
-
-  size = 0;
-  asize = (size_t) -1; /* make the compiler happy */
-  buf = NULL;
-  /* we do 2 iterations, #1 to determine total size, #2 to
-     actually construct the buffer */
-  do {
-    if (0 == size)
-    {
-      size = sizeof (struct MeltDataP);
-    }
-    else
-    {
-      struct MeltDataP *mdp;
-
-      buf = GNUNET_malloc (size);
-      asize = size; /* just for invariant check later */
-      size = sizeof (struct MeltDataP);
-      mdp = (struct MeltDataP *) buf;
-      mdp->rc = md->rc;
-      mdp->num_fresh_coins = htons (md->num_fresh_coins);
-    }
-    size += serialize_melted_coin (&md->melted_coin,
-                                   buf,
-                                   size);
-    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
-      size += serialize_denomination_key (&md->fresh_pks[i],
-                                          buf,
-                                          size);
-    for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-      for (unsigned int j = 0; j<md->num_fresh_coins; j++)
-        size += serialize_fresh_coin (&md->fresh_coins[i][j],
-                                      buf,
-                                      size);
-  } while (NULL == buf);
-  GNUNET_assert (size == asize);
-  *res_size = size;
-  return buf;
-}
-
-
-/**
- * Deserialize melt data.
- *
- * @param buf serialized data
- * @param buf_size size of @a buf
- * @return deserialized melt data, NULL on error
- */
-static struct MeltData *
-deserialize_melt_data (const char *buf,
-                       size_t buf_size)
-{
-  struct MeltData *md;
-  struct MeltDataP mdp;
-  size_t off;
-  int ok;
-
-  if (buf_size < sizeof (struct MeltDataP))
-    return NULL;
-  memcpy (&mdp,
-          buf,
-          sizeof (struct MeltDataP));
-  md = GNUNET_new (struct MeltData);
-  md->rc = mdp.rc;
-  md->num_fresh_coins = ntohs (mdp.num_fresh_coins);
-  md->fresh_pks = GNUNET_new_array (md->num_fresh_coins,
-                                    struct TALER_DenominationPublicKey);
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-    md->fresh_coins[i] = GNUNET_new_array (md->num_fresh_coins,
-                                           struct TALER_PlanchetSecretsP);
-  off = sizeof (struct MeltDataP);
-  ok = GNUNET_YES;
-  off += deserialize_melted_coin (&md->melted_coin,
-                                  &buf[off],
-                                  buf_size - off,
-                                  &ok);
-  for (unsigned int i = 0; (i<md->num_fresh_coins) && (GNUNET_YES == ok); i++)
-    off += deserialize_denomination_key (&md->fresh_pks[i],
-                                         &buf[off],
-                                         buf_size - off,
-                                         &ok);
-
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-    for (unsigned int j = 0; (j<md->num_fresh_coins) && (GNUNET_YES == ok); 
j++)
-      off += deserialize_fresh_coin (&md->fresh_coins[i][j],
-                                     &buf[off],
-                                     buf_size - off,
-                                     &ok);
-  if (off != buf_size)
-  {
-    GNUNET_break (0);
-    ok = GNUNET_NO;
-  }
-  if (GNUNET_YES != ok)
-  {
-    free_melt_data (md);
-    GNUNET_free (md);
-    return NULL;
-  }
-  return md;
-}
-
-
-/**
- * Melt (partially spent) coins to obtain fresh coins that are
- * unlinkable to the original coin(s).  Note that melting more
- * than one coin in a single request will make those coins linkable,
- * so the safest operation only melts one coin at a time.
- *
- * This API is typically used by a wallet.  Note that to ensure that
- * no money is lost in case of hardware failures, this operation does
- * not actually initiate the request. Instead, it generates a buffer
- * which the caller must store before proceeding with the actual call
- * to #TALER_EXCHANGE_refresh_melt() that will generate the request.
- *
- * This function does verify that the given request data is internally
- * consistent.  However, the @a melts_sigs are NOT verified.
- *
- * Aside from some non-trivial cryptographic operations that might
- * take a bit of CPU time to complete, this function returns
- * its result immediately and does not start any asynchronous
- * processing.  This function is also thread-safe.
- *
- * @param melt_priv private key of the coin to melt
- * @param melt_amount amount specifying how much
- *                     the coin will contribute to the melt (including fee)
- * @param melt_sig signature affirming the
- *                   validity of the public keys corresponding to the
- *                   @a melt_priv private key
- * @param melt_pk denomination key information
- *                   record corresponding to the @a melt_sig
- *                   validity of the keys
- * @param fresh_pks_len length of the @a pks array
- * @param fresh_pks array of @a pks_len denominations of fresh coins to create
- * @param[out] res_size set to the size of the return value, or 0 on error
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         Otherwise, pointer to a buffer of @a res_size to store persistently
- *         before proceeding to #TALER_EXCHANGE_refresh_melt().
- *         Non-null results should be freed using GNUNET_free().
- */
-char *
-TALER_EXCHANGE_refresh_prepare (const struct
-                                TALER_CoinSpendPrivateKeyP *melt_priv,
-                                const struct TALER_Amount *melt_amount,
-                                const struct
-                                TALER_DenominationSignature *melt_sig,
-                                const struct
-                                TALER_EXCHANGE_DenomPublicKey *melt_pk,
-                                unsigned int fresh_pks_len,
-                                const struct
-                                TALER_EXCHANGE_DenomPublicKey *fresh_pks,
-                                size_t *res_size)
-{
-  struct MeltData md;
-  char *buf;
-  struct TALER_Amount total;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-  struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
-  struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
-
-  GNUNET_CRYPTO_eddsa_key_get_public (&melt_priv->eddsa_priv,
-                                      &coin_pub.eddsa_pub);
-  /* build up melt data structure */
-  memset (&md, 0, sizeof (md));
-  md.num_fresh_coins = fresh_pks_len;
-  md.melted_coin.coin_priv = *melt_priv;
-  md.melted_coin.melt_amount_with_fee = *melt_amount;
-  md.melted_coin.fee_melt = melt_pk->fee_refresh;
-  md.melted_coin.original_value = melt_pk->value;
-  md.melted_coin.expire_deposit
-    = melt_pk->expire_deposit;
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (melt_amount->currency,
-                                        &total));
-  md.melted_coin.pub_key.rsa_public_key
-    = GNUNET_CRYPTO_rsa_public_key_dup (melt_pk->key.rsa_public_key);
-  md.melted_coin.sig.rsa_signature
-    = GNUNET_CRYPTO_rsa_signature_dup (melt_sig->rsa_signature);
-  md.fresh_pks = GNUNET_new_array (fresh_pks_len,
-                                   struct TALER_DenominationPublicKey);
-  for (unsigned int i = 0; i<fresh_pks_len; i++)
-  {
-    md.fresh_pks[i].rsa_public_key
-      = GNUNET_CRYPTO_rsa_public_key_dup (fresh_pks[i].key.rsa_public_key);
-    if ( (GNUNET_OK !=
-          TALER_amount_add (&total,
-                            &total,
-                            &fresh_pks[i].value)) ||
-         (GNUNET_OK !=
-          TALER_amount_add (&total,
-                            &total,
-                            &fresh_pks[i].fee_withdraw)) )
-    {
-      GNUNET_break (0);
-      free_melt_data (&md);
-      return NULL;
-    }
-  }
-  /* verify that melt_amount is above total cost */
-  if (1 ==
-      TALER_amount_cmp (&total,
-                        melt_amount) )
-  {
-    /* Eh, this operation is more expensive than the
-       @a melt_amount. This is not OK. */
-    GNUNET_break (0);
-    free_melt_data (&md);
-    return NULL;
-  }
-
-  /* build up coins */
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-  {
-    struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
-
-    tpk = GNUNET_CRYPTO_ecdhe_key_create ();
-    md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk;
-    GNUNET_free (tpk);
-
-    GNUNET_CRYPTO_ecdhe_key_get_public (
-      &md.melted_coin.transfer_priv[i].ecdhe_priv,
-      &rce[i].transfer_pub.ecdhe_pub);
-    TALER_link_derive_transfer_secret  (melt_priv,
-                                        &md.melted_coin.transfer_priv[i],
-                                        &trans_sec[i]);
-    md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
-                                          struct TALER_PlanchetSecretsP);
-    rce[i].new_coins = GNUNET_new_array (fresh_pks_len,
-                                         struct TALER_RefreshCoinData);
-    for (unsigned int j = 0; j<fresh_pks_len; j++)
-    {
-      struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
-      struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
-      struct TALER_PlanchetDetail pd;
-
-      TALER_planchet_setup_refresh (&trans_sec[i],
-                                    j,
-                                    fc);
-      if (GNUNET_OK !=
-          TALER_planchet_prepare (&md.fresh_pks[j],
-                                  fc,
-                                  &pd))
-      {
-        GNUNET_break_op (0);
-        free_melt_data (&md);
-        return NULL;
-      }
-      rcd->dk = &md.fresh_pks[j];
-      rcd->coin_ev = pd.coin_ev;
-      rcd->coin_ev_size = pd.coin_ev_size;
-    }
-  }
-
-  /* Compute refresh commitment */
-  TALER_refresh_get_commitment (&md.rc,
-                                TALER_CNC_KAPPA,
-                                fresh_pks_len,
-                                rce,
-                                &coin_pub,
-                                melt_amount);
-  /* finally, serialize everything */
-  buf = serialize_melt_data (&md,
-                             res_size);
-  for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
-  {
-    for (unsigned int j = 0; j < fresh_pks_len; j++)
-      GNUNET_free_non_null (rce[i].new_coins[j].coin_ev);
-    GNUNET_free_non_null (rce[i].new_coins);
-  }
-  free_melt_data (&md);
-  return buf;
-}
-
-
-/* ********************* /refresh/melt ***************************** */
-
-
-/**
- * @brief A /refresh/melt Handle
- */
-struct TALER_EXCHANGE_RefreshMeltHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext ctx;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with refresh melt failure results.
-   */
-  TALER_EXCHANGE_RefreshMeltCallback melt_cb;
-
-  /**
-   * Closure for @e result_cb and @e melt_failure_cb.
-   */
-  void *melt_cb_cls;
-
-  /**
-   * Actual information about the melt operation.
-   */
-  struct MeltData *md;
-
-  /**
-   * @brief Public information about the coin's denomination key
-   */
-  struct TALER_EXCHANGE_DenomPublicKey dki;
-};
-
-
-/**
- * Verify that the signature on the "200 OK" response
- * from the exchange is valid.
- *
- * @param rmh melt handle
- * @param json json reply with the signature
- * @param[out] exchange_pub public key of the exchange used for the signature
- * @param[out] noreveal_index set to the noreveal index selected by the 
exchange
- * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
- */
-static int
-verify_refresh_melt_signature_ok (struct TALER_EXCHANGE_RefreshMeltHandle *rmh,
-                                  const json_t *json,
-                                  struct TALER_ExchangePublicKeyP 
*exchange_pub,
-                                  uint32_t *noreveal_index)
-{
-  struct TALER_ExchangeSignatureP exchange_sig;
-  const struct TALER_EXCHANGE_Keys *key_state;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
-    GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
-    GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index),
-    GNUNET_JSON_spec_end ()
-  };
-  struct TALER_RefreshMeltConfirmationPS confirm;
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* check that exchange signing key is permitted */
-  key_state = TALER_EXCHANGE_get_keys (rmh->exchange);
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_test_signing_key (key_state,
-                                       exchange_pub))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* check that noreveal index is in permitted range */
-  if (TALER_CNC_KAPPA <= *noreveal_index)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* verify signature by exchange */
-  confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
-  confirm.purpose.size = htonl (sizeof (struct
-                                        TALER_RefreshMeltConfirmationPS));
-  confirm.rc = rmh->md->rc;
-  confirm.noreveal_index = htonl (*noreveal_index);
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
-                                  &confirm.purpose,
-                                  &exchange_sig.eddsa_signature,
-                                  &exchange_pub->eddsa_pub))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Verify that the signatures on the "409 CONFLICT" response from the
- * exchange demonstrating customer double-spending are valid.
- *
- * @param rmh melt handle
- * @param json json reply with the signature(s) and transaction history
- * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
- */
-static int
-verify_refresh_melt_signature_conflict (struct
-                                        TALER_EXCHANGE_RefreshMeltHandle *rmh,
-                                        const json_t *json)
-{
-  json_t *history;
-  struct TALER_Amount original_value;
-  struct TALER_Amount melt_value_with_fee;
-  struct TALER_Amount total;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_json ("history", &history),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
-    TALER_JSON_spec_amount ("original_value", &original_value),
-    TALER_JSON_spec_amount ("requested_value", &melt_value_with_fee),
-    GNUNET_JSON_spec_end ()
-  };
-  const struct MeltedCoin *mc;
-
-  /* parse JSON reply */
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* Find out which coin was deemed problematic by the exchange */
-  mc = &rmh->md->melted_coin;
-
-  /* check basic coin properties */
-  if (0 != TALER_amount_cmp (&original_value,
-                             &mc->original_value))
-  {
-    /* We disagree on the value of the coin */
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-  if (0 != TALER_amount_cmp (&melt_value_with_fee,
-                             &mc->melt_amount_with_fee))
-  {
-    /* We disagree on the value of the coin */
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-
-  /* verify coin history */
-  history = json_object_get (json,
-                             "history");
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (&rmh->dki,
-                                          original_value.currency,
-                                          &coin_pub,
-                                          history,
-                                          &total))
-  {
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-  json_decref (history);
-
-  /* check if melt operation was really too expensive given history */
-  if (GNUNET_OK !=
-      TALER_amount_add (&total,
-                        &total,
-                        &melt_value_with_fee))
-  {
-    /* clearly not OK if our transaction would have caused
-       the overflow... */
-    return GNUNET_OK;
-  }
-
-  if (0 >= TALER_amount_cmp (&total,
-                             &original_value))
-  {
-    /* transaction should have still fit */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* everything OK, valid proof of double-spending was provided */
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /refresh/melt request.
- *
- * @param cls the `struct TALER_EXCHANGE_RefreshMeltHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_refresh_melt_finished (void *cls,
-                              long response_code,
-                              const void *response)
-{
-  struct TALER_EXCHANGE_RefreshMeltHandle *rmh = cls;
-  uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
-  struct TALER_ExchangePublicKeyP exchange_pub;
-  const json_t *j = response;
-
-  rmh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        verify_refresh_melt_signature_ok (rmh,
-                                          j,
-                                          &exchange_pub,
-                                          &noreveal_index))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    if (NULL != rmh->melt_cb)
-    {
-      rmh->melt_cb (rmh->melt_cb_cls,
-                    response_code,
-                    TALER_JSON_get_error_code (j),
-                    noreveal_index,
-                    (0 == response_code) ? NULL : &exchange_pub,
-                    j);
-      rmh->melt_cb = NULL;
-    }
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_CONFLICT:
-    /* Double spending; check signatures on transaction history */
-    if (GNUNET_OK !=
-        verify_refresh_melt_signature_conflict (rmh,
-                                                j))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    /* Nothing really to verify, exchange says one of the signatures is
-       invalid; assuming we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, this should never
-       happen, we should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != rmh->melt_cb)
-    rmh->melt_cb (rmh->melt_cb_cls,
-                  response_code,
-                  TALER_JSON_get_error_code (j),
-                  UINT32_MAX,
-                  NULL,
-                  j);
-  TALER_EXCHANGE_refresh_melt_cancel (rmh);
-}
-
-
-/**
- * Submit a melt request to the exchange and get the exchange's
- * response.
- *
- * This API is typically used by a wallet.  Note that to ensure that
- * no money is lost in case of hardware failures, the provided
- * argument should have been constructed using
- * #TALER_EXCHANGE_refresh_prepare and committed to persistent storage
- * prior to calling this function.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param refresh_data_length size of the @a refresh_data (returned
- *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
- * @param refresh_data the refresh data as returned from
-          #TALER_EXCHANGE_refresh_prepare())
- * @param melt_cb the callback to call with the result
- * @param melt_cb_cls closure for @a melt_cb
- * @return a handle for this request; NULL if the argument was invalid.
- *         In this case, neither callback will be called.
- */
-struct TALER_EXCHANGE_RefreshMeltHandle *
-TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange,
-                             size_t refresh_data_length,
-                             const char *refresh_data,
-                             TALER_EXCHANGE_RefreshMeltCallback melt_cb,
-                             void *melt_cb_cls)
-{
-  const struct TALER_EXCHANGE_Keys *key_state;
-  const struct TALER_EXCHANGE_DenomPublicKey *dki;
-  json_t *melt_obj;
-  struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
-  CURL *eh;
-  struct GNUNET_CURL_Context *ctx;
-  struct MeltData *md;
-  struct TALER_CoinSpendSignatureP confirm_sig;
-  struct TALER_RefreshMeltCoinAffirmationPS melt;
-  struct GNUNET_HashCode h_denom_pub;
-
-  GNUNET_assert (GNUNET_YES ==
-                 TEAH_handle_is_ready (exchange));
-  md = deserialize_melt_data (refresh_data,
-                              refresh_data_length);
-  if (NULL == md)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
-  melt.purpose.size = htonl (sizeof (struct
-                                     TALER_RefreshMeltCoinAffirmationPS));
-  melt.rc = md->rc;
-  TALER_amount_hton (&melt.amount_with_fee,
-                     &md->melted_coin.melt_amount_with_fee);
-  TALER_amount_hton (&melt.melt_fee,
-                     &md->melted_coin.fee_melt);
-  GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
-                                      &melt.coin_pub.eddsa_pub);
-  GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv,
-                            &melt.purpose,
-                            &confirm_sig.eddsa_signature);
-  GNUNET_CRYPTO_rsa_public_key_hash (md->melted_coin.pub_key.rsa_public_key,
-                                     &h_denom_pub);
-  melt_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
-                        "coin_pub",
-                        GNUNET_JSON_from_data_auto (&melt.coin_pub),
-                        "denom_pub_hash",
-                        GNUNET_JSON_from_data_auto (&h_denom_pub),
-                        "denom_sig",
-                        GNUNET_JSON_from_rsa_signature (
-                          md->melted_coin.sig.rsa_signature),
-                        "confirm_sig",
-                        GNUNET_JSON_from_data_auto (&confirm_sig),
-                        "value_with_fee",
-                        TALER_JSON_from_amount (
-                          &md->melted_coin.melt_amount_with_fee),
-                        "rc",
-                        GNUNET_JSON_from_data_auto (&melt.rc));
-  if (NULL == melt_obj)
-  {
-    GNUNET_break (0);
-    free_melt_data (md);
-    return NULL;
-  }
-  key_state = TALER_EXCHANGE_get_keys (exchange);
-  dki = TALER_EXCHANGE_get_denomination_key (key_state,
-                                             &md->melted_coin.pub_key);
-
-  /* and now we can at last begin the actual request handling */
-  rmh = GNUNET_new (struct TALER_EXCHANGE_RefreshMeltHandle);
-  rmh->exchange = exchange;
-  rmh->dki = *dki;
-  rmh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
-                                         not copy the pointer */
-  rmh->melt_cb = melt_cb;
-  rmh->melt_cb_cls = melt_cb_cls;
-  rmh->md = md;
-  rmh->url = TEAH_path_to_url (exchange,
-                               "/refresh/melt");
-  eh = TEL_curl_easy_get (rmh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&rmh->ctx,
-                            eh,
-                            melt_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (melt_obj);
-    GNUNET_free (rmh->url);
-    GNUNET_free (rmh);
-    return NULL;
-  }
-  json_decref (melt_obj);
-  ctx = TEAH_handle_to_context (exchange);
-  rmh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   rmh->ctx.headers,
-                                   &handle_refresh_melt_finished,
-                                   rmh);
-  return rmh;
-}
-
-
-/**
- * Cancel a refresh execute request.  This function cannot be used
- * on a request handle if either callback was already invoked.
- *
- * @param rmh the refresh melt handle
- */
-void
-TALER_EXCHANGE_refresh_melt_cancel (struct
-                                    TALER_EXCHANGE_RefreshMeltHandle *rmh)
-{
-  if (NULL != rmh->job)
-  {
-    GNUNET_CURL_job_cancel (rmh->job);
-    rmh->job = NULL;
-  }
-  free_melt_data (rmh->md); /* does not free 'md' itself */
-  GNUNET_free (rmh->md);
-  GNUNET_free (rmh->url);
-  TALER_curl_easy_post_finished (&rmh->ctx);
-  GNUNET_free (rmh);
-}
-
-
-/* ********************* /refresh/reveal ***************************** */
-
-
-/**
- * @brief A /refresh/reveal Handle
- */
-struct TALER_EXCHANGE_RefreshRevealHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext ctx;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_RefreshRevealCallback reveal_cb;
-
-  /**
-   * Closure for @e reveal_cb.
-   */
-  void *reveal_cb_cls;
-
-  /**
-   * Actual information about the melt operation.
-   */
-  struct MeltData *md;
-
-  /**
-   * The index selected by the exchange in cut-and-choose to not be revealed.
-   */
-  uint16_t noreveal_index;
-
-};
-
-
-/**
- * We got a 200 OK response for the /refresh/reveal operation.
- * Extract the coin signatures and return them to the caller.
- * The signatures we get from the exchange is for the blinded value.
- * Thus, we first must unblind them and then should verify their
- * validity.
- *
- * If everything checks out, we return the unblinded signatures
- * to the application via the callback.
- *
- * @param rrh operation handle
- * @param json reply from the exchange
- * @param[out] sigs array of length `num_fresh_coins`, initialized to cointain 
RSA signatures
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static int
-refresh_reveal_ok (struct TALER_EXCHANGE_RefreshRevealHandle *rrh,
-                   const json_t *json,
-                   struct TALER_DenominationSignature *sigs)
-{
-  json_t *jsona;
-  struct GNUNET_JSON_Specification outer_spec[] = {
-    GNUNET_JSON_spec_json ("ev_sigs", &jsona),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         outer_spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  if (! json_is_array (jsona))
-  {
-    /* We expected an array of coins */
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (outer_spec);
-    return GNUNET_SYSERR;
-  }
-  if (rrh->md->num_fresh_coins != json_array_size (jsona))
-  {
-    /* Number of coins generated does not match our expectation */
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (outer_spec);
-    return GNUNET_SYSERR;
-  }
-  for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
-  {
-    const struct TALER_PlanchetSecretsP *fc;
-    struct TALER_DenominationPublicKey *pk;
-    json_t *jsonai;
-    struct GNUNET_CRYPTO_RsaSignature *blind_sig;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct GNUNET_HashCode coin_hash;
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
-      GNUNET_JSON_spec_end ()
-    };
-    struct TALER_FreshCoin coin;
-
-    fc = &rrh->md->fresh_coins[rrh->noreveal_index][i];
-    pk = &rrh->md->fresh_pks[i];
-    jsonai = json_array_get (jsona, i);
-    GNUNET_assert (NULL != jsonai);
-
-    if (GNUNET_OK !=
-        GNUNET_JSON_parse (jsonai,
-                           spec,
-                           NULL, NULL))
-    {
-      GNUNET_break_op (0);
-      GNUNET_JSON_parse_free (outer_spec);
-      return GNUNET_SYSERR;
-    }
-
-    /* needed to verify the signature, and we didn't store it earlier,
-       hence recomputing it here... */
-    GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
-                                        &coin_pub.eddsa_pub);
-    GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub,
-                        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
-                        &coin_hash);
-    if (GNUNET_OK !=
-        TALER_planchet_to_coin (pk,
-                                blind_sig,
-                                fc,
-                                &coin_hash,
-                                &coin))
-    {
-      GNUNET_break_op (0);
-      GNUNET_CRYPTO_rsa_signature_free (blind_sig);
-      GNUNET_JSON_parse_free (outer_spec);
-      return GNUNET_SYSERR;
-    }
-    GNUNET_CRYPTO_rsa_signature_free (blind_sig);
-    sigs[i] = coin.sig;
-  }
-  GNUNET_JSON_parse_free (outer_spec);
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /refresh/reveal request.
- *
- * @param cls the `struct TALER_EXCHANGE_RefreshHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_refresh_reveal_finished (void *cls,
-                                long response_code,
-                                const void *response)
-{
-  struct TALER_EXCHANGE_RefreshRevealHandle *rrh = cls;
-  const json_t *j = response;
-
-  rrh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    {
-      struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins];
-      int ret;
-
-      memset (sigs, 0, sizeof (sigs));
-      ret = refresh_reveal_ok (rrh,
-                               j,
-                               sigs);
-      if (GNUNET_OK != ret)
-      {
-        response_code = 0;
-      }
-      else
-      {
-        rrh->reveal_cb (rrh->reveal_cb_cls,
-                        MHD_HTTP_OK,
-                        TALER_EC_NONE,
-                        rrh->md->num_fresh_coins,
-                        rrh->md->fresh_coins[rrh->noreveal_index],
-                        sigs,
-                        j);
-        rrh->reveal_cb = NULL;
-      }
-      for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
-        if (NULL != sigs[i].rsa_signature)
-          GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
-    }
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_CONFLICT:
-    /* Nothing really to verify, exchange says our reveal is inconsitent
-       with our commitment, so either side is buggy; we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != rrh->reveal_cb)
-    rrh->reveal_cb (rrh->reveal_cb_cls,
-                    response_code,
-                    TALER_JSON_get_error_code (j),
-                    0,
-                    NULL,
-                    NULL,
-                    j);
-  TALER_EXCHANGE_refresh_reveal_cancel (rrh);
-}
-
-
-/**
- * Submit a /refresh/reval request to the exchange and get the exchange's
- * response.
- *
- * This API is typically used by a wallet.  Note that to ensure that
- * no money is lost in case of hardware failures, the provided
- * arguments should have been committed to persistent storage
- * prior to calling this function.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param refresh_data_length size of the @a refresh_data (returned
- *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
- * @param refresh_data the refresh data as returned from
-          #TALER_EXCHANGE_refresh_prepare())
- * @param noreveal_index response from the exchange to the
- *        #TALER_EXCHANGE_refresh_melt() invocation
- * @param reveal_cb the callback to call with the final result of the
- *        refresh operation
- * @param reveal_cb_cls closure for the above callback
- * @return a handle for this request; NULL if the argument was invalid.
- *         In this case, neither callback will be called.
- */
-struct TALER_EXCHANGE_RefreshRevealHandle *
-TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
-                               size_t refresh_data_length,
-                               const char *refresh_data,
-                               uint32_t noreveal_index,
-                               TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
-                               void *reveal_cb_cls)
-{
-  struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
-  json_t *transfer_privs;
-  json_t *new_denoms_h;
-  json_t *coin_evs;
-  json_t *reveal_obj;
-  json_t *link_sigs;
-  CURL *eh;
-  struct GNUNET_CURL_Context *ctx;
-  struct MeltData *md;
-  struct TALER_TransferPublicKeyP transfer_pub;
-
-  if (noreveal_index >= TALER_CNC_KAPPA)
-  {
-    /* We check this here, as it would be really bad to below just
-       disclose all the transfer keys. Note that this error should
-       have been caught way earlier when the exchange replied, but maybe
-       we had some internal corruption that changed the value... */
-    GNUNET_break (0);
-    return NULL;
-  }
-  if (GNUNET_YES !=
-      TEAH_handle_is_ready (exchange))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  md = deserialize_melt_data (refresh_data,
-                              refresh_data_length);
-  if (NULL == md)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  /* now transfer_pub */
-  GNUNET_CRYPTO_ecdhe_key_get_public (
-    &md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv,
-    &transfer_pub.ecdhe_pub);
-
-  /* now new_denoms */
-  GNUNET_assert (NULL != (new_denoms_h = json_array ()));
-  GNUNET_assert (NULL != (coin_evs = json_array ()));
-  GNUNET_assert (NULL != (link_sigs = json_array ()));
-  for (unsigned int i = 0; i<md->num_fresh_coins; i++)
-  {
-    struct GNUNET_HashCode denom_hash;
-    struct TALER_PlanchetDetail pd;
-
-    GNUNET_CRYPTO_rsa_public_key_hash (md->fresh_pks[i].rsa_public_key,
-                                       &denom_hash);
-    GNUNET_assert (0 ==
-                   json_array_append_new (new_denoms_h,
-                                          GNUNET_JSON_from_data_auto (
-                                            &denom_hash)));
-
-    if (GNUNET_OK !=
-        TALER_planchet_prepare (&md->fresh_pks[i],
-                                &md->fresh_coins[noreveal_index][i],
-                                &pd))
-    {
-      /* This should have been noticed during the preparation stage. */
-      GNUNET_break (0);
-      json_decref (new_denoms_h);
-      json_decref (coin_evs);
-      return NULL;
-    }
-    GNUNET_assert (0 ==
-                   json_array_append_new (coin_evs,
-                                          GNUNET_JSON_from_data (pd.coin_ev,
-                                                                 
pd.coin_ev_size)));
-
-    /* compute link signature */
-    {
-      struct TALER_CoinSpendSignatureP link_sig;
-      struct TALER_LinkDataPS ldp;
-
-      ldp.purpose.size = htonl (sizeof (ldp));
-      ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
-      ldp.h_denom_pub = denom_hash;
-      GNUNET_CRYPTO_eddsa_key_get_public 
(&md->melted_coin.coin_priv.eddsa_priv,
-                                          &ldp.old_coin_pub.eddsa_pub);
-      ldp.transfer_pub = transfer_pub;
-      GNUNET_CRYPTO_hash (pd.coin_ev,
-                          pd.coin_ev_size,
-                          &ldp.coin_envelope_hash);
-      GNUNET_assert (GNUNET_OK ==
-                     GNUNET_CRYPTO_eddsa_sign (
-                       &md->melted_coin.coin_priv.eddsa_priv,
-                       &ldp.purpose,
-                       &link_sig.eddsa_signature));
-      GNUNET_assert (0 ==
-                     json_array_append_new (link_sigs,
-                                            GNUNET_JSON_from_data_auto (
-                                              &link_sig)));
-    }
-
-    GNUNET_free (pd.coin_ev);
-  }
-
-  /* build array of transfer private keys */
-  GNUNET_assert (NULL != (transfer_privs = json_array ()));
-  for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++)
-  {
-    if (j == noreveal_index)
-    {
-      /* This is crucial: exclude the transfer key for the
-   noreval index! */
-      continue;
-    }
-    GNUNET_assert (0 ==
-                   json_array_append_new (transfer_privs,
-                                          GNUNET_JSON_from_data_auto (
-                                            
&md->melted_coin.transfer_priv[j])));
-  }
-
-  /* build main JSON request */
-  reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
-                          "rc",
-                          GNUNET_JSON_from_data_auto (&md->rc),
-                          "transfer_pub",
-                          GNUNET_JSON_from_data_auto (&transfer_pub),
-                          "transfer_privs",
-                          transfer_privs,
-                          "link_sigs",
-                          link_sigs,
-                          "new_denoms_h",
-                          new_denoms_h,
-                          "coin_evs",
-                          coin_evs);
-  if (NULL == reveal_obj)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  /* finally, we can actually issue the request */
-  rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshRevealHandle);
-  rrh->exchange = exchange;
-  rrh->noreveal_index = noreveal_index;
-  rrh->reveal_cb = reveal_cb;
-  rrh->reveal_cb_cls = reveal_cb_cls;
-  rrh->md = md;
-  rrh->url = TEAH_path_to_url (rrh->exchange,
-                               "/refresh/reveal");
-
-  eh = TEL_curl_easy_get (rrh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&rrh->ctx,
-                            eh,
-                            reveal_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (reveal_obj);
-    GNUNET_free (rrh->url);
-    GNUNET_free (rrh);
-    return NULL;
-  }
-  json_decref (reveal_obj);
-  ctx = TEAH_handle_to_context (rrh->exchange);
-  rrh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   rrh->ctx.headers,
-                                   &handle_refresh_reveal_finished,
-                                   rrh);
-  return rrh;
-}
-
-
-/**
- * Cancel a refresh reveal request.  This function cannot be used
- * on a request handle if the callback was already invoked.
- *
- * @param rrh the refresh reval handle
- */
-void
-TALER_EXCHANGE_refresh_reveal_cancel (struct
-                                      TALER_EXCHANGE_RefreshRevealHandle *rrh)
-{
-  if (NULL != rrh->job)
-  {
-    GNUNET_CURL_job_cancel (rrh->job);
-    rrh->job = NULL;
-  }
-  GNUNET_free (rrh->url);
-  TALER_curl_easy_post_finished (&rrh->ctx);
-  free_melt_data (rrh->md); /* does not free 'md' itself */
-  GNUNET_free (rrh->md);
-  GNUNET_free (rrh);
-}
-
-
-/* end of exchange_api_refresh.c */
diff --git a/src/lib/exchange_api_refresh_common.c 
b/src/lib/exchange_api_refresh_common.c
new file mode 100644
index 00000000..b2eec291
--- /dev/null
+++ b/src/lib/exchange_api_refresh_common.c
@@ -0,0 +1,631 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_refresh_common.c
+ * @brief Serialization logic shared between melt and reveal steps during 
refreshing
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "exchange_api_refresh_common.h"
+
+
+/**
+ * Free all information associated with a melted coin session.
+ *
+ * @param mc melted coin to release, the pointer itself is NOT
+ *           freed (as it is typically not allocated by itself)
+ */
+static void
+free_melted_coin (struct MeltedCoin *mc)
+{
+  if (NULL != mc->pub_key.rsa_public_key)
+    GNUNET_CRYPTO_rsa_public_key_free (mc->pub_key.rsa_public_key);
+  if (NULL != mc->sig.rsa_signature)
+    GNUNET_CRYPTO_rsa_signature_free (mc->sig.rsa_signature);
+}
+
+
+/**
+ * Free all information associated with a melting session.  Note
+ * that we allow the melting session to be only partially initialized,
+ * as we use this function also when freeing melt data that was not
+ * fully initialized (i.e. due to failures in 
#TALER_EXCHANGE_deserialize_melt_data_()).
+ *
+ * @param md melting data to release, the pointer itself is NOT
+ *           freed (as it is typically not allocated by itself)
+ */
+void
+TALER_EXCHANGE_free_melt_data_ (struct MeltData *md)
+{
+  free_melted_coin (&md->melted_coin);
+  if (NULL != md->fresh_pks)
+  {
+    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
+      if (NULL != md->fresh_pks[i].rsa_public_key)
+        GNUNET_CRYPTO_rsa_public_key_free (md->fresh_pks[i].rsa_public_key);
+    GNUNET_free (md->fresh_pks);
+  }
+
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+    GNUNET_free_non_null (md->fresh_coins[i]);
+  /* Finally, clean up a bit...
+     (NOTE: compilers might optimize this away, so this is
+     not providing any strong assurances that the key material
+     is purged.) */
+  memset (md,
+          0,
+          sizeof (struct MeltData));
+}
+
+
+/**
+ * Serialize information about a coin we are melting.
+ *
+ * @param mc information to serialize
+ * @param buf buffer to write data in, NULL to just compute
+ *            required size
+ * @param off offeset at @a buf to use
+ * @return number of bytes written to @a buf at @a off, or if
+ *        @a buf is NULL, number of bytes required; 0 on error
+ */
+static size_t
+serialize_melted_coin (const struct MeltedCoin *mc,
+                       char *buf,
+                       size_t off)
+{
+  struct MeltedCoinP mcp;
+  unsigned int i;
+  char *pbuf;
+  size_t pbuf_size;
+  char *sbuf;
+  size_t sbuf_size;
+
+  sbuf_size = GNUNET_CRYPTO_rsa_signature_encode (mc->sig.rsa_signature,
+                                                  &sbuf);
+  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (mc->pub_key.rsa_public_key,
+                                                   &pbuf);
+  if (NULL == buf)
+  {
+    GNUNET_free (sbuf);
+    GNUNET_free (pbuf);
+    return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
+  }
+  if ( (sbuf_size > UINT16_MAX) ||
+       (pbuf_size > UINT16_MAX) )
+  {
+    GNUNET_break (0);
+    return 0;
+  }
+  mcp.coin_priv = mc->coin_priv;
+  TALER_amount_hton (&mcp.melt_amount_with_fee,
+                     &mc->melt_amount_with_fee);
+  TALER_amount_hton (&mcp.fee_melt,
+                     &mc->fee_melt);
+  TALER_amount_hton (&mcp.original_value,
+                     &mc->original_value);
+  for (i = 0; i<TALER_CNC_KAPPA; i++)
+    mcp.transfer_priv[i] = mc->transfer_priv[i];
+  mcp.expire_deposit = GNUNET_TIME_absolute_hton (mc->expire_deposit);
+  mcp.pbuf_size = htons ((uint16_t) pbuf_size);
+  mcp.sbuf_size = htons ((uint16_t) sbuf_size);
+  memcpy (&buf[off],
+          &mcp,
+          sizeof (struct MeltedCoinP));
+  memcpy (&buf[off + sizeof (struct MeltedCoinP)],
+          pbuf,
+          pbuf_size);
+  memcpy (&buf[off + sizeof (struct MeltedCoinP) + pbuf_size],
+          sbuf,
+          sbuf_size);
+  GNUNET_free (sbuf);
+  GNUNET_free (pbuf);
+  return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
+}
+
+
+/**
+ * Deserialize information about a coin we are melting.
+ *
+ * @param[out] mc information to deserialize
+ * @param buf buffer to read data from
+ * @param size number of bytes available at @a buf to use
+ * @param[out] ok set to #GNUNET_NO to report errors
+ * @return number of bytes read from @a buf, 0 on error
+ */
+static size_t
+deserialize_melted_coin (struct MeltedCoin *mc,
+                         const char *buf,
+                         size_t size,
+                         int *ok)
+{
+  struct MeltedCoinP mcp;
+  unsigned int i;
+  size_t pbuf_size;
+  size_t sbuf_size;
+  size_t off;
+
+  if (size < sizeof (struct MeltedCoinP))
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  memcpy (&mcp,
+          buf,
+          sizeof (struct MeltedCoinP));
+  pbuf_size = ntohs (mcp.pbuf_size);
+  sbuf_size = ntohs (mcp.sbuf_size);
+  if (size < sizeof (struct MeltedCoinP) + pbuf_size + sbuf_size)
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  off = sizeof (struct MeltedCoinP);
+  mc->pub_key.rsa_public_key
+    = GNUNET_CRYPTO_rsa_public_key_decode (&buf[off],
+                                           pbuf_size);
+  off += pbuf_size;
+  mc->sig.rsa_signature
+    = GNUNET_CRYPTO_rsa_signature_decode (&buf[off],
+                                          sbuf_size);
+  off += sbuf_size;
+  if ( (NULL == mc->pub_key.rsa_public_key) ||
+       (NULL == mc->sig.rsa_signature) )
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+
+  mc->coin_priv = mcp.coin_priv;
+  TALER_amount_ntoh (&mc->melt_amount_with_fee,
+                     &mcp.melt_amount_with_fee);
+  TALER_amount_ntoh (&mc->fee_melt,
+                     &mcp.fee_melt);
+  TALER_amount_ntoh (&mc->original_value,
+                     &mcp.original_value);
+  for (i = 0; i<TALER_CNC_KAPPA; i++)
+    mc->transfer_priv[i] = mcp.transfer_priv[i];
+  mc->expire_deposit = GNUNET_TIME_absolute_ntoh (mcp.expire_deposit);
+  return off;
+}
+
+
+/**
+ * Serialize information about a denomination key.
+ *
+ * @param dk information to serialize
+ * @param buf buffer to write data in, NULL to just compute
+ *            required size
+ * @param off offeset at @a buf to use
+ * @return number of bytes written to @a buf at @a off, or if
+ *        @a buf is NULL, number of bytes required
+ */
+static size_t
+serialize_denomination_key (const struct TALER_DenominationPublicKey *dk,
+                            char *buf,
+                            size_t off)
+{
+  char *pbuf;
+  size_t pbuf_size;
+  uint32_t be;
+
+  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (dk->rsa_public_key,
+                                                   &pbuf);
+  if (NULL == buf)
+  {
+    GNUNET_free (pbuf);
+    return pbuf_size + sizeof (uint32_t);
+  }
+  be = htonl ((uint32_t) pbuf_size);
+  memcpy (&buf[off],
+          &be,
+          sizeof (uint32_t));
+  memcpy (&buf[off + sizeof (uint32_t)],
+          pbuf,
+          pbuf_size);
+  GNUNET_free (pbuf);
+  return pbuf_size + sizeof (uint32_t);
+}
+
+
+/**
+ * Deserialize information about a denomination key.
+ *
+ * @param[out] dk information to deserialize
+ * @param buf buffer to read data from
+ * @param size number of bytes available at @a buf to use
+ * @param[out] ok set to #GNUNET_NO to report errors
+ * @return number of bytes read from @a buf, 0 on error
+ */
+static size_t
+deserialize_denomination_key (struct TALER_DenominationPublicKey *dk,
+                              const char *buf,
+                              size_t size,
+                              int *ok)
+{
+  size_t pbuf_size;
+  uint32_t be;
+
+  if (size < sizeof (uint32_t))
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  memcpy (&be,
+          buf,
+          sizeof (uint32_t));
+  pbuf_size = ntohl (be);
+  if (size < sizeof (uint32_t) + pbuf_size)
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  dk->rsa_public_key
+    = GNUNET_CRYPTO_rsa_public_key_decode (&buf[sizeof (uint32_t)],
+                                           pbuf_size);
+  if (NULL == dk->rsa_public_key)
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  return sizeof (uint32_t) + pbuf_size;
+}
+
+
+/**
+ * Serialize information about a fresh coin we are generating.
+ *
+ * @param fc information to serialize
+ * @param buf buffer to write data in, NULL to just compute
+ *            required size
+ * @param off offeset at @a buf to use
+ * @return number of bytes written to @a buf at @a off, or if
+ *        @a buf is NULL, number of bytes required
+ */
+static size_t
+serialize_fresh_coin (const struct TALER_PlanchetSecretsP *fc,
+                      char *buf,
+                      size_t off)
+{
+  if (NULL != buf)
+    memcpy (&buf[off],
+            fc,
+            sizeof (struct TALER_PlanchetSecretsP));
+  return sizeof (struct TALER_PlanchetSecretsP);
+}
+
+
+/**
+ * Deserialize information about a fresh coin we are generating.
+ *
+ * @param[out] fc information to deserialize
+ * @param buf buffer to read data from
+ * @param size number of bytes available at @a buf to use
+ * @param[out] ok set to #GNUNET_NO to report errors
+ * @return number of bytes read from @a buf, 0 on error
+ */
+static size_t
+deserialize_fresh_coin (struct TALER_PlanchetSecretsP *fc,
+                        const char *buf,
+                        size_t size,
+                        int *ok)
+{
+  if (size < sizeof (struct TALER_PlanchetSecretsP))
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  memcpy (fc,
+          buf,
+          sizeof (struct TALER_PlanchetSecretsP));
+  return sizeof (struct TALER_PlanchetSecretsP);
+}
+
+
+/**
+ * Serialize melt data.
+ *
+ * @param md data to serialize
+ * @param[out] res_size size of buffer returned
+ * @return serialized melt data
+ */
+static char *
+serialize_melt_data (const struct MeltData *md,
+                     size_t *res_size)
+{
+  size_t size;
+  size_t asize;
+  char *buf;
+
+  size = 0;
+  asize = (size_t) -1; /* make the compiler happy */
+  buf = NULL;
+  /* we do 2 iterations, #1 to determine total size, #2 to
+     actually construct the buffer */
+  do {
+    if (0 == size)
+    {
+      size = sizeof (struct MeltDataP);
+    }
+    else
+    {
+      struct MeltDataP *mdp;
+
+      buf = GNUNET_malloc (size);
+      asize = size; /* just for invariant check later */
+      size = sizeof (struct MeltDataP);
+      mdp = (struct MeltDataP *) buf;
+      mdp->rc = md->rc;
+      mdp->num_fresh_coins = htons (md->num_fresh_coins);
+    }
+    size += serialize_melted_coin (&md->melted_coin,
+                                   buf,
+                                   size);
+    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
+      size += serialize_denomination_key (&md->fresh_pks[i],
+                                          buf,
+                                          size);
+    for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+      for (unsigned int j = 0; j<md->num_fresh_coins; j++)
+        size += serialize_fresh_coin (&md->fresh_coins[i][j],
+                                      buf,
+                                      size);
+  } while (NULL == buf);
+  GNUNET_assert (size == asize);
+  *res_size = size;
+  return buf;
+}
+
+
+/**
+ * Deserialize melt data.
+ *
+ * @param buf serialized data
+ * @param buf_size size of @a buf
+ * @return deserialized melt data, NULL on error
+ */
+struct MeltData *
+TALER_EXCHANGE_deserialize_melt_data_ (const char *buf,
+                                       size_t buf_size)
+{
+  struct MeltData *md;
+  struct MeltDataP mdp;
+  size_t off;
+  int ok;
+
+  if (buf_size < sizeof (struct MeltDataP))
+    return NULL;
+  memcpy (&mdp,
+          buf,
+          sizeof (struct MeltDataP));
+  md = GNUNET_new (struct MeltData);
+  md->rc = mdp.rc;
+  md->num_fresh_coins = ntohs (mdp.num_fresh_coins);
+  md->fresh_pks = GNUNET_new_array (md->num_fresh_coins,
+                                    struct TALER_DenominationPublicKey);
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+    md->fresh_coins[i] = GNUNET_new_array (md->num_fresh_coins,
+                                           struct TALER_PlanchetSecretsP);
+  off = sizeof (struct MeltDataP);
+  ok = GNUNET_YES;
+  off += deserialize_melted_coin (&md->melted_coin,
+                                  &buf[off],
+                                  buf_size - off,
+                                  &ok);
+  for (unsigned int i = 0; (i<md->num_fresh_coins) && (GNUNET_YES == ok); i++)
+    off += deserialize_denomination_key (&md->fresh_pks[i],
+                                         &buf[off],
+                                         buf_size - off,
+                                         &ok);
+
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+    for (unsigned int j = 0; (j<md->num_fresh_coins) && (GNUNET_YES == ok); 
j++)
+      off += deserialize_fresh_coin (&md->fresh_coins[i][j],
+                                     &buf[off],
+                                     buf_size - off,
+                                     &ok);
+  if (off != buf_size)
+  {
+    GNUNET_break (0);
+    ok = GNUNET_NO;
+  }
+  if (GNUNET_YES != ok)
+  {
+    TALER_EXCHANGE_free_melt_data_ (md);
+    GNUNET_free (md);
+    return NULL;
+  }
+  return md;
+}
+
+
+/**
+ * Melt (partially spent) coins to obtain fresh coins that are
+ * unlinkable to the original coin(s).  Note that melting more
+ * than one coin in a single request will make those coins linkable,
+ * so the safest operation only melts one coin at a time.
+ *
+ * This API is typically used by a wallet.  Note that to ensure that
+ * no money is lost in case of hardware failures, this operation does
+ * not actually initiate the request. Instead, it generates a buffer
+ * which the caller must store before proceeding with the actual call
+ * to #TALER_EXCHANGE_melt() that will generate the request.
+ *
+ * This function does verify that the given request data is internally
+ * consistent.  However, the @a melts_sigs are NOT verified.
+ *
+ * Aside from some non-trivial cryptographic operations that might
+ * take a bit of CPU time to complete, this function returns
+ * its result immediately and does not start any asynchronous
+ * processing.  This function is also thread-safe.
+ *
+ * @param melt_priv private key of the coin to melt
+ * @param melt_amount amount specifying how much
+ *                     the coin will contribute to the melt (including fee)
+ * @param melt_sig signature affirming the
+ *                   validity of the public keys corresponding to the
+ *                   @a melt_priv private key
+ * @param melt_pk denomination key information
+ *                   record corresponding to the @a melt_sig
+ *                   validity of the keys
+ * @param fresh_pks_len length of the @a pks array
+ * @param fresh_pks array of @a pks_len denominations of fresh coins to create
+ * @param[out] res_size set to the size of the return value, or 0 on error
+ * @return NULL
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         Otherwise, pointer to a buffer of @a res_size to store persistently
+ *         before proceeding to #TALER_EXCHANGE_melt().
+ *         Non-null results should be freed using GNUNET_free().
+ */
+char *
+TALER_EXCHANGE_refresh_prepare (const struct
+                                TALER_CoinSpendPrivateKeyP *melt_priv,
+                                const struct TALER_Amount *melt_amount,
+                                const struct
+                                TALER_DenominationSignature *melt_sig,
+                                const struct
+                                TALER_EXCHANGE_DenomPublicKey *melt_pk,
+                                unsigned int fresh_pks_len,
+                                const struct
+                                TALER_EXCHANGE_DenomPublicKey *fresh_pks,
+                                size_t *res_size)
+{
+  struct MeltData md;
+  char *buf;
+  struct TALER_Amount total;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
+  struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&melt_priv->eddsa_priv,
+                                      &coin_pub.eddsa_pub);
+  /* build up melt data structure */
+  memset (&md, 0, sizeof (md));
+  md.num_fresh_coins = fresh_pks_len;
+  md.melted_coin.coin_priv = *melt_priv;
+  md.melted_coin.melt_amount_with_fee = *melt_amount;
+  md.melted_coin.fee_melt = melt_pk->fee_refresh;
+  md.melted_coin.original_value = melt_pk->value;
+  md.melted_coin.expire_deposit
+    = melt_pk->expire_deposit;
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (melt_amount->currency,
+                                        &total));
+  md.melted_coin.pub_key.rsa_public_key
+    = GNUNET_CRYPTO_rsa_public_key_dup (melt_pk->key.rsa_public_key);
+  md.melted_coin.sig.rsa_signature
+    = GNUNET_CRYPTO_rsa_signature_dup (melt_sig->rsa_signature);
+  md.fresh_pks = GNUNET_new_array (fresh_pks_len,
+                                   struct TALER_DenominationPublicKey);
+  for (unsigned int i = 0; i<fresh_pks_len; i++)
+  {
+    md.fresh_pks[i].rsa_public_key
+      = GNUNET_CRYPTO_rsa_public_key_dup (fresh_pks[i].key.rsa_public_key);
+    if ( (GNUNET_OK !=
+          TALER_amount_add (&total,
+                            &total,
+                            &fresh_pks[i].value)) ||
+         (GNUNET_OK !=
+          TALER_amount_add (&total,
+                            &total,
+                            &fresh_pks[i].fee_withdraw)) )
+    {
+      GNUNET_break (0);
+      TALER_EXCHANGE_free_melt_data_ (&md);
+      return NULL;
+    }
+  }
+  /* verify that melt_amount is above total cost */
+  if (1 ==
+      TALER_amount_cmp (&total,
+                        melt_amount) )
+  {
+    /* Eh, this operation is more expensive than the
+       @a melt_amount. This is not OK. */
+    GNUNET_break (0);
+    TALER_EXCHANGE_free_melt_data_ (&md);
+    return NULL;
+  }
+
+  /* build up coins */
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+  {
+    struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
+
+    tpk = GNUNET_CRYPTO_ecdhe_key_create ();
+    md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk;
+    GNUNET_free (tpk);
+
+    GNUNET_CRYPTO_ecdhe_key_get_public (
+      &md.melted_coin.transfer_priv[i].ecdhe_priv,
+      &rce[i].transfer_pub.ecdhe_pub);
+    TALER_link_derive_transfer_secret  (melt_priv,
+                                        &md.melted_coin.transfer_priv[i],
+                                        &trans_sec[i]);
+    md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
+                                          struct TALER_PlanchetSecretsP);
+    rce[i].new_coins = GNUNET_new_array (fresh_pks_len,
+                                         struct TALER_RefreshCoinData);
+    for (unsigned int j = 0; j<fresh_pks_len; j++)
+    {
+      struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
+      struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
+      struct TALER_PlanchetDetail pd;
+
+      TALER_planchet_setup_refresh (&trans_sec[i],
+                                    j,
+                                    fc);
+      if (GNUNET_OK !=
+          TALER_planchet_prepare (&md.fresh_pks[j],
+                                  fc,
+                                  &pd))
+      {
+        GNUNET_break_op (0);
+        TALER_EXCHANGE_free_melt_data_ (&md);
+        return NULL;
+      }
+      rcd->dk = &md.fresh_pks[j];
+      rcd->coin_ev = pd.coin_ev;
+      rcd->coin_ev_size = pd.coin_ev_size;
+    }
+  }
+
+  /* Compute refresh commitment */
+  TALER_refresh_get_commitment (&md.rc,
+                                TALER_CNC_KAPPA,
+                                fresh_pks_len,
+                                rce,
+                                &coin_pub,
+                                melt_amount);
+  /* finally, serialize everything */
+  buf = serialize_melt_data (&md,
+                             res_size);
+  for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
+  {
+    for (unsigned int j = 0; j < fresh_pks_len; j++)
+      GNUNET_free_non_null (rce[i].new_coins[j].coin_ev);
+    GNUNET_free_non_null (rce[i].new_coins);
+  }
+  TALER_EXCHANGE_free_melt_data_ (&md);
+  return buf;
+}
diff --git a/src/lib/exchange_api_refresh_common.h 
b/src/lib/exchange_api_refresh_common.h
new file mode 100644
index 00000000..05988e65
--- /dev/null
+++ b/src/lib/exchange_api_refresh_common.h
@@ -0,0 +1,240 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_refresh.c
+ * @brief Implementation of the /refresh/melt+reveal requests of the 
exchange's HTTP API
+ * @author Christian Grothoff
+ */
+#ifndef REFRESH_COMMON_H
+#define REFRESH_COMMON_H
+#include <jansson.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "taler_signatures.h"
+
+
+/* structures for committing refresh data to disk before doing the
+   network interaction(s) */
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Header of serialized information about a coin we are melting.
+ */
+struct MeltedCoinP
+{
+  /**
+   * Private key of the coin.
+   */
+  struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+  /**
+   * Amount this coin contributes to the melt, including fee.
+   */
+  struct TALER_AmountNBO melt_amount_with_fee;
+
+  /**
+   * The applicable fee for withdrawing a coin of this denomination
+   */
+  struct TALER_AmountNBO fee_melt;
+
+  /**
+   * The original value of the coin.
+   */
+  struct TALER_AmountNBO original_value;
+
+  /**
+   * Transfer private keys for each cut-and-choose dimension.
+   */
+  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
+
+  /**
+   * Timestamp indicating when coins of this denomination become invalid.
+   */
+  struct GNUNET_TIME_AbsoluteNBO expire_deposit;
+
+  /**
+   * Size of the encoded public key that follows.
+   */
+  uint16_t pbuf_size;
+
+  /**
+   * Size of the encoded signature that follows.
+   */
+  uint16_t sbuf_size;
+
+  /* Followed by serializations of:
+     1) struct TALER_DenominationPublicKey pub_key;
+     2) struct TALER_DenominationSignature sig;
+  */
+};
+
+
+/**
+ * Header of serialized data about a melt operation, suitable for
+ * persisting it on disk.
+ */
+struct MeltDataP
+{
+
+  /**
+   * Hash over the melting session.
+   */
+  struct TALER_RefreshCommitmentP rc;
+
+  /**
+   * Number of coins we are melting, in NBO
+   */
+  uint16_t num_melted_coins GNUNET_PACKED;
+
+  /**
+   * Number of coins we are creating, in NBO
+   */
+  uint16_t num_fresh_coins GNUNET_PACKED;
+
+  /* Followed by serializations of:
+     1) struct MeltedCoinP melted_coins[num_melted_coins];
+     2) struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins];
+     3) TALER_CNC_KAPPA times:
+        3a) struct TALER_PlanchetSecretsP fresh_coins[num_fresh_coins];
+  */
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * Information about a coin we are melting.
+ */
+struct MeltedCoin
+{
+  /**
+   * Private key of the coin.
+   */
+  struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+  /**
+   * Amount this coin contributes to the melt, including fee.
+   */
+  struct TALER_Amount melt_amount_with_fee;
+
+  /**
+   * The applicable fee for melting a coin of this denomination
+   */
+  struct TALER_Amount fee_melt;
+
+  /**
+   * The original value of the coin.
+   */
+  struct TALER_Amount original_value;
+
+  /**
+   * Transfer private keys for each cut-and-choose dimension.
+   */
+  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
+
+  /**
+   * Timestamp indicating when coins of this denomination become invalid.
+   */
+  struct GNUNET_TIME_Absolute expire_deposit;
+
+  /**
+   * Denomination key of the original coin.
+   */
+  struct TALER_DenominationPublicKey pub_key;
+
+  /**
+   * Exchange's signature over the coin.
+   */
+  struct TALER_DenominationSignature sig;
+
+};
+
+
+/**
+ * Melt data in non-serialized format for convenient processing.
+ */
+struct MeltData
+{
+
+  /**
+   * Hash over the committed data during refresh operation.
+   */
+  struct TALER_RefreshCommitmentP rc;
+
+  /**
+   * Number of coins we are creating
+   */
+  uint16_t num_fresh_coins;
+
+  /**
+   * Information about the melted coin.
+   */
+  struct MeltedCoin melted_coin;
+
+  /**
+   * Array of @e num_fresh_coins denomination keys for the coins to be
+   * freshly exchangeed.
+   */
+  struct TALER_DenominationPublicKey *fresh_pks;
+
+  /**
+   * Arrays of @e num_fresh_coins with information about the fresh
+   * coins to be created, for each cut-and-choose dimension.
+   */
+  struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
+};
+
+
+/**
+ * Serialize melt data.
+ *
+ * @param md data to serialize
+ * @param[out] res_size size of buffer returned
+ * @return serialized melt data
+ */
+char *
+TALER_EXCHANGE_serialize_melt_data_ (const struct MeltData *md,
+                                     size_t *res_size);
+
+
+/**
+ * Deserialize melt data.
+ *
+ * @param buf serialized data
+ * @param buf_size size of @a buf
+ * @return deserialized melt data, NULL on error
+ */
+struct MeltData *
+TALER_EXCHANGE_deserialize_melt_data_ (const char *buf,
+                                       size_t buf_size);
+
+
+/**
+ * Free all information associated with a melting session.  Note
+ * that we allow the melting session to be only partially initialized,
+ * as we use this function also when freeing melt data that was not
+ * fully initialized (i.e. due to failures in #deserialize_melt_data()).
+ *
+ * @param md melting data to release, the pointer itself is NOT
+ *           freed (as it is typically not allocated by itself)
+ */
+void
+TALER_EXCHANGE_free_melt_data_ (struct MeltData *md);
+
+#endif
diff --git a/src/lib/exchange_api_refreshes_reveal.c 
b/src/lib/exchange_api_refreshes_reveal.c
new file mode 100644
index 00000000..20e19673
--- /dev/null
+++ b/src/lib/exchange_api_refreshes_reveal.c
@@ -0,0 +1,512 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_refreshes_reveal.c
+ * @brief Implementation of the /refreshes/$RCH/reveal requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+#include "exchange_api_refresh_common.h"
+
+
+/**
+ * @brief A /refreshes/$RCH/reveal Handle
+ */
+struct TALER_EXCHANGE_RefreshesRevealHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext ctx;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_RefreshesRevealCallback reveal_cb;
+
+  /**
+   * Closure for @e reveal_cb.
+   */
+  void *reveal_cb_cls;
+
+  /**
+   * Actual information about the melt operation.
+   */
+  struct MeltData *md;
+
+  /**
+   * The index selected by the exchange in cut-and-choose to not be revealed.
+   */
+  uint16_t noreveal_index;
+
+};
+
+
+/**
+ * We got a 200 OK response for the /refreshes/$RCH/reveal operation.
+ * Extract the coin signatures and return them to the caller.
+ * The signatures we get from the exchange is for the blinded value.
+ * Thus, we first must unblind them and then should verify their
+ * validity.
+ *
+ * If everything checks out, we return the unblinded signatures
+ * to the application via the callback.
+ *
+ * @param rrh operation handle
+ * @param json reply from the exchange
+ * @param[out] sigs array of length `num_fresh_coins`, initialized to cointain 
RSA signatures
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
+                   const json_t *json,
+                   struct TALER_DenominationSignature *sigs)
+{
+  json_t *jsona;
+  struct GNUNET_JSON_Specification outer_spec[] = {
+    GNUNET_JSON_spec_json ("ev_sigs", &jsona),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         outer_spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (! json_is_array (jsona))
+  {
+    /* We expected an array of coins */
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (outer_spec);
+    return GNUNET_SYSERR;
+  }
+  if (rrh->md->num_fresh_coins != json_array_size (jsona))
+  {
+    /* Number of coins generated does not match our expectation */
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (outer_spec);
+    return GNUNET_SYSERR;
+  }
+  for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
+  {
+    const struct TALER_PlanchetSecretsP *fc;
+    struct TALER_DenominationPublicKey *pk;
+    json_t *jsonai;
+    struct GNUNET_CRYPTO_RsaSignature *blind_sig;
+    struct TALER_CoinSpendPublicKeyP coin_pub;
+    struct GNUNET_HashCode coin_hash;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
+      GNUNET_JSON_spec_end ()
+    };
+    struct TALER_FreshCoin coin;
+
+    fc = &rrh->md->fresh_coins[rrh->noreveal_index][i];
+    pk = &rrh->md->fresh_pks[i];
+    jsonai = json_array_get (jsona, i);
+    GNUNET_assert (NULL != jsonai);
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (jsonai,
+                           spec,
+                           NULL, NULL))
+    {
+      GNUNET_break_op (0);
+      GNUNET_JSON_parse_free (outer_spec);
+      return GNUNET_SYSERR;
+    }
+
+    /* needed to verify the signature, and we didn't store it earlier,
+       hence recomputing it here... */
+    GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
+                                        &coin_pub.eddsa_pub);
+    GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub,
+                        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+                        &coin_hash);
+    if (GNUNET_OK !=
+        TALER_planchet_to_coin (pk,
+                                blind_sig,
+                                fc,
+                                &coin_hash,
+                                &coin))
+    {
+      GNUNET_break_op (0);
+      GNUNET_CRYPTO_rsa_signature_free (blind_sig);
+      GNUNET_JSON_parse_free (outer_spec);
+      return GNUNET_SYSERR;
+    }
+    GNUNET_CRYPTO_rsa_signature_free (blind_sig);
+    sigs[i] = coin.sig;
+  }
+  GNUNET_JSON_parse_free (outer_spec);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /refreshes/$RCH/reveal request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_RefreshHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_refresh_reveal_finished (void *cls,
+                                long response_code,
+                                const void *response)
+{
+  struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls;
+  const json_t *j = response;
+
+  rrh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    {
+      struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins];
+      int ret;
+
+      memset (sigs, 0, sizeof (sigs));
+      ret = refresh_reveal_ok (rrh,
+                               j,
+                               sigs);
+      if (GNUNET_OK != ret)
+      {
+        response_code = 0;
+      }
+      else
+      {
+        rrh->reveal_cb (rrh->reveal_cb_cls,
+                        MHD_HTTP_OK,
+                        TALER_EC_NONE,
+                        rrh->md->num_fresh_coins,
+                        rrh->md->fresh_coins[rrh->noreveal_index],
+                        sigs,
+                        j);
+        rrh->reveal_cb = NULL;
+      }
+      for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
+        if (NULL != sigs[i].rsa_signature)
+          GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_CONFLICT:
+    /* Nothing really to verify, exchange says our reveal is inconsitent
+       with our commitment, so either side is buggy; we
+       should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != rrh->reveal_cb)
+    rrh->reveal_cb (rrh->reveal_cb_cls,
+                    response_code,
+                    TALER_JSON_get_error_code (j),
+                    0,
+                    NULL,
+                    NULL,
+                    j);
+  TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
+}
+
+
+/**
+ * Submit a /refresh/reval request to the exchange and get the exchange's
+ * response.
+ *
+ * This API is typically used by a wallet.  Note that to ensure that
+ * no money is lost in case of hardware failures, the provided
+ * arguments should have been committed to persistent storage
+ * prior to calling this function.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param refresh_data_length size of the @a refresh_data (returned
+ *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
+ * @param refresh_data the refresh data as returned from
+          #TALER_EXCHANGE_refresh_prepare())
+ * @param noreveal_index response from the exchange to the
+ *        #TALER_EXCHANGE_melt() invocation
+ * @param reveal_cb the callback to call with the final result of the
+ *        refresh operation
+ * @param reveal_cb_cls closure for the above callback
+ * @return a handle for this request; NULL if the argument was invalid.
+ *         In this case, neither callback will be called.
+ */
+struct TALER_EXCHANGE_RefreshesRevealHandle *
+TALER_EXCHANGE_refreshes_reveal (struct TALER_EXCHANGE_Handle *exchange,
+                                 size_t refresh_data_length,
+                                 const char *refresh_data,
+                                 uint32_t noreveal_index,
+                                 TALER_EXCHANGE_RefreshesRevealCallback
+                                 reveal_cb,
+                                 void *reveal_cb_cls)
+{
+  struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
+  json_t *transfer_privs;
+  json_t *new_denoms_h;
+  json_t *coin_evs;
+  json_t *reveal_obj;
+  json_t *link_sigs;
+  CURL *eh;
+  struct GNUNET_CURL_Context *ctx;
+  struct MeltData *md;
+  struct TALER_TransferPublicKeyP transfer_pub;
+  char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32];
+
+  if (noreveal_index >= TALER_CNC_KAPPA)
+  {
+    /* We check this here, as it would be really bad to below just
+       disclose all the transfer keys. Note that this error should
+       have been caught way earlier when the exchange replied, but maybe
+       we had some internal corruption that changed the value... */
+    GNUNET_break (0);
+    return NULL;
+  }
+  if (GNUNET_YES !=
+      TEAH_handle_is_ready (exchange))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
+                                              refresh_data_length);
+  if (NULL == md)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  /* now transfer_pub */
+  GNUNET_CRYPTO_ecdhe_key_get_public (
+    &md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv,
+    &transfer_pub.ecdhe_pub);
+
+  /* now new_denoms */
+  GNUNET_assert (NULL != (new_denoms_h = json_array ()));
+  GNUNET_assert (NULL != (coin_evs = json_array ()));
+  GNUNET_assert (NULL != (link_sigs = json_array ()));
+  for (unsigned int i = 0; i<md->num_fresh_coins; i++)
+  {
+    struct GNUNET_HashCode denom_hash;
+    struct TALER_PlanchetDetail pd;
+
+    GNUNET_CRYPTO_rsa_public_key_hash (md->fresh_pks[i].rsa_public_key,
+                                       &denom_hash);
+    GNUNET_assert (0 ==
+                   json_array_append_new (new_denoms_h,
+                                          GNUNET_JSON_from_data_auto (
+                                            &denom_hash)));
+
+    if (GNUNET_OK !=
+        TALER_planchet_prepare (&md->fresh_pks[i],
+                                &md->fresh_coins[noreveal_index][i],
+                                &pd))
+    {
+      /* This should have been noticed during the preparation stage. */
+      GNUNET_break (0);
+      json_decref (new_denoms_h);
+      json_decref (coin_evs);
+      return NULL;
+    }
+    GNUNET_assert (0 ==
+                   json_array_append_new (coin_evs,
+                                          GNUNET_JSON_from_data (pd.coin_ev,
+                                                                 
pd.coin_ev_size)));
+
+    /* compute link signature */
+    {
+      struct TALER_CoinSpendSignatureP link_sig;
+      struct TALER_LinkDataPS ldp;
+
+      ldp.purpose.size = htonl (sizeof (ldp));
+      ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
+      ldp.h_denom_pub = denom_hash;
+      GNUNET_CRYPTO_eddsa_key_get_public 
(&md->melted_coin.coin_priv.eddsa_priv,
+                                          &ldp.old_coin_pub.eddsa_pub);
+      ldp.transfer_pub = transfer_pub;
+      GNUNET_CRYPTO_hash (pd.coin_ev,
+                          pd.coin_ev_size,
+                          &ldp.coin_envelope_hash);
+      GNUNET_assert (GNUNET_OK ==
+                     GNUNET_CRYPTO_eddsa_sign (
+                       &md->melted_coin.coin_priv.eddsa_priv,
+                       &ldp.purpose,
+                       &link_sig.eddsa_signature));
+      GNUNET_assert (0 ==
+                     json_array_append_new (link_sigs,
+                                            GNUNET_JSON_from_data_auto (
+                                              &link_sig)));
+    }
+
+    GNUNET_free (pd.coin_ev);
+  }
+
+  /* build array of transfer private keys */
+  GNUNET_assert (NULL != (transfer_privs = json_array ()));
+  for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++)
+  {
+    if (j == noreveal_index)
+    {
+      /* This is crucial: exclude the transfer key for the
+   noreval index! */
+      continue;
+    }
+    GNUNET_assert (0 ==
+                   json_array_append_new (transfer_privs,
+                                          GNUNET_JSON_from_data_auto (
+                                            
&md->melted_coin.transfer_priv[j])));
+  }
+
+  /* build main JSON request */
+  reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}",
+                          "transfer_pub",
+                          GNUNET_JSON_from_data_auto (&transfer_pub),
+                          "transfer_privs",
+                          transfer_privs,
+                          "link_sigs",
+                          link_sigs,
+                          "new_denoms_h",
+                          new_denoms_h,
+                          "coin_evs",
+                          coin_evs);
+  if (NULL == reveal_obj)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  {
+    char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (&md->rc,
+                                         sizeof (struct
+                                                 TALER_RefreshCommitmentP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/refreshes/%s/reveal",
+                     pub_str);
+  }
+  /* finally, we can actually issue the request */
+  rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle);
+  rrh->exchange = exchange;
+  rrh->noreveal_index = noreveal_index;
+  rrh->reveal_cb = reveal_cb;
+  rrh->reveal_cb_cls = reveal_cb_cls;
+  rrh->md = md;
+  rrh->url = TEAH_path_to_url (rrh->exchange,
+                               arg_str);
+
+  eh = TALER_EXCHANGE_curl_easy_get_ (rrh->url);
+  if (GNUNET_OK !=
+      TALER_curl_easy_post (&rrh->ctx,
+                            eh,
+                            reveal_obj))
+  {
+    GNUNET_break (0);
+    curl_easy_cleanup (eh);
+    json_decref (reveal_obj);
+    GNUNET_free (rrh->url);
+    GNUNET_free (rrh);
+    return NULL;
+  }
+  json_decref (reveal_obj);
+  ctx = TEAH_handle_to_context (rrh->exchange);
+  rrh->job = GNUNET_CURL_job_add2 (ctx,
+                                   eh,
+                                   rrh->ctx.headers,
+                                   &handle_refresh_reveal_finished,
+                                   rrh);
+  return rrh;
+}
+
+
+/**
+ * Cancel a refresh reveal request.  This function cannot be used
+ * on a request handle if the callback was already invoked.
+ *
+ * @param rrh the refresh reval handle
+ */
+void
+TALER_EXCHANGE_refreshes_reveal_cancel (struct
+                                        TALER_EXCHANGE_RefreshesRevealHandle *
+                                        rrh)
+{
+  if (NULL != rrh->job)
+  {
+    GNUNET_CURL_job_cancel (rrh->job);
+    rrh->job = NULL;
+  }
+  GNUNET_free (rrh->url);
+  TALER_curl_easy_post_finished (&rrh->ctx);
+  TALER_EXCHANGE_free_melt_data_ (rrh->md); /* does not free 'md' itself */
+  GNUNET_free (rrh->md);
+  GNUNET_free (rrh);
+}
+
+
+/* exchange_api_refreshes_reveal.c */
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index ef9a4359..8c50c80b 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -62,7 +62,7 @@ struct TALER_EXCHANGE_RefundHandle
   /**
    * Function to call with the result.
    */
-  TALER_EXCHANGE_RefundResultCallback cb;
+  TALER_EXCHANGE_RefundCallback cb;
 
   /**
    * Closure for @a cb.
@@ -250,7 +250,7 @@ TALER_EXCHANGE_refund (struct TALER_EXCHANGE_Handle 
*exchange,
                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
                        uint64_t rtransaction_id,
                        const struct TALER_MerchantPrivateKeyP *merchant_priv,
-                       TALER_EXCHANGE_RefundResultCallback cb,
+                       TALER_EXCHANGE_RefundCallback cb,
                        void *cb_cls)
 {
   struct TALER_RefundRequestPS rr;
@@ -326,23 +326,38 @@ TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle 
*exchange,
                         uint64_t rtransaction_id,
                         const struct TALER_MerchantPublicKeyP *merchant_pub,
                         const struct TALER_MerchantSignatureP *merchant_sig,
-                        TALER_EXCHANGE_RefundResultCallback cb,
+                        TALER_EXCHANGE_RefundCallback cb,
                         void *cb_cls)
 {
   struct TALER_EXCHANGE_RefundHandle *rh;
   struct GNUNET_CURL_Context *ctx;
   json_t *refund_obj;
   CURL *eh;
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
 
+  {
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/coins/%s/refund",
+                     pub_str);
+  }
   refund_obj = json_pack ("{s:o, s:o," /* amount/fee */
-                          " s:o, s:o," /* h_contract_terms, coin_pub */
+                          " s:o," /* h_contract_terms */
                           " s:I," /* rtransaction id */
                           " s:o, s:o}", /* merchant_pub, merchant_sig */
                           "refund_amount", TALER_JSON_from_amount (amount),
                           "refund_fee", TALER_JSON_from_amount (refund_fee),
                           "h_contract_terms", GNUNET_JSON_from_data_auto (
                             h_contract_terms),
-                          "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
                           "rtransaction_id", (json_int_t) rtransaction_id,
                           "merchant_pub", GNUNET_JSON_from_data_auto (
                             merchant_pub),
@@ -359,7 +374,8 @@ TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle 
*exchange,
   rh->exchange = exchange;
   rh->cb = cb;
   rh->cb_cls = cb_cls;
-  rh->url = TEAH_path_to_url (exchange, "/refund");
+  rh->url = TEAH_path_to_url (exchange,
+                              arg_str);
   rh->depconf.purpose.size = htonl (sizeof (struct 
TALER_RefundConfirmationPS));
   rh->depconf.purpose.purpose = htonl 
(TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
   rh->depconf.h_contract_terms = *h_contract_terms;
@@ -371,7 +387,7 @@ TALER_EXCHANGE_refund2 (struct TALER_EXCHANGE_Handle 
*exchange,
   TALER_amount_hton (&rh->depconf.refund_fee,
                      refund_fee);
 
-  eh = TEL_curl_easy_get (rh->url);
+  eh = TALER_EXCHANGE_curl_easy_get_ (rh->url);
   if (GNUNET_OK !=
       TALER_curl_easy_post (&rh->ctx,
                             eh,
diff --git a/src/lib/exchange_api_reserve.c b/src/lib/exchange_api_reserve.c
deleted file mode 100644
index 710cd588..00000000
--- a/src/lib/exchange_api_reserve.c
+++ /dev/null
@@ -1,1288 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015 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 lib/exchange_api_reserve.c
- * @brief Implementation of the /reserve requests of the exchange's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_exchange_service.h"
-#include "taler_json_lib.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/* ********************** /reserve/status ********************** */
-
-/**
- * @brief A Withdraw Status Handle
- */
-struct TALER_EXCHANGE_ReserveStatusHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_ReserveStatusResultCallback cb;
-
-  /**
-   * Public key of the reserve we are querying.
-   */
-  struct TALER_ReservePublicKeyP reserve_pub;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-};
-
-
-/**
- * Parse history given in JSON format and return it in binary
- * format.
- *
- * @param exchange connection to the exchange we can use
- * @param history JSON array with the history
- * @param reserve_pub public key of the reserve to inspect
- * @param currency currency we expect the balance to be in
- * @param[out] balance final balance
- * @param history_length number of entries in @a history
- * @param[out] rhistory array of length @a history_length, set to the
- *             parsed history entries
- * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
- *         were set,
- *         #GNUNET_SYSERR if there was a protocol violation in @a history
- */
-static int
-parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
-                       const json_t *history,
-                       const struct TALER_ReservePublicKeyP *reserve_pub,
-                       const char *currency,
-                       struct TALER_Amount *balance,
-                       unsigned int history_length,
-                       struct TALER_EXCHANGE_ReserveHistory *rhistory)
-{
-  struct GNUNET_HashCode uuid[history_length];
-  unsigned int uuid_off;
-  struct TALER_Amount total_in;
-  struct TALER_Amount total_out;
-  size_t off;
-
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
-                                        &total_in));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
-                                        &total_out));
-  uuid_off = 0;
-  for (off = 0; off<history_length; off++)
-  {
-    json_t *transaction;
-    struct TALER_Amount amount;
-    const char *type;
-    struct GNUNET_JSON_Specification hist_spec[] = {
-      GNUNET_JSON_spec_string ("type", &type),
-      TALER_JSON_spec_amount ("amount",
-                              &amount),
-      /* 'wire' and 'signature' are optional depending on 'type'! */
-      GNUNET_JSON_spec_end ()
-    };
-
-    transaction = json_array_get (history,
-                                  off);
-    if (GNUNET_OK !=
-        GNUNET_JSON_parse (transaction,
-                           hist_spec,
-                           NULL, NULL))
-    {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-    rhistory[off].amount = amount;
-
-    if (0 == strcasecmp (type,
-                         "DEPOSIT"))
-    {
-      const char *wire_url;
-      void *wire_reference;
-      size_t wire_reference_size;
-      struct GNUNET_TIME_Absolute timestamp;
-
-      struct GNUNET_JSON_Specification withdraw_spec[] = {
-        GNUNET_JSON_spec_varsize ("wire_reference",
-                                  &wire_reference,
-                                  &wire_reference_size),
-        GNUNET_JSON_spec_absolute_time ("timestamp",
-                                        &timestamp),
-        GNUNET_JSON_spec_string ("sender_account_url",
-                                 &wire_url),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_in,
-                            &total_in,
-                            &amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             withdraw_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url);
-      rhistory[off].details.in_details.wire_reference = wire_reference;
-      rhistory[off].details.in_details.wire_reference_size =
-        wire_reference_size;
-      rhistory[off].details.in_details.timestamp = timestamp;
-      /* end type==DEPOSIT */
-    }
-    else if (0 == strcasecmp (type,
-                              "WITHDRAW"))
-    {
-      struct TALER_ReserveSignatureP sig;
-      struct TALER_WithdrawRequestPS withdraw_purpose;
-      struct GNUNET_JSON_Specification withdraw_spec[] = {
-        GNUNET_JSON_spec_fixed_auto ("reserve_sig",
-                                     &sig),
-        TALER_JSON_spec_amount_nbo ("withdraw_fee",
-                                    &withdraw_purpose.withdraw_fee),
-        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                     &withdraw_purpose.h_denomination_pub),
-        GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
-                                     &withdraw_purpose.h_coin_envelope),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_WITHDRAWAL;
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             withdraw_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      withdraw_purpose.purpose.size
-        = htonl (sizeof (withdraw_purpose));
-      withdraw_purpose.purpose.purpose
-        = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
-      withdraw_purpose.reserve_pub = *reserve_pub;
-      TALER_amount_hton (&withdraw_purpose.amount_with_fee,
-                         &amount);
-      /* Check that the signature is a valid withdraw request */
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
-                                      &withdraw_purpose.purpose,
-                                      &sig.eddsa_signature,
-                                      &reserve_pub->eddsa_pub))
-      {
-        GNUNET_break_op (0);
-        GNUNET_JSON_parse_free (withdraw_spec);
-        return GNUNET_SYSERR;
-      }
-      /* check that withdraw fee matches expectations! */
-      {
-        const struct TALER_EXCHANGE_Keys *key_state;
-        const struct TALER_EXCHANGE_DenomPublicKey *dki;
-        struct TALER_Amount fee;
-
-        key_state = TALER_EXCHANGE_get_keys (exchange);
-        dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
-                                                           &withdraw_purpose.
-                                                           h_denomination_pub);
-        TALER_amount_ntoh (&fee,
-                           &withdraw_purpose.withdraw_fee);
-        if ( (GNUNET_YES !=
-              TALER_amount_cmp_currency (&fee,
-                                         &dki->fee_withdraw)) ||
-             (0 !=
-              TALER_amount_cmp (&fee,
-                                &dki->fee_withdraw)) )
-        {
-          GNUNET_break_op (0);
-          GNUNET_JSON_parse_free (withdraw_spec);
-          return GNUNET_SYSERR;
-        }
-      }
-      rhistory[off].details.out_authorization_sig
-        = json_object_get (transaction,
-                           "signature");
-      /* Check check that the same withdraw transaction
-         isn't listed twice by the exchange. We use the
-         "uuid" array to remember the hashes of all
-         purposes, and compare the hashes to find
-         duplicates. *///
-      GNUNET_CRYPTO_hash (&withdraw_purpose,
-                          ntohl (withdraw_purpose.purpose.size),
-                          &uuid[uuid_off]);
-      for (unsigned int i = 0; i<uuid_off; i++)
-      {
-        if (0 == GNUNET_memcmp (&uuid[uuid_off],
-                                &uuid[i]))
-        {
-          GNUNET_break_op (0);
-          GNUNET_JSON_parse_free (withdraw_spec);
-          return GNUNET_SYSERR;
-        }
-      }
-      uuid_off++;
-
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_out,
-                            &total_out,
-                            &amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        GNUNET_JSON_parse_free (withdraw_spec);
-        return GNUNET_SYSERR;
-      }
-      /* end type==WITHDRAW */
-    }
-    else if (0 == strcasecmp (type,
-                              "RECOUP"))
-    {
-      struct TALER_RecoupConfirmationPS pc;
-      struct GNUNET_TIME_Absolute timestamp;
-      const struct TALER_EXCHANGE_Keys *key_state;
-      struct GNUNET_JSON_Specification recoup_spec[] = {
-        GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                     &pc.coin_pub),
-        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                     &rhistory[off].details.recoup_details.
-                                     exchange_sig),
-        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                     &rhistory[off].details.recoup_details.
-                                     exchange_pub),
-        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
-                                            &pc.timestamp),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_RECOUP;
-      rhistory[off].amount = amount;
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             recoup_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      rhistory[off].details.recoup_details.coin_pub = pc.coin_pub;
-      TALER_amount_hton (&pc.recoup_amount,
-                         &amount);
-      pc.purpose.size = htonl (sizeof (pc));
-      pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP);
-      pc.reserve_pub = *reserve_pub;
-      timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
-      rhistory[off].details.recoup_details.timestamp = timestamp;
-
-      key_state = TALER_EXCHANGE_get_keys (exchange);
-      if (GNUNET_OK !=
-          TALER_EXCHANGE_test_signing_key (key_state,
-                                           &rhistory[off].details.
-                                           recoup_details.exchange_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
-                                      &pc.purpose,
-                                      &rhistory[off].details.recoup_details.
-                                      exchange_sig.eddsa_signature,
-                                      &rhistory[off].details.recoup_details.
-                                      exchange_pub.eddsa_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_in,
-                            &total_in,
-                            &rhistory[off].amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      /* end type==RECOUP */
-    }
-    else if (0 == strcasecmp (type,
-                              "CLOSING"))
-    {
-      const struct TALER_EXCHANGE_Keys *key_state;
-      struct TALER_ReserveCloseConfirmationPS rcc;
-      struct GNUNET_TIME_Absolute timestamp;
-      struct GNUNET_JSON_Specification closing_spec[] = {
-        GNUNET_JSON_spec_string ("receiver_account_details",
-                                 &rhistory[off].details.close_details.
-                                 receiver_account_details),
-        GNUNET_JSON_spec_fixed_auto ("wtid",
-                                     
&rhistory[off].details.close_details.wtid),
-        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                     &rhistory[off].details.close_details.
-                                     exchange_sig),
-        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                     &rhistory[off].details.close_details.
-                                     exchange_pub),
-        TALER_JSON_spec_amount_nbo ("closing_fee",
-                                    &rcc.closing_fee),
-        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
-                                            &rcc.timestamp),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_CLOSE;
-      rhistory[off].amount = amount;
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             closing_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      TALER_amount_hton (&rcc.closing_amount,
-                         &amount);
-      GNUNET_CRYPTO_hash (
-        rhistory[off].details.close_details.receiver_account_details,
-        strlen (
-          rhistory[off].details.close_details.receiver_account_details) + 1,
-        &rcc.h_wire);
-      rcc.wtid = rhistory[off].details.close_details.wtid;
-      rcc.purpose.size = htonl (sizeof (rcc));
-      rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
-      rcc.reserve_pub = *reserve_pub;
-      timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
-      rhistory[off].details.close_details.timestamp = timestamp;
-
-      key_state = TALER_EXCHANGE_get_keys (exchange);
-      if (GNUNET_OK !=
-          TALER_EXCHANGE_test_signing_key (key_state,
-                                           
&rhistory[off].details.close_details.
-                                           exchange_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
-                                      &rcc.purpose,
-                                      &rhistory[off].details.close_details.
-                                      exchange_sig.eddsa_signature,
-                                      &rhistory[off].details.close_details.
-                                      exchange_pub.eddsa_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_out,
-                            &total_out,
-                            &rhistory[off].amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      /* end type==CLOSING */
-    }
-    else
-    {
-      /* unexpected 'type', protocol incompatibility, complain! */
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-  }
-
-  /* check balance = total_in - total_out < withdraw-amount */
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (balance,
-                             &total_in,
-                             &total_out))
-  {
-    /* total_in < total_out, why did the exchange ever allow this!? */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Free memory (potentially) allocated by #parse_reserve_history().
- *
- * @param rhistory result to free
- * @param len number of entries in @a rhistory
- */
-static void
-free_rhistory (struct TALER_EXCHANGE_ReserveHistory *rhistory,
-               unsigned int len)
-{
-  for (unsigned int i = 0; i<len; i++)
-  {
-    switch (rhistory[i].type)
-    {
-    case TALER_EXCHANGE_RTT_DEPOSIT:
-      GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
-      GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
-      break;
-    case TALER_EXCHANGE_RTT_WITHDRAWAL:
-      break;
-    case TALER_EXCHANGE_RTT_RECOUP:
-      break;
-    case TALER_EXCHANGE_RTT_CLOSE:
-      // should we free "receiver_account_details" ?
-      break;
-    }
-  }
-  GNUNET_free (rhistory);
-}
-
-
-/**
- * We received an #MHD_HTTP_OK status code. Handle the JSON
- * response.
- *
- * @param rsh handle of the request
- * @param j JSON response
- * @return #GNUNET_OK on success
- */
-static int
-handle_reserve_status_ok (struct TALER_EXCHANGE_ReserveStatusHandle *rsh,
-                          const json_t *j)
-{
-  json_t *history;
-  unsigned int len;
-  struct TALER_Amount balance;
-  struct TALER_Amount balance_from_history;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("balance", &balance),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (j,
-                         spec,
-                         NULL,
-                         NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  history = json_object_get (j,
-                             "history");
-  if (NULL == history)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  len = json_array_size (history);
-  {
-    struct TALER_EXCHANGE_ReserveHistory *rhistory;
-
-    rhistory = GNUNET_new_array (len,
-                                 struct TALER_EXCHANGE_ReserveHistory);
-    if (GNUNET_OK !=
-        parse_reserve_history (rsh->exchange,
-                               history,
-                               &rsh->reserve_pub,
-                               balance.currency,
-                               &balance_from_history,
-                               len,
-                               rhistory))
-    {
-      GNUNET_break_op (0);
-      free_rhistory (rhistory,
-                     len);
-      return GNUNET_SYSERR;
-    }
-    if (0 !=
-        TALER_amount_cmp (&balance_from_history,
-                          &balance))
-    {
-      /* exchange cannot add up balances!? */
-      GNUNET_break_op (0);
-      free_rhistory (rhistory,
-                     len);
-      return GNUNET_SYSERR;
-    }
-    if (NULL != rsh->cb)
-    {
-      rsh->cb (rsh->cb_cls,
-               MHD_HTTP_OK,
-               TALER_EC_NONE,
-               j,
-               &balance,
-               len,
-               rhistory);
-      rsh->cb = NULL;
-    }
-    free_rhistory (rhistory,
-                   len);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserve/status request.
- *
- * @param cls the `struct TALER_EXCHANGE_ReserveStatusHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserve_status_finished (void *cls,
-                                long response_code,
-                                const void *response)
-{
-  struct TALER_EXCHANGE_ReserveStatusHandle *rsh = cls;
-  const json_t *j = response;
-
-  rsh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        handle_reserve_status_ok (rsh,
-                                  j))
-      response_code = 0;
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, this should never
-       happen, we should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != rsh->cb)
-  {
-    rsh->cb (rsh->cb_cls,
-             response_code,
-             TALER_JSON_get_error_code (j),
-             j,
-             NULL,
-             0, NULL);
-    rsh->cb = NULL;
-  }
-  TALER_EXCHANGE_reserve_status_cancel (rsh);
-}
-
-
-/**
- * Submit a request to obtain the transaction history of a reserve
- * from the exchange.  Note that while we return the full response to the
- * caller for further processing, we do already verify that the
- * response is well-formed (i.e. that signatures included in the
- * response are all valid and add up to the balance).  If the exchange's
- * reply is not well-formed, we return an HTTP status code of zero to
- * @a cb.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param reserve_pub public key of the reserve to inspect
- * @param cb the callback to call when a reply for this request is available
- * @param cb_cls closure for the above callback
- * @return a handle for this request; NULL if the inputs are invalid (i.e.
- *         signatures fail to verify).  In this case, the callback is not 
called.
- */
-struct TALER_EXCHANGE_ReserveStatusHandle *
-TALER_EXCHANGE_reserve_status (struct TALER_EXCHANGE_Handle *exchange,
-                               const struct
-                               TALER_ReservePublicKeyP *reserve_pub,
-                               TALER_EXCHANGE_ReserveStatusResultCallback cb,
-                               void *cb_cls)
-{
-  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
-  struct GNUNET_CURL_Context *ctx;
-  CURL *eh;
-  char *pub_str;
-  char *arg_str;
-
-  if (GNUNET_YES !=
-      TEAH_handle_is_ready (exchange))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  pub_str = GNUNET_STRINGS_data_to_string_alloc (reserve_pub,
-                                                 sizeof (struct
-                                                         
TALER_ReservePublicKeyP));
-  GNUNET_asprintf (&arg_str,
-                   "/reserve/status?reserve_pub=%s",
-                   pub_str);
-  GNUNET_free (pub_str);
-  rsh = GNUNET_new (struct TALER_EXCHANGE_ReserveStatusHandle);
-  rsh->exchange = exchange;
-  rsh->cb = cb;
-  rsh->cb_cls = cb_cls;
-  rsh->reserve_pub = *reserve_pub;
-  rsh->url = TEAH_path_to_url (exchange,
-                               arg_str);
-  GNUNET_free (arg_str);
-
-  eh = TEL_curl_easy_get (rsh->url);
-  ctx = TEAH_handle_to_context (exchange);
-  rsh->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_NO,
-                                  &handle_reserve_status_finished,
-                                  rsh);
-  return rsh;
-}
-
-
-/**
- * Cancel a reserve status request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param rsh the reserve status request handle
- */
-void
-TALER_EXCHANGE_reserve_status_cancel (struct
-                                      TALER_EXCHANGE_ReserveStatusHandle *rsh)
-{
-  if (NULL != rsh->job)
-  {
-    GNUNET_CURL_job_cancel (rsh->job);
-    rsh->job = NULL;
-  }
-  GNUNET_free (rsh->url);
-  GNUNET_free (rsh);
-}
-
-
-/* ********************** /reserve/withdraw ********************** */
-
-/**
- * @brief A Withdraw Sign Handle
- */
-struct TALER_EXCHANGE_ReserveWithdrawHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext ctx;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_ReserveWithdrawResultCallback cb;
-
-  /**
-   * Secrets of the planchet.
-   */
-  struct TALER_PlanchetSecretsP ps;
-
-  /**
-   * Denomination key we are withdrawing.
-   */
-  struct TALER_EXCHANGE_DenomPublicKey pk;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Hash of the public key of the coin we are signing.
-   */
-  struct GNUNET_HashCode c_hash;
-
-  /**
-   * Public key of the reserve we are withdrawing from.
-   */
-  struct TALER_ReservePublicKeyP reserve_pub;
-
-};
-
-
-/**
- * We got a 200 OK response for the /reserve/withdraw operation.
- * Extract the coin's signature and return it to the caller.
- * The signature we get from the exchange is for the blinded value.
- * Thus, we first must unblind it and then should verify its
- * validity against our coin's hash.
- *
- * If everything checks out, we return the unblinded signature
- * to the application via the callback.
- *
- * @param wsh operation handle
- * @param json reply from the exchange
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static int
-reserve_withdraw_ok (struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh,
-                     const json_t *json)
-{
-  struct GNUNET_CRYPTO_RsaSignature *blind_sig;
-  struct TALER_FreshCoin fc;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_rsa_signature ("ev_sig",
-                                    &blind_sig),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_planchet_to_coin (&wsh->pk.key,
-                              blind_sig,
-                              &wsh->ps,
-                              &wsh->c_hash,
-                              &fc))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_JSON_parse_free (spec);
-
-  /* signature is valid, return it to the application */
-  wsh->cb (wsh->cb_cls,
-           MHD_HTTP_OK,
-           TALER_EC_NONE,
-           &fc.sig,
-           json);
-  /* make sure callback isn't called again after return */
-  wsh->cb = NULL;
-  GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
-  return GNUNET_OK;
-}
-
-
-/**
- * We got a 409 CONFLICT response for the /reserve/withdraw operation.
- * Check the signatures on the withdraw transactions in the provided
- * history and that the balances add up.  We don't do anything directly
- * with the information, as the JSON will be returned to the application.
- * However, our job is ensuring that the exchange followed the protocol, and
- * this in particular means checking all of the signatures in the history.
- *
- * @param wsh operation handle
- * @param json reply from the exchange
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static int
-reserve_withdraw_payment_required (struct
-                                   TALER_EXCHANGE_ReserveWithdrawHandle *wsh,
-                                   const json_t *json)
-{
-  struct TALER_Amount balance;
-  struct TALER_Amount balance_from_history;
-  struct TALER_Amount requested_amount;
-  json_t *history;
-  size_t len;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("balance", &balance),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  history = json_object_get (json,
-                             "history");
-  if (NULL == history)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* go over transaction history and compute
-     total incoming and outgoing amounts */
-  len = json_array_size (history);
-  {
-    struct TALER_EXCHANGE_ReserveHistory *rhistory;
-
-    /* Use heap allocation as "len" may be very big and thus this may
-       not fit on the stack. Use "GNUNET_malloc_large" as a malicious
-       exchange may theoretically try to crash us by giving a history
-       that does not fit into our memory. */
-    rhistory = GNUNET_malloc_large (sizeof (struct
-                                            TALER_EXCHANGE_ReserveHistory)
-                                    * len);
-    if (NULL == rhistory)
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-
-    if (GNUNET_OK !=
-        parse_reserve_history (wsh->exchange,
-                               history,
-                               &wsh->reserve_pub,
-                               balance.currency,
-                               &balance_from_history,
-                               len,
-                               rhistory))
-    {
-      GNUNET_break_op (0);
-      free_rhistory (rhistory,
-                     len);
-      return GNUNET_SYSERR;
-    }
-    free_rhistory (rhistory,
-                   len);
-  }
-
-  if (0 !=
-      TALER_amount_cmp (&balance_from_history,
-                        &balance))
-  {
-    /* exchange cannot add up balances!? */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  /* Compute how much we expected to charge to the reserve */
-  if (GNUNET_OK !=
-      TALER_amount_add (&requested_amount,
-                        &wsh->pk.value,
-                        &wsh->pk.fee_withdraw))
-  {
-    /* Overflow here? Very strange, our CPU must be fried... */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  /* Check that funds were really insufficient */
-  if (0 >= TALER_amount_cmp (&requested_amount,
-                             &balance))
-  {
-    /* Requested amount is smaller or equal to reported balance,
-       so this should not have failed. */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserve/withdraw request.
- *
- * @param cls the `struct TALER_EXCHANGE_ReserveWithdrawHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserve_withdraw_finished (void *cls,
-                                  long response_code,
-                                  const void *response)
-{
-  struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh = cls;
-  const json_t *j = response;
-
-  wsh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        reserve_withdraw_ok (wsh,
-                             j))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_CONFLICT:
-    /* The exchange says that the reserve has insufficient funds;
-       check the signatures in the history... */
-    if (GNUNET_OK !=
-        reserve_withdraw_payment_required (wsh,
-                                           j))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    GNUNET_break (0);
-    /* Nothing really to verify, exchange says one of the signatures is
-       invalid; as we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, the exchange basically just says
-       that it doesn't know this reserve.  Can happen if we
-       query before the wire transfer went through.
-       We should simply pass the JSON reply to the application. */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != wsh->cb)
-  {
-    wsh->cb (wsh->cb_cls,
-             response_code,
-             TALER_JSON_get_error_code (j),
-             NULL,
-             j);
-    wsh->cb = NULL;
-  }
-  TALER_EXCHANGE_reserve_withdraw_cancel (wsh);
-}
-
-
-/**
- * Helper function for #TALER_EXCHANGE_reserve_withdraw2() and
- * #TALER_EXCHANGE_reserve_withdraw().
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param pk kind of coin to create
- * @param reserve_sig signature from the reserve authorizing the withdrawal
- * @param reserve_pub public key of the reserve to withdraw from
- * @param ps secrets of the planchet
- *        caller must have committed this value to disk before the call (with 
@a pk)
- * @param pd planchet details matching @a ps
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for @a res_cb
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_ReserveWithdrawHandle *
-reserve_withdraw_internal (struct TALER_EXCHANGE_Handle *exchange,
-                           const struct TALER_EXCHANGE_DenomPublicKey *pk,
-                           const struct TALER_ReserveSignatureP *reserve_sig,
-                           const struct TALER_ReservePublicKeyP *reserve_pub,
-                           const struct TALER_PlanchetSecretsP *ps,
-                           const struct TALER_PlanchetDetail *pd,
-                           TALER_EXCHANGE_ReserveWithdrawResultCallback res_cb,
-                           void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
-  struct GNUNET_CURL_Context *ctx;
-  json_t *withdraw_obj;
-  CURL *eh;
-  struct GNUNET_HashCode h_denom_pub;
-
-  wsh = GNUNET_new (struct TALER_EXCHANGE_ReserveWithdrawHandle);
-  wsh->exchange = exchange;
-  wsh->cb = res_cb;
-  wsh->cb_cls = res_cb_cls;
-  wsh->pk = *pk;
-  wsh->pk.key.rsa_public_key = GNUNET_CRYPTO_rsa_public_key_dup (
-    pk->key.rsa_public_key);
-  wsh->reserve_pub = *reserve_pub;
-  wsh->c_hash = pd->c_hash;
-  GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key,
-                                     &h_denom_pub);
-  withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub_hash and coin_ev */
-                            " s:o, s:o}",/* reserve_pub and reserve_sig */
-                            "denom_pub_hash", GNUNET_JSON_from_data_auto (
-                              &h_denom_pub),
-                            "coin_ev", GNUNET_JSON_from_data (pd->coin_ev,
-                                                              
pd->coin_ev_size),
-                            "reserve_pub", GNUNET_JSON_from_data_auto (
-                              reserve_pub),
-                            "reserve_sig", GNUNET_JSON_from_data_auto (
-                              reserve_sig));
-  if (NULL == withdraw_obj)
-  {
-    GNUNET_break (0);
-    GNUNET_CRYPTO_rsa_public_key_free (wsh->pk.key.rsa_public_key);
-    GNUNET_free (wsh);
-    return NULL;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Attempting to withdraw from reserve %s\n",
-              TALER_B2S (reserve_pub));
-
-  wsh->ps = *ps;
-  wsh->url = TEAH_path_to_url (exchange, "/reserve/withdraw");
-  eh = TEL_curl_easy_get (wsh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&wsh->ctx,
-                            eh,
-                            withdraw_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (withdraw_obj);
-    GNUNET_free (wsh->url);
-    GNUNET_CRYPTO_rsa_public_key_free (wsh->pk.key.rsa_public_key);
-    GNUNET_free (wsh);
-    return NULL;
-  }
-  json_decref (withdraw_obj);
-  ctx = TEAH_handle_to_context (exchange);
-  wsh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   wsh->ctx.headers,
-                                   &handle_reserve_withdraw_finished,
-                                   wsh);
-  return wsh;
-}
-
-
-/**
- * Withdraw a coin from the exchange using a /reserve/withdraw request.  Note
- * that to ensure that no money is lost in case of hardware failures,
- * the caller must have committed (most of) the arguments to disk
- * before calling, and be ready to repeat the request with the same
- * arguments in case of failures.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param pk kind of coin to create
- * @param reserve_priv private key of the reserve to withdraw from
- * @param ps secrets of the planchet
- *        caller must have committed this value to disk before the call (with 
@a pk)
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for the above callback
- * @return handle for the operation on success, NULL on error, i.e.
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_ReserveWithdrawHandle *
-TALER_EXCHANGE_reserve_withdraw (struct TALER_EXCHANGE_Handle *exchange,
-                                 const struct TALER_EXCHANGE_DenomPublicKey 
*pk,
-                                 const struct
-                                 TALER_ReservePrivateKeyP *reserve_priv,
-                                 const struct TALER_PlanchetSecretsP *ps,
-                                 TALER_EXCHANGE_ReserveWithdrawResultCallback
-                                 res_cb,
-                                 void *res_cb_cls)
-{
-  struct TALER_Amount amount_with_fee;
-  struct TALER_ReserveSignatureP reserve_sig;
-  struct TALER_WithdrawRequestPS req;
-  struct TALER_PlanchetDetail pd;
-  struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
-
-  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
-                                      &req.reserve_pub.eddsa_pub);
-  req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
-  req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
-  if (GNUNET_OK !=
-      TALER_amount_add (&amount_with_fee,
-                        &pk->fee_withdraw,
-                        &pk->value))
-  {
-    /* exchange gave us denomination keys that overflow like this!? */
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  TALER_amount_hton (&req.amount_with_fee,
-                     &amount_with_fee);
-  TALER_amount_hton (&req.withdraw_fee,
-                     &pk->fee_withdraw);
-  if (GNUNET_OK !=
-      TALER_planchet_prepare (&pk->key,
-                              ps,
-                              &pd))
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  req.h_denomination_pub = pd.denom_pub_hash;
-  GNUNET_CRYPTO_hash (pd.coin_ev,
-                      pd.coin_ev_size,
-                      &req.h_coin_envelope);
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
-                                           &req.purpose,
-                                           &reserve_sig.eddsa_signature));
-  wsh = reserve_withdraw_internal (exchange,
-                                   pk,
-                                   &reserve_sig,
-                                   &req.reserve_pub,
-                                   ps,
-                                   &pd,
-                                   res_cb,
-                                   res_cb_cls);
-  GNUNET_free (pd.coin_ev);
-  return wsh;
-}
-
-
-/**
- * Withdraw a coin from the exchange using a /reserve/withdraw
- * request.  This API is typically used by a wallet to withdraw a tip
- * where the reserve's signature was created by the merchant already.
- *
- * Note that to ensure that no money is lost in case of hardware
- * failures, the caller must have committed (most of) the arguments to
- * disk before calling, and be ready to repeat the request with the
- * same arguments in case of failures.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param pk kind of coin to create
- * @param reserve_sig signature from the reserve authorizing the withdrawal
- * @param reserve_pub public key of the reserve to withdraw from
- * @param ps secrets of the planchet
- *        caller must have committed this value to disk before the call (with 
@a pk)
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for @a res_cb
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_ReserveWithdrawHandle *
-TALER_EXCHANGE_reserve_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
-                                  const struct
-                                  TALER_EXCHANGE_DenomPublicKey *pk,
-                                  const struct
-                                  TALER_ReserveSignatureP *reserve_sig,
-                                  const struct
-                                  TALER_ReservePublicKeyP *reserve_pub,
-                                  const struct TALER_PlanchetSecretsP *ps,
-                                  TALER_EXCHANGE_ReserveWithdrawResultCallback
-                                  res_cb,
-                                  void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
-  struct TALER_PlanchetDetail pd;
-
-  if (GNUNET_OK !=
-      TALER_planchet_prepare (&pk->key,
-                              ps,
-                              &pd))
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  wsh = reserve_withdraw_internal (exchange,
-                                   pk,
-                                   reserve_sig,
-                                   reserve_pub,
-                                   ps,
-                                   &pd,
-                                   res_cb,
-                                   res_cb_cls);
-  GNUNET_free (pd.coin_ev);
-  return wsh;
-}
-
-
-/**
- * Cancel a withdraw status request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param sign the withdraw sign request handle
- */
-void
-TALER_EXCHANGE_reserve_withdraw_cancel (struct
-                                        TALER_EXCHANGE_ReserveWithdrawHandle *
-                                        sign)
-{
-  if (NULL != sign->job)
-  {
-    GNUNET_CURL_job_cancel (sign->job);
-    sign->job = NULL;
-  }
-  GNUNET_free (sign->url);
-  TALER_curl_easy_post_finished (&sign->ctx);
-  GNUNET_CRYPTO_rsa_public_key_free (sign->pk.key.rsa_public_key);
-  GNUNET_free (sign);
-}
-
-
-/* end of exchange_api_reserve.c */
diff --git a/src/lib/exchange_api_reserves_get.c 
b/src/lib/exchange_api_reserves_get.c
new file mode 100644
index 00000000..37adace5
--- /dev/null
+++ b/src/lib/exchange_api_reserves_get.c
@@ -0,0 +1,308 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2020 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 lib/exchange_api_reserves_get.c
+ * @brief Implementation of the GET /reserves/$RESERVE_PUB requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A /reserves/ GET Handle
+ */
+struct TALER_EXCHANGE_ReservesGetHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_ReservesGetCallback cb;
+
+  /**
+   * Public key of the reserve we are querying.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK status code. Handle the JSON
+ * response.
+ *
+ * @param rgh handle of the request
+ * @param j JSON response
+ * @return #GNUNET_OK on success
+ */
+static int
+handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
+                        const json_t *j)
+{
+  json_t *history;
+  unsigned int len;
+  struct TALER_Amount balance;
+  struct TALER_Amount balance_from_history;
+  struct GNUNET_JSON_Specification spec[] = {
+    TALER_JSON_spec_amount ("balance", &balance),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (j,
+                         spec,
+                         NULL,
+                         NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  history = json_object_get (j,
+                             "history");
+  if (NULL == history)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  len = json_array_size (history);
+  {
+    struct TALER_EXCHANGE_ReserveHistory *rhistory;
+
+    rhistory = GNUNET_new_array (len,
+                                 struct TALER_EXCHANGE_ReserveHistory);
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
+                                              history,
+                                              &rgh->reserve_pub,
+                                              balance.currency,
+                                              &balance_from_history,
+                                              len,
+                                              rhistory))
+    {
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      return GNUNET_SYSERR;
+    }
+    if (0 !=
+        TALER_amount_cmp (&balance_from_history,
+                          &balance))
+    {
+      /* exchange cannot add up balances!? */
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      return GNUNET_SYSERR;
+    }
+    if (NULL != rgh->cb)
+    {
+      rgh->cb (rgh->cb_cls,
+               MHD_HTTP_OK,
+               TALER_EC_NONE,
+               j,
+               &balance,
+               len,
+               rhistory);
+      rgh->cb = NULL;
+    }
+    TALER_EXCHANGE_free_reserve_history (rhistory,
+                                         len);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/ GET request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_ReservesGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_get_finished (void *cls,
+                              long response_code,
+                              const void *response)
+{
+  struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
+  const json_t *j = response;
+
+  rgh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        handle_reserves_get_ok (rgh,
+                                j))
+      response_code = 0;
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != rgh->cb)
+  {
+    rgh->cb (rgh->cb_cls,
+             response_code,
+             TALER_JSON_get_error_code (j),
+             j,
+             NULL,
+             0, NULL);
+    rgh->cb = NULL;
+  }
+  TALER_EXCHANGE_reserves_get_cancel (rgh);
+}
+
+
+/**
+ * Submit a request to obtain the transaction history of a reserve
+ * from the exchange.  Note that while we return the full response to the
+ * caller for further processing, we do already verify that the
+ * response is well-formed (i.e. that signatures included in the
+ * response are all valid and add up to the balance).  If the exchange's
+ * reply is not well-formed, we return an HTTP status code of zero to
+ * @a cb.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param reserve_pub public key of the reserve to inspect
+ * @param cb the callback to call when a reply for this request is available
+ * @param cb_cls closure for the above callback
+ * @return a handle for this request; NULL if the inputs are invalid (i.e.
+ *         signatures fail to verify).  In this case, the callback is not 
called.
+ */
+struct TALER_EXCHANGE_ReservesGetHandle *
+TALER_EXCHANGE_reserves_get (struct TALER_EXCHANGE_Handle *exchange,
+                             const struct
+                             TALER_ReservePublicKeyP *reserve_pub,
+                             TALER_EXCHANGE_ReservesGetCallback cb,
+                             void *cb_cls)
+{
+  struct TALER_EXCHANGE_ReservesGetHandle *rgh;
+  struct GNUNET_CURL_Context *ctx;
+  CURL *eh;
+  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16];
+
+  if (GNUNET_YES !=
+      TEAH_handle_is_ready (exchange))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  {
+    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (reserve_pub,
+                                         sizeof (struct
+                                                 TALER_ReservePublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/reserves/%s",
+                     pub_str);
+  }
+  rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesGetHandle);
+  rgh->exchange = exchange;
+  rgh->cb = cb;
+  rgh->cb_cls = cb_cls;
+  rgh->reserve_pub = *reserve_pub;
+  rgh->url = TEAH_path_to_url (exchange,
+                               arg_str);
+  eh = TALER_EXCHANGE_curl_easy_get_ (rgh->url);
+  ctx = TEAH_handle_to_context (exchange);
+  rgh->job = GNUNET_CURL_job_add (ctx,
+                                  eh,
+                                  GNUNET_NO,
+                                  &handle_reserves_get_finished,
+                                  rgh);
+  return rgh;
+}
+
+
+/**
+ * Cancel a reserve status request.  This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param rgh the reserve status request handle
+ */
+void
+TALER_EXCHANGE_reserves_get_cancel (struct
+                                    TALER_EXCHANGE_ReservesGetHandle *rgh)
+{
+  if (NULL != rgh->job)
+  {
+    GNUNET_CURL_job_cancel (rgh->job);
+    rgh->job = NULL;
+  }
+  GNUNET_free (rgh->url);
+  GNUNET_free (rgh);
+}
+
+
+/* end of exchange_api_reserve.c */
diff --git a/src/lib/exchange_api_track_transfer.c 
b/src/lib/exchange_api_transfers_get.c
similarity index 80%
rename from src/lib/exchange_api_track_transfer.c
rename to src/lib/exchange_api_transfers_get.c
index ba8948fe..25a1fea8 100644
--- a/src/lib/exchange_api_track_transfer.c
+++ b/src/lib/exchange_api_transfers_get.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/exchange_api_track_transfer.c
- * @brief Implementation of the /track/transfer request of the exchange's HTTP 
API
+ * @file lib/exchange_api_transfers_get.c
+ * @brief Implementation of the GET /transfers/ request
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -32,9 +32,9 @@
 
 
 /**
- * @brief A /track/transfer Handle
+ * @brief A /transfers/ GET Handle
  */
-struct TALER_EXCHANGE_TrackTransferHandle
+struct TALER_EXCHANGE_TransfersGetHandle
 {
 
   /**
@@ -55,7 +55,7 @@ struct TALER_EXCHANGE_TrackTransferHandle
   /**
    * Function to call with the result.
    */
-  TALER_EXCHANGE_TrackTransferCallback cb;
+  TALER_EXCHANGE_TransfersGetCallback cb;
 
   /**
    * Closure for @a cb.
@@ -66,12 +66,12 @@ struct TALER_EXCHANGE_TrackTransferHandle
 
 
 /**
- * We got a #MHD_HTTP_OK response for the /track/transfer request.
+ * We got a #MHD_HTTP_OK response for the /transfers/ request.
  * Check that the response is well-formed and if it is, call the
  * callback.  If not, return an error code.
  *
  * This code is very similar to
- * merchant_api_track_transfer.c::check_track_transfer_response_ok.
+ * merchant_api_track_transfer.c::check_transfers_get_response_ok.
  * Any changes should likely be reflected there as well.
  *
  * @param wdh handle to the operation
@@ -80,9 +80,9 @@ struct TALER_EXCHANGE_TrackTransferHandle
  *         #GNUNET_SYSERR if the response was bogus
  */
 static int
-check_track_transfer_response_ok (struct
-                                  TALER_EXCHANGE_TrackTransferHandle *wdh,
-                                  const json_t *json)
+check_transfers_get_response_ok (struct
+                                 TALER_EXCHANGE_TransfersGetHandle *wdh,
+                                 const json_t *json)
 {
   json_t *details_j;
   struct GNUNET_HashCode h_wire;
@@ -241,25 +241,25 @@ check_track_transfer_response_ok (struct
              details);
   }
   GNUNET_JSON_parse_free (spec);
-  TALER_EXCHANGE_track_transfer_cancel (wdh);
+  TALER_EXCHANGE_transfers_get_cancel (wdh);
   return GNUNET_OK;
 }
 
 
 /**
  * Function called when we're done processing the
- * HTTP /track/transfer request.
+ * HTTP /transfers/ request.
  *
- * @param cls the `struct TALER_EXCHANGE_TrackTransferHandle`
+ * @param cls the `struct TALER_EXCHANGE_TransfersGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response parsed JSON result, NULL on error
  */
 static void
-handle_track_transfer_finished (void *cls,
-                                long response_code,
-                                const void *response)
+handle_transfers_get_finished (void *cls,
+                               long response_code,
+                               const void *response)
 {
-  struct TALER_EXCHANGE_TrackTransferHandle *wdh = cls;
+  struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
   const json_t *j = response;
 
   wdh->job = NULL;
@@ -269,8 +269,8 @@ handle_track_transfer_finished (void *cls,
     break;
   case MHD_HTTP_OK:
     if (GNUNET_OK ==
-        check_track_transfer_response_ok (wdh,
-                                          j))
+        check_transfers_get_response_ok (wdh,
+                                         j))
       return;
     GNUNET_break_op (0);
     response_code = 0;
@@ -311,7 +311,7 @@ handle_track_transfer_finished (void *cls,
            NULL,
            NULL,
            0, NULL);
-  TALER_EXCHANGE_track_transfer_cancel (wdh);
+  TALER_EXCHANGE_transfers_get_cancel (wdh);
 }
 
 
@@ -325,18 +325,17 @@ handle_track_transfer_finished (void *cls,
  * @param cb_cls closure for @a cb
  * @return handle to cancel operation
  */
-struct TALER_EXCHANGE_TrackTransferHandle *
-TALER_EXCHANGE_track_transfer (struct TALER_EXCHANGE_Handle *exchange,
-                               const struct
-                               TALER_WireTransferIdentifierRawP *wtid,
-                               TALER_EXCHANGE_TrackTransferCallback cb,
-                               void *cb_cls)
+struct TALER_EXCHANGE_TransfersGetHandle *
+TALER_EXCHANGE_transfers_get (struct TALER_EXCHANGE_Handle *exchange,
+                              const struct
+                              TALER_WireTransferIdentifierRawP *wtid,
+                              TALER_EXCHANGE_TransfersGetCallback cb,
+                              void *cb_cls)
 {
-  struct TALER_EXCHANGE_TrackTransferHandle *wdh;
+  struct TALER_EXCHANGE_TransfersGetHandle *wdh;
   struct GNUNET_CURL_Context *ctx;
-  char *buf;
-  char *path;
   CURL *eh;
+  char arg_str[sizeof (struct TALER_WireTransferIdentifierRawP) * 2 + 32];
 
   if (GNUNET_YES !=
       TEAH_handle_is_ready (exchange))
@@ -345,28 +344,34 @@ TALER_EXCHANGE_track_transfer (struct 
TALER_EXCHANGE_Handle *exchange,
     return NULL;
   }
 
-  wdh = GNUNET_new (struct TALER_EXCHANGE_TrackTransferHandle);
+  wdh = GNUNET_new (struct TALER_EXCHANGE_TransfersGetHandle);
   wdh->exchange = exchange;
   wdh->cb = cb;
   wdh->cb_cls = cb_cls;
 
-  buf = GNUNET_STRINGS_data_to_string_alloc (wtid,
-                                             sizeof (struct
-                                                     
TALER_WireTransferIdentifierRawP));
-  GNUNET_asprintf (&path,
-                   "/track/transfer?wtid=%s",
-                   buf);
-  wdh->url = TEAH_path_to_url (wdh->exchange,
-                               path);
-  GNUNET_free (buf);
-  GNUNET_free (path);
+  {
+    char wtid_str[sizeof (struct TALER_WireTransferIdentifierRawP) * 2];
+    char *end;
 
-  eh = TEL_curl_easy_get (wdh->url);
+    end = GNUNET_STRINGS_data_to_string (wtid,
+                                         sizeof (struct
+                                                 
TALER_WireTransferIdentifierRawP),
+                                         wtid_str,
+                                         sizeof (wtid_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/transfers/%s",
+                     wtid_str);
+  }
+  wdh->url = TEAH_path_to_url (wdh->exchange,
+                               arg_str);
+  eh = TALER_EXCHANGE_curl_easy_get_ (wdh->url);
   ctx = TEAH_handle_to_context (exchange);
   wdh->job = GNUNET_CURL_job_add (ctx,
                                   eh,
                                   GNUNET_YES,
-                                  &handle_track_transfer_finished,
+                                  &handle_transfers_get_finished,
                                   wdh);
   return wdh;
 }
@@ -379,8 +384,8 @@ TALER_EXCHANGE_track_transfer (struct TALER_EXCHANGE_Handle 
*exchange,
  * @param wdh the wire deposits request handle
  */
 void
-TALER_EXCHANGE_track_transfer_cancel (struct
-                                      TALER_EXCHANGE_TrackTransferHandle *wdh)
+TALER_EXCHANGE_transfers_get_cancel (struct
+                                     TALER_EXCHANGE_TransfersGetHandle *wdh)
 {
   if (NULL != wdh->job)
   {
diff --git a/src/lib/exchange_api_wire.c b/src/lib/exchange_api_wire.c
index 9d17f5c8..81b9f430 100644
--- a/src/lib/exchange_api_wire.c
+++ b/src/lib/exchange_api_wire.c
@@ -55,7 +55,7 @@ struct TALER_EXCHANGE_WireHandle
   /**
    * Function to call with the result.
    */
-  TALER_EXCHANGE_WireResultCallback cb;
+  TALER_EXCHANGE_WireCallback cb;
 
   /**
    * Closure for @a cb.
@@ -388,7 +388,7 @@ handle_wire_finished (void *cls,
  */
 struct TALER_EXCHANGE_WireHandle *
 TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
-                     TALER_EXCHANGE_WireResultCallback wire_cb,
+                     TALER_EXCHANGE_WireCallback wire_cb,
                      void *wire_cb_cls)
 {
   struct TALER_EXCHANGE_WireHandle *wh;
@@ -407,7 +407,7 @@ TALER_EXCHANGE_wire (struct TALER_EXCHANGE_Handle *exchange,
   wh->cb_cls = wire_cb_cls;
   wh->url = TEAH_path_to_url (exchange, "/wire");
 
-  eh = TEL_curl_easy_get (wh->url);
+  eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
   ctx = TEAH_handle_to_context (exchange);
   wh->job = GNUNET_CURL_job_add (ctx,
                                  eh,
diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c
new file mode 100644
index 00000000..e7be4153
--- /dev/null
+++ b/src/lib/exchange_api_withdraw.c
@@ -0,0 +1,611 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2020 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 lib/exchange_api_withdraw.c
+ * @brief Implementation of the /reserves/$RESERVE_PUB/withdraw requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A Withdraw Handle
+ */
+struct TALER_EXCHANGE_WithdrawHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext ctx;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_WithdrawCallback cb;
+
+  /**
+   * Secrets of the planchet.
+   */
+  struct TALER_PlanchetSecretsP ps;
+
+  /**
+   * Denomination key we are withdrawing.
+   */
+  struct TALER_EXCHANGE_DenomPublicKey pk;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Hash of the public key of the coin we are signing.
+   */
+  struct GNUNET_HashCode c_hash;
+
+  /**
+   * Public key of the reserve we are withdrawing from.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+};
+
+
+/**
+ * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
+ * Extract the coin's signature and return it to the caller.  The signature we
+ * get from the exchange is for the blinded value.  Thus, we first must
+ * unblind it and then should verify its validity against our coin's hash.
+ *
+ * If everything checks out, we return the unblinded signature
+ * to the application via the callback.
+ *
+ * @param wh operation handle
+ * @param json reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+reserve_withdraw_ok (struct TALER_EXCHANGE_WithdrawHandle *wh,
+                     const json_t *json)
+{
+  struct GNUNET_CRYPTO_RsaSignature *blind_sig;
+  struct TALER_FreshCoin fc;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_rsa_signature ("ev_sig",
+                                    &blind_sig),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      TALER_planchet_to_coin (&wh->pk.key,
+                              blind_sig,
+                              &wh->ps,
+                              &wh->c_hash,
+                              &fc))
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_JSON_parse_free (spec);
+
+  /* signature is valid, return it to the application */
+  wh->cb (wh->cb_cls,
+          MHD_HTTP_OK,
+          TALER_EC_NONE,
+          &fc.sig,
+          json);
+  /* make sure callback isn't called again after return */
+  wh->cb = NULL;
+  GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
+  return GNUNET_OK;
+}
+
+
+/**
+ * We got a 409 CONFLICT response for the /reserves/$RESERVE_PUB/withdraw 
operation.
+ * Check the signatures on the withdraw transactions in the provided
+ * history and that the balances add up.  We don't do anything directly
+ * with the information, as the JSON will be returned to the application.
+ * However, our job is ensuring that the exchange followed the protocol, and
+ * this in particular means checking all of the signatures in the history.
+ *
+ * @param wh operation handle
+ * @param json reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+reserve_withdraw_payment_required (struct
+                                   TALER_EXCHANGE_WithdrawHandle *wh,
+                                   const json_t *json)
+{
+  struct TALER_Amount balance;
+  struct TALER_Amount balance_from_history;
+  struct TALER_Amount requested_amount;
+  json_t *history;
+  size_t len;
+  struct GNUNET_JSON_Specification spec[] = {
+    TALER_JSON_spec_amount ("balance", &balance),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  history = json_object_get (json,
+                             "history");
+  if (NULL == history)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* go over transaction history and compute
+     total incoming and outgoing amounts */
+  len = json_array_size (history);
+  {
+    struct TALER_EXCHANGE_ReserveHistory *rhistory;
+
+    /* Use heap allocation as "len" may be very big and thus this may
+       not fit on the stack. Use "GNUNET_malloc_large" as a malicious
+       exchange may theoretically try to crash us by giving a history
+       that does not fit into our memory. */
+    rhistory = GNUNET_malloc_large (sizeof (struct
+                                            TALER_EXCHANGE_ReserveHistory)
+                                    * len);
+    if (NULL == rhistory)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_parse_reserve_history (wh->exchange,
+                                              history,
+                                              &wh->reserve_pub,
+                                              balance.currency,
+                                              &balance_from_history,
+                                              len,
+                                              rhistory))
+    {
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      return GNUNET_SYSERR;
+    }
+    TALER_EXCHANGE_free_reserve_history (rhistory,
+                                         len);
+  }
+
+  if (0 !=
+      TALER_amount_cmp (&balance_from_history,
+                        &balance))
+  {
+    /* exchange cannot add up balances!? */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  /* Compute how much we expected to charge to the reserve */
+  if (GNUNET_OK !=
+      TALER_amount_add (&requested_amount,
+                        &wh->pk.value,
+                        &wh->pk.fee_withdraw))
+  {
+    /* Overflow here? Very strange, our CPU must be fried... */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  /* Check that funds were really insufficient */
+  if (0 >= TALER_amount_cmp (&requested_amount,
+                             &balance))
+  {
+    /* Requested amount is smaller or equal to reported balance,
+       so this should not have failed. */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RESERVE_PUB/withdraw request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserve_withdraw_finished (void *cls,
+                                  long response_code,
+                                  const void *response)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
+  const json_t *j = response;
+
+  wh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        reserve_withdraw_ok (wh,
+                             j))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_CONFLICT:
+    /* The exchange says that the reserve has insufficient funds;
+       check the signatures in the history... */
+    if (GNUNET_OK !=
+        reserve_withdraw_payment_required (wh,
+                                           j))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    GNUNET_break (0);
+    /* Nothing really to verify, exchange says one of the signatures is
+       invalid; as we checked them, this should never happen, we
+       should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, the exchange basically just says
+       that it doesn't know this reserve.  Can happen if we
+       query before the wire transfer went through.
+       We should simply pass the JSON reply to the application. */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != wh->cb)
+  {
+    wh->cb (wh->cb_cls,
+            response_code,
+            TALER_JSON_get_error_code (j),
+            NULL,
+            j);
+    wh->cb = NULL;
+  }
+  TALER_EXCHANGE_withdraw_cancel (wh);
+}
+
+
+/**
+ * Helper function for #TALER_EXCHANGE_withdraw2() and
+ * #TALER_EXCHANGE_withdraw().
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk kind of coin to create
+ * @param reserve_sig signature from the reserve authorizing the withdrawal
+ * @param reserve_pub public key of the reserve to withdraw from
+ * @param ps secrets of the planchet
+ *        caller must have committed this value to disk before the call (with 
@a pk)
+ * @param pd planchet details matching @a ps
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_WithdrawHandle *
+reserve_withdraw_internal (struct TALER_EXCHANGE_Handle *exchange,
+                           const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                           const struct TALER_ReserveSignatureP *reserve_sig,
+                           const struct TALER_ReservePublicKeyP *reserve_pub,
+                           const struct TALER_PlanchetSecretsP *ps,
+                           const struct TALER_PlanchetDetail *pd,
+                           TALER_EXCHANGE_WithdrawCallback res_cb,
+                           void *res_cb_cls)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh;
+  struct GNUNET_CURL_Context *ctx;
+  json_t *withdraw_obj;
+  CURL *eh;
+  struct GNUNET_HashCode h_denom_pub;
+  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+
+  {
+    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (reserve_pub,
+                                         sizeof (struct
+                                                 TALER_ReservePublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/reserves/%s/withdraw",
+                     pub_str);
+  }
+  wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
+  wh->exchange = exchange;
+  wh->cb = res_cb;
+  wh->cb_cls = res_cb_cls;
+  wh->pk = *pk;
+  wh->pk.key.rsa_public_key
+    = GNUNET_CRYPTO_rsa_public_key_dup (pk->key.rsa_public_key);
+  wh->reserve_pub = *reserve_pub;
+  wh->c_hash = pd->c_hash;
+  GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key,
+                                     &h_denom_pub);
+  withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub_hash and coin_ev */
+                            " s:o}",/* reserve_pub and reserve_sig */
+                            "denom_pub_hash", GNUNET_JSON_from_data_auto (
+                              &h_denom_pub),
+                            "coin_ev", GNUNET_JSON_from_data (pd->coin_ev,
+                                                              
pd->coin_ev_size),
+                            "reserve_sig", GNUNET_JSON_from_data_auto (
+                              reserve_sig));
+  if (NULL == withdraw_obj)
+  {
+    GNUNET_break (0);
+    GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
+    GNUNET_free (wh);
+    return NULL;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Attempting to withdraw from reserve %s\n",
+              TALER_B2S (reserve_pub));
+  wh->ps = *ps;
+  wh->url = TEAH_path_to_url (exchange,
+                              arg_str);
+  eh = TALER_EXCHANGE_curl_easy_get_ (wh->url);
+  if (GNUNET_OK !=
+      TALER_curl_easy_post (&wh->ctx,
+                            eh,
+                            withdraw_obj))
+  {
+    GNUNET_break (0);
+    curl_easy_cleanup (eh);
+    json_decref (withdraw_obj);
+    GNUNET_free (wh->url);
+    GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
+    GNUNET_free (wh);
+    return NULL;
+  }
+  json_decref (withdraw_obj);
+  ctx = TEAH_handle_to_context (exchange);
+  wh->job = GNUNET_CURL_job_add2 (ctx,
+                                  eh,
+                                  wh->ctx.headers,
+                                  &handle_reserve_withdraw_finished,
+                                  wh);
+  return wh;
+}
+
+
+/**
+ * Withdraw a coin from the exchange using a /reserve/withdraw request.  Note
+ * that to ensure that no money is lost in case of hardware failures,
+ * the caller must have committed (most of) the arguments to disk
+ * before calling, and be ready to repeat the request with the same
+ * arguments in case of failures.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk kind of coin to create
+ * @param reserve_priv private key of the reserve to withdraw from
+ * @param ps secrets of the planchet
+ *        caller must have committed this value to disk before the call (with 
@a pk)
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for the above callback
+ * @return handle for the operation on success, NULL on error, i.e.
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_WithdrawHandle *
+TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle *exchange,
+                         const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                         const struct
+                         TALER_ReservePrivateKeyP *reserve_priv,
+                         const struct TALER_PlanchetSecretsP *ps,
+                         TALER_EXCHANGE_WithdrawCallback
+                         res_cb,
+                         void *res_cb_cls)
+{
+  struct TALER_Amount amount_with_fee;
+  struct TALER_ReserveSignatureP reserve_sig;
+  struct TALER_WithdrawRequestPS req;
+  struct TALER_PlanchetDetail pd;
+  struct TALER_EXCHANGE_WithdrawHandle *wh;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+                                      &req.reserve_pub.eddsa_pub);
+  req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
+  req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+  if (GNUNET_OK !=
+      TALER_amount_add (&amount_with_fee,
+                        &pk->fee_withdraw,
+                        &pk->value))
+  {
+    /* exchange gave us denomination keys that overflow like this!? */
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  TALER_amount_hton (&req.amount_with_fee,
+                     &amount_with_fee);
+  TALER_amount_hton (&req.withdraw_fee,
+                     &pk->fee_withdraw);
+  if (GNUNET_OK !=
+      TALER_planchet_prepare (&pk->key,
+                              ps,
+                              &pd))
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  req.h_denomination_pub = pd.denom_pub_hash;
+  GNUNET_CRYPTO_hash (pd.coin_ev,
+                      pd.coin_ev_size,
+                      &req.h_coin_envelope);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
+                                           &req.purpose,
+                                           &reserve_sig.eddsa_signature));
+  wh = reserve_withdraw_internal (exchange,
+                                  pk,
+                                  &reserve_sig,
+                                  &req.reserve_pub,
+                                  ps,
+                                  &pd,
+                                  res_cb,
+                                  res_cb_cls);
+  GNUNET_free (pd.coin_ev);
+  return wh;
+}
+
+
+/**
+ * Withdraw a coin from the exchange using a /reserve/withdraw
+ * request.  This API is typically used by a wallet to withdraw a tip
+ * where the reserve's signature was created by the merchant already.
+ *
+ * Note that to ensure that no money is lost in case of hardware
+ * failures, the caller must have committed (most of) the arguments to
+ * disk before calling, and be ready to repeat the request with the
+ * same arguments in case of failures.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk kind of coin to create
+ * @param reserve_sig signature from the reserve authorizing the withdrawal
+ * @param reserve_pub public key of the reserve to withdraw from
+ * @param ps secrets of the planchet
+ *        caller must have committed this value to disk before the call (with 
@a pk)
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_WithdrawHandle *
+TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
+                          const struct
+                          TALER_EXCHANGE_DenomPublicKey *pk,
+                          const struct
+                          TALER_ReserveSignatureP *reserve_sig,
+                          const struct
+                          TALER_ReservePublicKeyP *reserve_pub,
+                          const struct TALER_PlanchetSecretsP *ps,
+                          TALER_EXCHANGE_WithdrawCallback
+                          res_cb,
+                          void *res_cb_cls)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh;
+  struct TALER_PlanchetDetail pd;
+
+  if (GNUNET_OK !=
+      TALER_planchet_prepare (&pk->key,
+                              ps,
+                              &pd))
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  wh = reserve_withdraw_internal (exchange,
+                                  pk,
+                                  reserve_sig,
+                                  reserve_pub,
+                                  ps,
+                                  &pd,
+                                  res_cb,
+                                  res_cb_cls);
+  GNUNET_free (pd.coin_ev);
+  return wh;
+}
+
+
+/**
+ * Cancel a withdraw status request.  This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param sign the withdraw sign request handle
+ */
+void
+TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
+{
+  if (NULL != wh->job)
+  {
+    GNUNET_CURL_job_cancel (wh->job);
+    wh->job = NULL;
+  }
+  GNUNET_free (wh->url);
+  TALER_curl_easy_post_finished (&wh->ctx);
+  GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
+  GNUNET_free (wh);
+}
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index 144f004d..a2d7e00a 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -190,10 +190,10 @@ run (void *cls,
     /**
      * Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x
      * EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
-                                           "refresh-withdraw-coin-1",
-                                           MHD_HTTP_OK,
-                                           NULL),
+    TALER_TESTING_cmd_melt_double ("refresh-melt-1",
+                                   "refresh-withdraw-coin-1",
+                                   MHD_HTTP_OK,
+                                   NULL),
     /**
      * Complete (successful) melt operation, and withdraw the coins
      */
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index 38d0c921..ba655d9b 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -239,10 +239,10 @@ run (void *cls,
     /**
      * Melt the rest of the coin's value
      * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
-                                           "refresh-withdraw-coin-1",
-                                           MHD_HTTP_OK,
-                                           NULL),
+    TALER_TESTING_cmd_melt_double ("refresh-melt-1",
+                                   "refresh-withdraw-coin-1",
+                                   MHD_HTTP_OK,
+                                   NULL),
     /**
      * Complete (successful) melt operation, and
      * withdraw the coins
@@ -286,16 +286,16 @@ run (void *cls,
                                MHD_HTTP_OK),
     /* Test running a failing melt operation (same operation
      * again must fail) */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing",
-                                    "refresh-withdraw-coin-1",
-                                    MHD_HTTP_CONFLICT,
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-failing",
+                            "refresh-withdraw-coin-1",
+                            MHD_HTTP_CONFLICT,
+                            NULL),
     /* Test running a failing melt operation (on a coin that
        was itself revealed and subsequently deposited) */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing-2",
-                                    "refresh-reveal-1",
-                                    MHD_HTTP_CONFLICT,
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-failing-2",
+                            "refresh-reveal-1",
+                            MHD_HTTP_CONFLICT,
+                            NULL),
 
     TALER_TESTING_cmd_end ()
   };
diff --git a/src/testing/test_exchange_api_revocation.c 
b/src/testing/test_exchange_api_revocation.c
index 45463ded..6a9cdefe 100644
--- a/src/testing/test_exchange_api_revocation.c
+++ b/src/testing/test_exchange_api_revocation.c
@@ -98,10 +98,10 @@ run (void *cls,
     /**
      * Melt SOME of the rest of the coin's value
      * (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-1",
-                                    "withdraw-revocation-coin-1",
-                                    MHD_HTTP_OK,
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-1",
+                            "withdraw-revocation-coin-1",
+                            MHD_HTTP_OK,
+                            NULL),
     /**
      * Complete (successful) melt operation, and withdraw the coins
      */
@@ -133,11 +133,11 @@ run (void *cls,
     /* Melt original coin AGAIN, but only create one 0.1 EUR coin;
        This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
        leaving EUR:3.69. */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-2",
-                                    "withdraw-revocation-coin-1",
-                                    MHD_HTTP_OK,
-                                    "EUR:0.1",
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-2",
+                            "withdraw-revocation-coin-1",
+                            MHD_HTTP_OK,
+                            "EUR:0.1",
+                            NULL),
     /**
      * Complete (successful) melt operation, and withdraw the coins
      */
diff --git a/src/testing/test_exchange_api_twisted.c 
b/src/testing/test_exchange_api_twisted.c
index a3a038d4..146e28de 100644
--- a/src/testing/test_exchange_api_twisted.c
+++ b/src/testing/test_exchange_api_twisted.c
@@ -140,7 +140,7 @@ run (void *cls,
     /**
      * Melt the rest of the coin's value
      * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt
+    TALER_TESTING_cmd_melt
       ("refresh-melt",
       "refresh-withdraw-coin",
       MHD_HTTP_OK,
diff --git a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c 
b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
index 6115ceef..96d0740e 100644
--- a/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
+++ b/src/testing/testing_api_cmd_auditor_deposit_confirmation.c
@@ -227,8 +227,8 @@ deposit_confirmation_run (void *cls,
                                                        &exchange_sig));
   keys = TALER_EXCHANGE_get_keys (dcs->is->exchange);
   GNUNET_assert (NULL != keys);
-  spk = TALER_EXCHANGE_get_exchange_signing_key_info (keys,
-                                                      exchange_pub);
+  spk = TALER_EXCHANGE_get_signing_key_info (keys,
+                                             exchange_pub);
 
   GNUNET_assert (GNUNET_OK ==
                  TALER_TESTING_get_trait_contract_terms (deposit_cmd,
diff --git a/src/testing/testing_api_cmd_refresh.c 
b/src/testing/testing_api_cmd_refresh.c
index 73b74daf..dd861fae 100644
--- a/src/testing/testing_api_cmd_refresh.c
+++ b/src/testing/testing_api_cmd_refresh.c
@@ -87,7 +87,7 @@ struct RefreshMeltState
   /**
    * Melt handle while operation is running.
    */
-  struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
+  struct TALER_EXCHANGE_MeltHandle *rmh;
 
   /**
    * Interpreter state.
@@ -173,7 +173,7 @@ struct RefreshRevealState
   /**
    * Reveal handle while operation is running.
    */
-  struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
+  struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
 
   /**
    * Convenience struct to keep in one place all the
@@ -230,7 +230,7 @@ struct RefreshLinkState
   /**
    * Handle to the ongoing operation.
    */
-  struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
+  struct TALER_EXCHANGE_LinkHandle *rlh;
 
   /**
    * Interpreter state.
@@ -423,7 +423,7 @@ refresh_reveal_run (void *cls,
     return;
   }
   rms = melt_cmd->cls;
-  rrs->rrh = TALER_EXCHANGE_refresh_reveal
+  rrs->rrh = TALER_EXCHANGE_refreshes_reveal
                (is->exchange,
                rms->refresh_data_length,
                rms->refresh_data,
@@ -459,7 +459,7 @@ refresh_reveal_cleanup (void *cls,
                 rrs->is->ip,
                 cmd->label);
 
-    TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
+    TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh);
     rrs->rrh = NULL;
   }
   if (NULL != rrs->retry_task)
@@ -730,7 +730,7 @@ refresh_link_run (void *cls,
   }
 
   /* finally, use private key from withdraw sign command */
-  rls->rlh = TALER_EXCHANGE_refresh_link
+  rls->rlh = TALER_EXCHANGE_link
                (is->exchange, coin_priv, &link_cb, rls);
 
   if (NULL == rls->rlh)
@@ -762,7 +762,7 @@ refresh_link_cleanup (void *cls,
                 "Command %u (%s) did not complete\n",
                 rls->is->ip,
                 cmd->label);
-    TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
+    TALER_EXCHANGE_link_cancel (rls->rlh);
     rls->rlh = NULL;
   }
   if (NULL != rls->retry_task)
@@ -782,13 +782,13 @@ refresh_link_cleanup (void *cls,
  * @param is the interpreter state.
  */
 static void
-refresh_melt_run (void *cls,
-                  const struct TALER_TESTING_Command *cmd,
-                  struct TALER_TESTING_Interpreter *is);
+melt_run (void *cls,
+          const struct TALER_TESTING_Command *cmd,
+          struct TALER_TESTING_Interpreter *is);
 
 
 /**
- * Task scheduled to re-try #refresh_melt_run.
+ * Task scheduled to re-try #melt_run.
  *
  * @param cls a `struct RefreshMeltState`
  */
@@ -798,9 +798,9 @@ do_melt_retry (void *cls)
   struct RefreshMeltState *rms = cls;
 
   rms->retry_task = NULL;
-  refresh_melt_run (rms,
-                    NULL,
-                    rms->is);
+  melt_run (rms,
+            NULL,
+            rms->is);
 }
 
 
@@ -870,7 +870,7 @@ melt_cb (void *cls,
   {
     TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
                      rms->is->commands[rms->is->ip].label);
-    rms->rmh = TALER_EXCHANGE_refresh_melt
+    rms->rmh = TALER_EXCHANGE_melt
                  (rms->is->exchange, rms->refresh_data_length,
                  rms->refresh_data, &melt_cb, rms);
     rms->double_melt = GNUNET_NO;
@@ -888,9 +888,9 @@ melt_cb (void *cls,
  * @param is the interpreter state.
  */
 static void
-refresh_melt_run (void *cls,
-                  const struct TALER_TESTING_Command *cmd,
-                  struct TALER_TESTING_Interpreter *is)
+melt_run (void *cls,
+          const struct TALER_TESTING_Command *cmd,
+          struct TALER_TESTING_Interpreter *is)
 {
   struct RefreshMeltState *rms = cls;
   unsigned int num_fresh_coins;
@@ -1006,11 +1006,11 @@ refresh_melt_run (void *cls,
       TALER_TESTING_interpreter_fail (rms->is);
       return;
     }
-    rms->rmh = TALER_EXCHANGE_refresh_melt (is->exchange,
-                                            rms->refresh_data_length,
-                                            rms->refresh_data,
-                                            &melt_cb,
-                                            rms);
+    rms->rmh = TALER_EXCHANGE_melt (is->exchange,
+                                    rms->refresh_data_length,
+                                    rms->refresh_data,
+                                    &melt_cb,
+                                    rms);
 
     if (NULL == rms->rmh)
     {
@@ -1030,8 +1030,8 @@ refresh_melt_run (void *cls,
  * @param cmd the command which is being cleaned up.
  */
 static void
-refresh_melt_cleanup (void *cls,
-                      const struct TALER_TESTING_Command *cmd)
+melt_cleanup (void *cls,
+              const struct TALER_TESTING_Command *cmd)
 {
   struct RefreshMeltState *rms = cls;
 
@@ -1040,7 +1040,7 @@ refresh_melt_cleanup (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Command %u (%s) did not complete\n",
                 rms->is->ip, rms->is->commands[rms->is->ip].label);
-    TALER_EXCHANGE_refresh_melt_cancel (rms->rmh);
+    TALER_EXCHANGE_melt_cancel (rms->rmh);
     rms->rmh = NULL;
   }
   if (NULL != rms->retry_task)
@@ -1073,10 +1073,10 @@ refresh_melt_cleanup (void *cls,
  * @return #GNUNET_OK on success.
  */
 static int
-refresh_melt_traits (void *cls,
-                     const void **ret,
-                     const char *trait,
-                     unsigned int index)
+melt_traits (void *cls,
+             const void **ret,
+             const char *trait,
+             unsigned int index)
 {
   struct RefreshMeltState *rms = cls;
 
@@ -1161,10 +1161,10 @@ parse_amounts (struct RefreshMeltState *rms,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt (const char *label,
-                                const char *coin_reference,
-                                unsigned int expected_response_code,
-                                ...)
+TALER_TESTING_cmd_melt (const char *label,
+                        const char *coin_reference,
+                        unsigned int expected_response_code,
+                        ...)
 {
   struct RefreshMeltState *rms;
   va_list ap;
@@ -1180,9 +1180,9 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
     struct TALER_TESTING_Command cmd = {
       .label = label,
       .cls = rms,
-      .run = &refresh_melt_run,
-      .cleanup = &refresh_melt_cleanup,
-      .traits = &refresh_melt_traits
+      .run = &melt_run,
+      .cleanup = &melt_cleanup,
+      .traits = &melt_traits
     };
 
     return cmd;
@@ -1203,10 +1203,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_double (const char *label,
-                                       const char *coin_reference,
-                                       unsigned int expected_response_code,
-                                       ...)
+TALER_TESTING_cmd_melt_double (const char *label,
+                               const char *coin_reference,
+                               unsigned int expected_response_code,
+                               ...)
 {
   struct RefreshMeltState *rms;
   va_list ap;
@@ -1223,9 +1223,9 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
     struct TALER_TESTING_Command cmd = {
       .label = label,
       .cls = rms,
-      .run = &refresh_melt_run,
-      .cleanup = &refresh_melt_cleanup,
-      .traits = &refresh_melt_traits
+      .run = &melt_run,
+      .cleanup = &melt_cleanup,
+      .traits = &melt_traits
     };
 
     return cmd;
@@ -1240,11 +1240,11 @@ TALER_TESTING_cmd_refresh_melt_double (const char 
*label,
  * @return modified command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd)
+TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
 {
   struct RefreshMeltState *rms;
 
-  GNUNET_assert (&refresh_melt_run == cmd.run);
+  GNUNET_assert (&melt_run == cmd.run);
   rms = cmd.cls;
   rms->do_retry = GNUNET_YES;
   return cmd;
diff --git a/src/testing/testing_api_cmd_status.c 
b/src/testing/testing_api_cmd_status.c
index 1c652b6d..bb132645 100644
--- a/src/testing/testing_api_cmd_status.c
+++ b/src/testing/testing_api_cmd_status.c
@@ -41,7 +41,7 @@ struct StatusState
   /**
    * Handle to the "reserve status" operation.
    */
-  struct TALER_EXCHANGE_ReserveStatusHandle *rsh;
+  struct TALER_EXCHANGE_ReservesGetHandle *rsh;
 
   /**
    * Expected reserve balance.
@@ -93,6 +93,7 @@ reserve_status_cb (void *cls,
                 http_status,
                 __FILE__,
                 __LINE__);
+    json_dumpf (json, stderr, 0);
     TALER_TESTING_interpreter_fail (ss->is);
     return;
   }
@@ -168,10 +169,10 @@ status_run (void *cls,
     TALER_TESTING_interpreter_fail (is);
     return;
   }
-  ss->rsh = TALER_EXCHANGE_reserve_status (is->exchange,
-                                           reserve_pubp,
-                                           &reserve_status_cb,
-                                           ss);
+  ss->rsh = TALER_EXCHANGE_reserves_get (is->exchange,
+                                         reserve_pubp,
+                                         &reserve_status_cb,
+                                         ss);
 }
 
 
@@ -194,7 +195,7 @@ status_cleanup (void *cls,
                 "Command %u (%s) did not complete\n",
                 ss->is->ip,
                 cmd->label);
-    TALER_EXCHANGE_reserve_status_cancel (ss->rsh);
+    TALER_EXCHANGE_reserves_get_cancel (ss->rsh);
     ss->rsh = NULL;
   }
   GNUNET_free (ss);
diff --git a/src/testing/testing_api_cmd_track.c 
b/src/testing/testing_api_cmd_track.c
index 2b7263cd..6ebef693 100644
--- a/src/testing/testing_api_cmd_track.c
+++ b/src/testing/testing_api_cmd_track.c
@@ -64,7 +64,7 @@ struct TrackTransactionState
   /**
    * Handle to the "track transaction" pending operation.
    */
-  struct TALER_EXCHANGE_TrackTransactionHandle *tth;
+  struct TALER_EXCHANGE_DepositGetHandle *tth;
 
   /**
    * Interpreter state.
@@ -124,7 +124,7 @@ struct TrackTransferState
   /**
    * Handle to a pending "track transfer" operation.
    */
-  struct TALER_EXCHANGE_TrackTransferHandle *tth;
+  struct TALER_EXCHANGE_TransfersGetHandle *tth;
 
   /**
    * Interpreter state.
@@ -327,13 +327,13 @@ track_transaction_run (void *cls,
     return;
   }
 
-  tts->tth = TALER_EXCHANGE_track_transaction (is->exchange,
-                                               merchant_priv,
-                                               &h_wire_details,
-                                               &h_contract_terms,
-                                               &coin_pub,
-                                               &deposit_wtid_cb,
-                                               tts);
+  tts->tth = TALER_EXCHANGE_deposits_get (is->exchange,
+                                          merchant_priv,
+                                          &h_wire_details,
+                                          &h_contract_terms,
+                                          &coin_pub,
+                                          &deposit_wtid_cb,
+                                          tts);
   GNUNET_assert (NULL != tts->tth);
 }
 
@@ -357,7 +357,7 @@ track_transaction_cleanup (void *cls,
                 "Command %u (%s) did not complete\n",
                 tts->is->ip,
                 cmd->label);
-    TALER_EXCHANGE_track_transaction_cancel (tts->tth);
+    TALER_EXCHANGE_deposits_get_cancel (tts->tth);
     tts->tth = NULL;
   }
   GNUNET_free (tts);
@@ -453,7 +453,7 @@ track_transfer_cleanup (void *cls,
                 "Command %u (%s) did not complete\n",
                 tts->is->ip,
                 cmd->label);
-    TALER_EXCHANGE_track_transfer_cancel (tts->tth);
+    TALER_EXCHANGE_transfers_get_cancel (tts->tth);
     tts->tth = NULL;
   }
   GNUNET_free (tts);
@@ -709,10 +709,10 @@ track_transfer_run (void *cls,
     }
     GNUNET_assert (NULL != wtid_ptr);
   }
-  tts->tth = TALER_EXCHANGE_track_transfer (is->exchange,
-                                            wtid_ptr,
-                                            &track_transfer_cb,
-                                            tts);
+  tts->tth = TALER_EXCHANGE_transfers_get (is->exchange,
+                                           wtid_ptr,
+                                           &track_transfer_cb,
+                                           tts);
   GNUNET_assert (NULL != tts->tth);
 }
 
diff --git a/src/testing/testing_api_cmd_withdraw.c 
b/src/testing/testing_api_cmd_withdraw.c
index 207d1187..96412156 100644
--- a/src/testing/testing_api_cmd_withdraw.c
+++ b/src/testing/testing_api_cmd_withdraw.c
@@ -80,7 +80,7 @@ struct WithdrawState
   /**
    * Withdraw handle (while operation is running).
    */
-  struct TALER_EXCHANGE_ReserveWithdrawHandle *wsh;
+  struct TALER_EXCHANGE_WithdrawHandle *wsh;
 
   /**
    * Task scheduled to try later.
@@ -280,12 +280,12 @@ withdraw_run (void *cls,
      * would free the old one. */
     ws->pk = TALER_EXCHANGE_copy_denomination_key (dpk);
   }
-  ws->wsh = TALER_EXCHANGE_reserve_withdraw (is->exchange,
-                                             ws->pk,
-                                             rp,
-                                             &ws->ps,
-                                             &reserve_withdraw_cb,
-                                             ws);
+  ws->wsh = TALER_EXCHANGE_withdraw (is->exchange,
+                                     ws->pk,
+                                     rp,
+                                     &ws->ps,
+                                     &reserve_withdraw_cb,
+                                     ws);
   if (NULL == ws->wsh)
   {
     GNUNET_break (0);
@@ -313,7 +313,7 @@ withdraw_cleanup (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Command %s did not complete\n",
                 cmd->label);
-    TALER_EXCHANGE_reserve_withdraw_cancel (ws->wsh);
+    TALER_EXCHANGE_withdraw_cancel (ws->wsh);
     ws->wsh = NULL;
   }
   if (NULL != ws->retry_task)
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index e9ccdb81..5f30f71e 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -693,6 +693,16 @@ do_abort (void *cls)
     TALER_EXCHANGE_disconnect (is->exchange);
     is->exchange = NULL;
   }
+  if (NULL != is->ctx)
+  {
+    GNUNET_CURL_fini (is->ctx);
+    is->ctx = NULL;
+  }
+  if (NULL != is->rc)
+  {
+    GNUNET_CURL_gnunet_rc_destroy (is->rc);
+    is->rc = NULL;
+  }
 }
 
 

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

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