diff --git a/birdy/client/__init__.py b/birdy/client/__init__.py
index b48b844..2d570c3 100644
--- a/birdy/client/__init__.py
+++ b/birdy/client/__init__.py
@@ -54,4 +54,4 @@ If a WPS server with a simple `hello` process is running on the local host on po
 
 """
 
-from .base import WPSClient
+from .base import WPSClient, nb_form
diff --git a/birdy/client/base.py b/birdy/client/base.py
index 507d5fb..1108a79 100644
--- a/birdy/client/base.py
+++ b/birdy/client/base.py
@@ -292,11 +292,12 @@ class WPSClient(object):
         else:
             self.logger.info("{} failed.".format(execution.process.identifier))
 
-    def interact(self, pid):
-        """Return a Notebook form to enter input values and launch process."""
-        if self._notebook:
-            return notebook.interact(
-                func=getattr(self, pid),
-                inputs=self._inputs[pid].items())
-        else:
-            return None
+
+def nb_form(wps, pid):
+    """Return a Notebook form to enter input values and launch process."""
+    if wps._notebook:
+        return notebook.interact(
+            func=getattr(wps, sanitize(pid)),
+            inputs=wps._inputs[pid].items())
+    else:
+        return None
diff --git a/birdy/client/notebook.py b/birdy/client/notebook.py
index 1f5b705..403c205 100644
--- a/birdy/client/notebook.py
+++ b/birdy/client/notebook.py
@@ -4,6 +4,7 @@ from owslib.wps import Input
 from . import utils
 from birdy.dependencies import ipywidgets as widgets
 from birdy.dependencies import IPython
+from birdy.utils import sanitize
 
 
 def is_notebook():
@@ -24,10 +25,14 @@ def is_notebook():
 
 
 def interact(func, inputs):
-    """Return a Notebook form to enter input values and launch process."""
-    ws = {key: input2widget(inpt) for key, inpt in inputs}
+    """Return a Notebook form to enter input values and launch process.
+
+    The output is stored in the `widget.result` attribute of the response.
+    """
+    ws = {sanitize(key): input2widget(inpt) for key, inpt in inputs}
     out = widgets.interact_manual(func, **ws)
     out.widget.children[-2].description = 'Launch process'
+    # IPython.display.display(out)
     return out
 
 
diff --git a/notebooks/Interactive.ipynb b/notebooks/Interactive.ipynb
new file mode 100644
index 0000000..84a6a9f
--- /dev/null
+++ b/notebooks/Interactive.ipynb
@@ -0,0 +1,72 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from birdy import WPSClient\n",
+    "from birdy.client import nb_form\n",
+    "emu = WPSClient(url='http://localhost:5000/wps')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "resp = nb_form(emu, 'binaryoperatorfornumbers')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "resp.widget.result.get(asobj=True)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nb_form(emu, 'non.py-id')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "nb_form(emu, 'chomsky')"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.3"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/notebooks/emu-example.ipynb b/notebooks/emu-example.ipynb
index 80d4d55..8663c57 100644
--- a/notebooks/emu-example.ipynb
+++ b/notebooks/emu-example.ipynb
@@ -213,7 +213,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.2"
+   "version": "3.7.3"
   }
  },
  "nbformat": 4,

diff --git a/tests/test_client.py b/tests/test_client.py
index 0658b80..922601d 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -5,7 +5,7 @@ import json
 # from owslib import crs
 
 from pathlib import Path
-from birdy.client import converters
+from birdy.client import converters, nb_form
 from birdy.client.utils import is_embedded_in_request
 from birdy import WPSClient
 
@@ -60,11 +60,11 @@ def test_wps_client_single_output(wps):
 
 
 @pytest.mark.online
-def test_wps_interact(wps):
+def test_wps_nb_form(wps):
     for pid in wps._processes.keys():
         if pid in ['bbox', ]:  # Unsupported
             continue
-        wps.interact(pid)
+        nb_form(wps, pid)
 
 
 @pytest.mark.online
diff --git a/tests/test_notebook.py b/tests/test_notebook.py
index 5e1a5d6..14eaeda 100644
--- a/tests/test_notebook.py
+++ b/tests/test_notebook.py
@@ -2,5 +2,5 @@ from birdy.client import notebook
 
 
 def test_is_notebook():
-    # we excpect True or False but no exception
+    # we expect True or False but no exception
     notebook.is_notebook()

Interact shadows the process names
## Description

I think it's a problem if the `interact` method is in the same scope as the actual WPS processes. 
Ideas ?
Could we attach the method to the function ?

```wps.inout.interact()```
tests/test_client.py::test_converter tests/test_client.py::test_jsonconverter tests/test_client.py::TestIsEmbedded::test_string tests/test_client.py::TestIsEmbedded::test_file_like tests/test_client.py::TestIsEmbedded::test_local_fn tests/test_client.py::TestIsEmbedded::test_local_path tests/test_client.py::TestIsEmbedded::test_local_uri tests/test_client.py::TestIsEmbedded::test_url tests/test_notebook.py::test_is_notebook