diff --git a/sys/include/net/gcoap.h b/sys/include/net/gcoap.h
index 6f73cb12b0b8260e612ced22a64c008fdb15aad7..097fd4812cc13641b3f4815378471ef9f69b650a 100644
--- a/sys/include/net/gcoap.h
+++ b/sys/include/net/gcoap.h
@@ -9,14 +9,21 @@
 /**
  * @defgroup    net_gcoap  CoAP
  * @ingroup     net
- * @brief       sock-based implementation of CoAP protocol, RFC 7252
- *
- * ## Architecture ##
- * Requests and responses are exchanged via an asynchronous RIOT message
- * processing thread. Depends on nanocoap for base level structs and
- * functionality.
- *
- * Uses a single UDP port for communication to support RFC 6282 compression.
+ * @brief       High-level interface to CoAP messaging
+ *
+ * gcoap provides a high-level interface for writing CoAP messages via RIOT's
+ * sock networking API. gcoap internalizes network event processing so an
+ * application only needs to focus on request/response handling. For a server,
+ * gcoap accepts a list of resource paths with callbacks for writing the
+ * response. For a client, gcoap provides a function to send a request, with a
+ * callback for reading the server response. Generation of the request or
+ * response requires from one to three well-defined steps, depending on
+ * inclusion of a payload.
+ *
+ * gcoap allocates a RIOT message processing thread, so a single instance can
+ * serve multiple applications. This approach also means gcoap uses a single UDP
+ * port, which supports RFC 6282 compression. Internally, gcoap depends on the
+ * nanocoap package for base level structs and functionality.
  *
  * ## Server Operation ##
  *
@@ -61,11 +68,10 @@
  *
  * ## Client Operation ##
  *
- * gcoap uses RIOT's asynchronous messaging facility to send and receive
- * messages. So, client operation includes two phases:  creating and sending a
- * request, and handling the response aynchronously in a client supplied
- * callback.  See `examples/gcoap/gcoap_cli.c` for a simple example of sending
- * a request and reading the response.
+ * Client operation includes two phases:  creating and sending a request, and
+ * handling the response aynchronously in a client supplied callback.  See
+ * `examples/gcoap/gcoap_cli.c` for a simple example of sending a request and
+ * reading the response.
  *
  * ### Creating a request ###
  *
@@ -85,8 +91,8 @@
  * as described above. The gcoap_request() function is inline, and uses those
  * two functions.
  *
- * Finally, call gcoap_req_send() with the destination host and port, as well
- * as a callback function for the host's response.
+ * Finally, call gcoap_req_send2() for the destination endpoint, as well as a
+ * callback function for the host's response.
  *
  * ### Handling the response ###
  *
@@ -114,13 +120,13 @@
  * header and the payload. So, gcoap provides space in the buffer for them in
  * the request/response ...init() function, and then writes them during
  * gcoap_finish(). We trade some inefficiency/work in the buffer for
- * simplicity for the user.
+ * simplicity in the API.
  *
  * ### Waiting for a response ###
  *
- * We take advantage of RIOT's GNRC stack by using an xtimer to wait for a
- * response, so the gcoap thread does not block while waiting. The user is
- * notified via the same callback whether the message is received or the wait
+ * We take advantage of RIOT's asynchronous messaging by using an xtimer to wait
+ * for a response, so the gcoap thread does not block while waiting. The user is
+ * notified via the same callback, whether the message is received or the wait
  * times out. We track the response with an entry in the
  * `_coap_state.open_reqs` array.
  *
@@ -135,9 +141,7 @@
 #ifndef GCOAP_H
 #define GCOAP_H
 
-#include "net/gnrc.h"
-#include "net/gnrc/ipv6.h"
-#include "net/gnrc/udp.h"
+#include "net/sock/udp.h"
 #include "nanocoap.h"
 #include "xtimer.h"
 
@@ -199,15 +203,27 @@ extern "C" {
 #define GCOAP_MEMO_ERR      (4)  /**< Error processing response packet */
 /** @} */
 
+/** @brief Time in usec that the event loop waits for an incoming CoAP message */
+#define GCOAP_RECV_TIMEOUT   (1 * US_PER_SEC)
+
 /**
+ *
  * @brief Default time to wait for a non-confirmable response, in usec
  *
  * Set to 0 to disable timeout.
  */
 #define GCOAP_NON_TIMEOUT    (5000000U)
 
-/** @brief Identifies a gcoap-specific timeout IPC message */
-#define GCOAP_NETAPI_MSG_TYPE_TIMEOUT    (0x1501)
+/** @brief Identifies waiting timed out for a response to a sent message. */
+#define GCOAP_MSG_TYPE_TIMEOUT    (0x1501)
+
+/**
+ * @brief Identifies a request to interrupt listening for an incoming message
+ *        on a sock.
+ *
+ * Allows the event loop to process IPC messages.
+ */
+#define GCOAP_MSG_TYPE_INTR    (0x1502)
 
 /**
  * @brief  A modular collection of resources for a server
@@ -243,7 +259,6 @@ typedef struct {
  * @brief  Container for the state of gcoap itself
  */
 typedef struct {
-    gnrc_netreg_entry_t netreg_port;   /**< Registration for IP port */
     gcoap_listener_t *listeners;       /**< List of registered listeners */
     gcoap_request_memo_t open_reqs[GCOAP_REQ_WAITING_MAX];
                                        /**< Storage for open requests; if first
@@ -322,9 +337,25 @@ static inline ssize_t gcoap_request(coap_pkt_t *pdu, uint8_t *buf, size_t len,
                 : -1;
 }
 
+/**
+ * @brief  Sends a buffer containing a CoAP request to the provided endpoint.
+ *
+ * @param[in] buf Buffer containing the PDU
+ * @param[in] len Length of the buffer
+ * @param[in] remote Destination for the packet
+ * @param[in] resp_handler Callback when response received
+ *
+ * @return length of the packet
+ * @return 0 if cannot send
+ */
+size_t gcoap_req_send2(uint8_t *buf, size_t len, sock_udp_ep_t *remote,
+                                                 gcoap_resp_handler_t resp_handler);
+
 /**
  * @brief  Sends a buffer containing a CoAP request to the provided host/port.
  *
+ * @deprecated  Please use @ref gcoap_req_send2() instead
+ *
  * @param[in] buf Buffer containing the PDU
  * @param[in] len Length of the buffer
  * @param[in] addr Destination for the packet
diff --git a/sys/net/application_layer/coap/gcoap.c b/sys/net/application_layer/coap/gcoap.c
index a2e055f3308eae8091d79e60e54b3924498137e6..a64586341fbb51617116a13c2d315005e071d46a 100644
--- a/sys/net/application_layer/coap/gcoap.c
+++ b/sys/net/application_layer/coap/gcoap.c
@@ -7,7 +7,7 @@
  */
 
 /**
- * @ingroup     net_gnrc_coap
+ * @ingroup     net_gcoap
  * @{
  *
  * @file
@@ -19,7 +19,7 @@
  */
 
 #include <errno.h>
-#include "net/gnrc/coap.h"
+#include "net/gcoap.h"
 #include "random.h"
 #include "thread.h"
 
@@ -31,14 +31,11 @@
 
 /* Internal functions */
 static void *_event_loop(void *arg);
-static int _register_port(gnrc_netreg_entry_t *netreg_port, uint16_t port);
-static void _receive(gnrc_pktsnip_t *pkt, ipv6_addr_t *src, uint16_t port);
-static size_t _send(gnrc_pktsnip_t *coap_snip, ipv6_addr_t *addr, uint16_t port);
+static void _listen(sock_udp_t *sock);
 static ssize_t _well_known_core_handler(coap_pkt_t* pdu, uint8_t *buf, size_t len);
 static ssize_t _write_options(coap_pkt_t *pdu, uint8_t *buf, size_t len);
 static size_t _handle_req(coap_pkt_t *pdu, uint8_t *buf, size_t len);
 static ssize_t _finish_pdu(coap_pkt_t *pdu, uint8_t *buf, size_t len);
-static size_t _send_buf( uint8_t *buf, size_t len, ipv6_addr_t *src, uint16_t port);
 static void _expire_request(gcoap_request_memo_t *memo);
 static void _find_req_memo(gcoap_request_memo_t **memo_ptr, coap_pkt_t *pdu,
                                                             uint8_t *buf, size_t len);
@@ -55,97 +52,91 @@ static gcoap_listener_t _default_listener = {
 };
 
 static gcoap_state_t _coap_state = {
-    .netreg_port = GNRC_NETREG_ENTRY_INIT_PID(0, KERNEL_PID_UNDEF),
     .listeners   = &_default_listener,
 };
 
 static kernel_pid_t _pid = KERNEL_PID_UNDEF;
 static char _msg_stack[GCOAP_STACK_SIZE];
+static sock_udp_t _sock;
 
 
 /* Event/Message loop for gcoap _pid thread. */
 static void *_event_loop(void *arg)
 {
     msg_t msg_rcvd, msg_queue[GCOAP_MSG_QUEUE_SIZE];
-    gnrc_pktsnip_t *pkt, *udp_snip, *ipv6_snip;
-    ipv6_addr_t *src_addr;
-    uint16_t port;
-
     (void)arg;
-    msg_init_queue(msg_queue, GCOAP_MSG_QUEUE_SIZE);
-
-    while (1) {
-        msg_receive(&msg_rcvd);
 
-        switch (msg_rcvd.type) {
-            case GNRC_NETAPI_MSG_TYPE_RCV:
-                /* find client from UDP destination port */
-                DEBUG("coap: GNRC_NETAPI_MSG_TYPE_RCV\n");
-                pkt = (gnrc_pktsnip_t *)msg_rcvd.content.ptr;
-                if (pkt->type != GNRC_NETTYPE_UNDEF) {
-                    gnrc_pktbuf_release(pkt);
-                    break;
-                }
-                udp_snip = pkt->next;
-                if (udp_snip->type != GNRC_NETTYPE_UDP) {
-                    gnrc_pktbuf_release(pkt);
-                    break;
-                }
-
-                /* read source port and address */
-                port = byteorder_ntohs(((udp_hdr_t *)udp_snip->data)->src_port);
+    msg_init_queue(msg_queue, GCOAP_MSG_QUEUE_SIZE);
 
-                LL_SEARCH_SCALAR(udp_snip, ipv6_snip, type, GNRC_NETTYPE_IPV6);
-                assert(ipv6_snip != NULL);
-                src_addr = &((ipv6_hdr_t *)ipv6_snip->data)->src;
+    sock_udp_ep_t local;
+    memset(&local, 0, sizeof(sock_udp_ep_t));
+    local.family = AF_INET6;
+    local.netif  = SOCK_ADDR_ANY_NETIF;
+    local.port   = GCOAP_PORT;
 
-                _receive(pkt, src_addr, port);
-                break;
+    int res = sock_udp_create(&_sock, &local, NULL, 0);
+    if (res < 0) {
+        DEBUG("gcoap: cannot create sock: %d\n", res);
+        return 0;
+    }
 
-            case GCOAP_NETAPI_MSG_TYPE_TIMEOUT:
-                _expire_request((gcoap_request_memo_t *)msg_rcvd.content.ptr);
-                break;
+    while(1) {
+        res = msg_try_receive(&msg_rcvd);
 
-            default:
-                break;
+        if (res > 0) {
+            switch (msg_rcvd.type) {
+                case GCOAP_MSG_TYPE_TIMEOUT:
+                    _expire_request((gcoap_request_memo_t *)msg_rcvd.content.ptr);
+                    break;
+                case GCOAP_MSG_TYPE_INTR:
+                    /* next _listen() timeout will account for open requests */
+                    break;
+                default:
+                    break;
+            }
         }
+
+        _listen(&_sock);
     }
+
     return 0;
 }
 
-/* Handles incoming network IPC message. */
-static void _receive(gnrc_pktsnip_t *pkt, ipv6_addr_t *src, uint16_t port)
+/* Listen for an incoming CoAP message. */
+static void _listen(sock_udp_t *sock)
 {
     coap_pkt_t pdu;
     uint8_t buf[GCOAP_PDU_BUF_SIZE];
-    size_t pdu_len = 0;
+    sock_udp_ep_t remote;
     gcoap_request_memo_t *memo = NULL;
+    uint8_t open_reqs;
 
-    /* If too big, handle below based on request vs. response */
-    size_t pkt_size = (pkt->size > sizeof(buf))
-                            ? sizeof(buf) : pkt->size;
+    gcoap_op_state(&open_reqs);
 
-    /* Copy request into temporary buffer, and parse it as CoAP. */
-    memcpy(buf, pkt->data, pkt_size);
+    ssize_t res = sock_udp_recv(sock, buf, sizeof(buf),
+                                open_reqs > 0 ? GCOAP_RECV_TIMEOUT : SOCK_NO_TIMEOUT,
+                                &remote);
+    if (res <= 0) {
+#if ENABLE_DEBUG
+        if (res < 0 && res != -ETIMEDOUT) {
+            DEBUG("gcoap: udp recv failure: %d\n", res);
+        }
+#endif
+        return;
+    }
 
-    int result = coap_parse(&pdu, buf, pkt_size);
-    if (result < 0) {
-        DEBUG("gcoap: parse failure: %d\n", result);
+    res = coap_parse(&pdu, buf, res);
+    if (res < 0) {
+        DEBUG("gcoap: parse failure: %d\n", res);
         /* If a response, can't clear memo, but it will timeout later. */
-        goto exit;
+        return;
     }
 
     /* incoming request */
     if (coap_get_code_class(&pdu) == COAP_CLASS_REQ) {
-        if (pkt->size > sizeof(buf)) {
-            DEBUG("gcoap: request too big: %u\n", pkt->size);
-            pdu_len = gcoap_response(&pdu, buf, sizeof(buf),
-                                           COAP_CODE_REQUEST_ENTITY_TOO_LARGE);
-        } else {
-            pdu_len = _handle_req(&pdu, buf, sizeof(buf));
-        }
+        size_t pdu_len = _handle_req(&pdu, buf, sizeof(buf));
         if (pdu_len > 0) {
-            _send_buf(buf, pdu_len, src, port);
+            sock_udp_send(sock, buf, pdu_len, &remote);
         }
     }
     /* incoming response */
@@ -153,17 +144,10 @@ static void _receive(gnrc_pktsnip_t *pkt, ipv6_addr_t *src, uint16_t port)
         _find_req_memo(&memo, &pdu, buf, sizeof(buf));
         if (memo) {
             xtimer_remove(&memo->response_timer);
-            if (pkt->size > sizeof(buf)) {
-                memo->state = GCOAP_MEMO_ERR;
-                DEBUG("gcoap: response too big: %u\n", pkt->size);
-            }
             memo->resp_handler(memo->state, &pdu);
             memo->state = GCOAP_MEMO_UNUSED;
         }
     }
-
-exit:
-    gnrc_pktbuf_release(pkt);
 }
 
 /*
@@ -296,77 +280,6 @@ static void _expire_request(gcoap_request_memo_t *memo)
     }
 }
 
-/* Registers receive/send port with GNRC registry. */
-static int _register_port(gnrc_netreg_entry_t *netreg_port, uint16_t port)
-{
-    if (!gnrc_netreg_lookup(GNRC_NETTYPE_UDP, port)) {
-        gnrc_netreg_entry_init_pid(netreg_port, port, _pid);
-        gnrc_netreg_register(GNRC_NETTYPE_UDP, netreg_port);
-        DEBUG("coap: registered UDP port %" PRIu32 "\n",
-               netreg_port->demux_ctx);
-        return 0;
-    }
-    else {
-        return -EINVAL;
-    }
-}
-
-/*
- * Sends a CoAP message to the provided host/port.
- *
- * @return Length of the packet
- * @return 0 if cannot send
- */
-static size_t _send(gnrc_pktsnip_t *coap_snip, ipv6_addr_t *addr, uint16_t port)
-{
-    gnrc_pktsnip_t *udp, *ip;
-    size_t pktlen;
-
-    /* allocate UDP header */
-    udp = gnrc_udp_hdr_build(coap_snip, (uint16_t)_coap_state.netreg_port.demux_ctx,
-                                                                               port);
-    if (udp == NULL) {
-        DEBUG("gcoap: unable to allocate UDP header\n");
-        gnrc_pktbuf_release(coap_snip);
-        return 0;
-    }
-    /* allocate IPv6 header */
-    ip = gnrc_ipv6_hdr_build(udp, NULL, addr);
-    if (ip == NULL) {
-        DEBUG("gcoap: unable to allocate IPv6 header\n");
-        gnrc_pktbuf_release(udp);
-        return 0;
-    }
-    pktlen = gnrc_pkt_len(ip);          /* count length now; snips deallocated after send */
-
-    /* send message */
-    if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, ip)) {
-        DEBUG("gcoap: unable to locate UDP thread\n");
-        gnrc_pktbuf_release(ip);
-        return 0;
-    }
-    return pktlen;
-}
-
-/*
- * Copies the request/response buffer to a pktsnip and sends it.
- *
- * @return Length of the packet
- * @return 0 if cannot send
- */
-static size_t _send_buf(uint8_t *buf, size_t len, ipv6_addr_t *src, uint16_t port)
-{
-    gnrc_pktsnip_t *snip;
-
-    snip = gnrc_pktbuf_add(NULL, NULL, len, GNRC_NETTYPE_UNDEF);
-    if (!snip) {
-        return 0;
-    }
-    memcpy(snip->data, buf, len);
-
-    return _send(snip, src, port);
-}
-
 /*
  * Handler for /.well-known/core. Lists registered handlers, except for
  * /.well-known/core itself.
@@ -456,10 +369,6 @@ kernel_pid_t gcoap_init(void)
     _pid = thread_create(_msg_stack, sizeof(_msg_stack), THREAD_PRIORITY_MAIN - 1,
                             THREAD_CREATE_STACKTEST, _event_loop, NULL, "coap");
 
-    /* must establish pid first */
-    if (_register_port(&_coap_state.netreg_port, GCOAP_PORT) < 0) {
-        return -EINVAL;
-    }
     /* Blank list of open requests so we know if an entry is available. */
     memset(&_coap_state.open_reqs[0], 0, sizeof(_coap_state.open_reqs));
     /* randomize initial value */
@@ -529,8 +438,23 @@ ssize_t gcoap_finish(coap_pkt_t *pdu, size_t payload_len, unsigned format)
 
 size_t gcoap_req_send(uint8_t *buf, size_t len, ipv6_addr_t *addr, uint16_t port,
                                                  gcoap_resp_handler_t resp_handler)
+{
+    sock_udp_ep_t remote;
+
+    remote.family = AF_INET6;
+    remote.netif  = SOCK_ADDR_ANY_NETIF;
+    remote.port   = port;
+
+    memcpy(&remote.addr.ipv6[0], &addr->u8[0], sizeof(addr->u8));
+
+    return gcoap_req_send2(buf, len, &remote, resp_handler);
+}
+
+size_t gcoap_req_send2(uint8_t *buf, size_t len, sock_udp_ep_t *remote,
+                                                 gcoap_resp_handler_t resp_handler)
 {
     gcoap_request_memo_t *memo = NULL;
+    assert(remote != NULL);
     assert(resp_handler != NULL);
 
     /* Find empty slot in list of open requests. */
@@ -545,16 +469,28 @@ size_t gcoap_req_send(uint8_t *buf, size_t len, ipv6_addr_t *addr, uint16_t port
         memcpy(&memo->hdr_buf[0], buf, GCOAP_HEADER_MAXLEN);
         memo->resp_handler = resp_handler;
 
-        size_t res = _send_buf(buf, len, addr, port);
+        size_t res = sock_udp_send(&_sock, buf, len, remote);
+
         if (res && (GCOAP_NON_TIMEOUT > 0)) {
-            /* start response wait timer */
-            memo->timeout_msg.type        = GCOAP_NETAPI_MSG_TYPE_TIMEOUT;
-            memo->timeout_msg.content.ptr = (char *)memo;
-            xtimer_set_msg(&memo->response_timer, GCOAP_NON_TIMEOUT,
-                                                  &memo->timeout_msg, _pid);
+            /* interrupt sock listening (to set a listen timeout) */
+            msg_t mbox_msg;
+            mbox_msg.type          = GCOAP_MSG_TYPE_INTR;
+            mbox_msg.content.value = 0;
+            if (mbox_try_put(&_sock.reg.mbox, &mbox_msg)) {
+                /* start response wait timer */
+                memo->timeout_msg.type        = GCOAP_MSG_TYPE_TIMEOUT;
+                memo->timeout_msg.content.ptr = (char *)memo;
+                xtimer_set_msg(&memo->response_timer, GCOAP_NON_TIMEOUT,
+                                                      &memo->timeout_msg, _pid);
+            }
+            else {
+                memo->state = GCOAP_MEMO_UNUSED;
+                DEBUG("gcoap: can't wake up mbox; no timeout for msg\n");
+            }
         }
         else if (!res) {
             memo->state = GCOAP_MEMO_UNUSED;
+            DEBUG("gcoap: sock send failed: %d\n", res);
         }
         return res;
     } else {