diff --git a/doc/source/index.rst b/doc/source/index.rst
index c6400bab46..cfd6bfafaa 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -61,10 +61,20 @@ Brief guides to areas of interest and importance when developing Horizon.
intro
quickstart
- topics/tutorial
contributing
testing
- plugins
+ plugin_registry
+
+Tutorials
+---------
+
+Detailed tutorials to help you get started.
+
+.. toctree::
+ :maxdepth: 1
+
+ tutorials/plugin
+ tutorials/dashboard
Topic Guides
------------
diff --git a/doc/source/plugins.rst b/doc/source/plugin_registry.rst
similarity index 100%
rename from doc/source/plugins.rst
rename to doc/source/plugin_registry.rst
diff --git a/doc/source/topics/angularjs.rst b/doc/source/topics/angularjs.rst
index 6539bd62a1..62ae72f556 100644
--- a/doc/source/topics/angularjs.rst
+++ b/doc/source/topics/angularjs.rst
@@ -329,7 +329,7 @@ To manually add files, add the following arrays and file paths to the enabled fi
Plugins
-------
-Add a new panel/ panel group/ dashboard (See :doc:`tutorial`). JavaScript file
+Add a new panel/ panel group/ dashboard (See :doc:`/tutorials/dashboard`). JavaScript file
inclusion is the same as the Upstream process.
To include external stylesheets, you must ensure that ``ADD_SCSS_FILES`` is
diff --git a/doc/source/topics/table_actions.rst b/doc/source/topics/table_actions.rst
index 411dd57a9f..160e12c3f1 100644
--- a/doc/source/topics/table_actions.rst
+++ b/doc/source/topics/table_actions.rst
@@ -6,7 +6,7 @@ This tutorial covers how to add a more complex action to a table, one that requi
an action and form definitions, as well as changes to the view, urls, and table.
This tutorial assumes you have already completed :doc:`Building a Dashboard using
-Horizon `. If not, please do so now as we will be modifying the
+Horizon `. If not, please do so now as we will be modifying the
files created there.
This action will create a snapshot of the instance. When the action is taken,
diff --git a/doc/source/topics/tutorial.rst b/doc/source/tutorials/dashboard.rst
similarity index 100%
rename from doc/source/topics/tutorial.rst
rename to doc/source/tutorials/dashboard.rst
diff --git a/doc/source/tutorials/plugin.rst b/doc/source/tutorials/plugin.rst
new file mode 100644
index 0000000000..000f42e722
--- /dev/null
+++ b/doc/source/tutorials/plugin.rst
@@ -0,0 +1,445 @@
+==============
+Horizon Plugin
+==============
+
+Why should I package my code as a plugin?
+=========================================
+
+We highly encourage that you write and maintain your code using our plugin
+architecture. A plugin by definition means the ability to be connected. In
+practical terms, plugins are a way to extend and add to the functionality that
+already exists. You can control its content and progress at a rate independent
+of Horizon. If you write and package your code as a plugin, it will continue to
+work in future releases.
+
+Writing your code as a plugin also modularizes your code making it easier to
+translate and test. This also makes it easier for deployers to consume your code
+allowing selective enablement of features. We are currently using this pattern
+internally for our dashboards.
+
+Creating the Plugin
+===================
+
+This tutorial assumes you have a basic understanding of Python, HTML,
+JavaScript. Knowledge of AngularJS is optional but recommended if you are
+attempting to create an Angular plugin.
+
+Types of Plugins that add content
+---------------------------------
+
+The file structure for your plugin type will be different depending on your
+needs. Your plugin can be categorized into two types:
+
+* Plugins that create new panels or dashboards
+* Plugins that modify existing workflows, actions, etc... (Angular only)
+
+We will cover the basics of working with panels for both Python and Angular.
+If you are interested in creating a new panel, follow the steps below.
+
+.. Note ::
+
+ This tutorial shows you how to create a new panel. If you are interested in
+ creating a new dashboard plugin, use the file structure from
+ :doc:`Tutorial: Building a Dashboard using Horizon `
+ instead.
+
+File Structure
+--------------
+Below is a skeleton of what your plugin should look like.::
+
+ myplugin
+ │
+ ├── myplugin
+ │ ├── __init__.py
+ │ │
+ │ ├── enabled
+ │ │ └──_31000_myplugin.py
+ │ │
+ │ ├── api
+ │ │ ├──__init__.py
+ │ │ ├── my_rest_api.py
+ │ │ └── myservice.py
+ │ │
+ │ ├── content
+ │ │ ├──__init__.py
+ │ │ └── mypanel
+ │ │ ├── __init__.py
+ │ │ ├── panel.py
+ │ │ ├── tests.py
+ │ │ ├── urls.py
+ │ │ ├── views.py
+ │ │ └── templates
+ │ │ └── mypanel
+ │ │ └── index.html
+ │ │
+ │ └── static
+ │ └── dashboard
+ │ └── identity
+ │ └── myplugin
+ │ └── mypanel
+ │ ├── mypanel.html
+ │ ├── mypanel.js
+ │ └── mypanel.scss
+ ├── setup.py
+ ├── setup.cfg
+ ├── LICENSE
+ ├── MANIFEST.in
+ └── README.rst
+
+If you are creating a Python plugin, you may ignore the ``static`` folder. Most
+of the classes you need are provided for in Python. If you intend on adding
+custom front-end logic, you will need to include additional JavaScript here.
+
+An AngularJS plugin is a collection of JavaScript files or static resources.
+Because it runs entirely in your browser, we need to place all of our static
+resources inside the ``static`` folder. This ensures that the Django static
+collector picks it up and distributes it to the browser correctly.
+
+The Enabled File
+----------------
+
+The enabled folder contains the configuration file(s) that registers your
+plugin with Horizon. The file is prefixed with an alpha-numberic string that
+determines the load order of your plugin. For more information on what you can
+include in this file, see pluggable settings in
+:doc:`Settings and Configuration `
+
+_31000_myplugin.py::
+
+ # The name of the panel to be added to HORIZON_CONFIG. Required.
+ PANEL = 'mypanel'
+
+ # The name of the dashboard the PANEL associated with. Required.
+ PANEL_DASHBOARD = 'identity'
+
+ # Python panel class of the PANEL to be added.
+ ADD_PANEL = 'myplugin.content.mypanel.panel.MyPanel'
+
+ # A list of applications to be prepended to INSTALLED_APPS
+ ADD_INSTALLED_APPS = ['myplugin']
+
+ # A list of AngularJS modules to be loaded when Angular bootstraps.
+ ADD_ANGULAR_MODULES = ['horizon.dashboard.identity.myplugin.mypanel']
+
+ # Automatically discover static resources in installed apps
+ AUTO_DISCOVER_STATIC_FILES = True
+
+ # A list of scss files to be included in the compressed set of files
+ ADD_SCSS_FILES = ['dashboard/identity/myplugin/myplugin.scss']
+
+my_rest_api.py
+--------------
+
+This file will likely be necessary if creating a plugin using Angular. Your
+plugin will need to communicate with a new service or require new interactions
+with a service already supported by Horizon. In this particular example, the
+plugin will augment the support for the already supported Identity service,
+Keystone. This file serves to define new REST interfaces for the plugin's
+clientside to communicate with Horizon. Typically, the REST interfaces here
+make calls into ``myservice.py``.
+
+This file is unnecessary in a purely Django based plugin, or if your Angular
+based plugin is relying on CORS support in the desired service. For more
+information on CORS, see
+`http://docs.openstack.org/admin-guide-cloud/cross_project_cors.html`
+
+myservice.py
+------------
+
+This file will likely be necessary if creating a Django or Angular driven
+plugin. This file is intended to act as a convenient location for interacting
+with the new service this plugin is supporting. While interactions with the
+service can be handled in the ``views.py``, isolating the logic is an
+established pattern in Horizon.
+
+panel.py
+--------
+
+We define a panel where our plugin's content will reside in. This is currently a
+neccessity even for Angular plugins. The slug is the panel's unique identifier
+and is often use as part of the URL. Make sure that it matches what you have in
+your enabled file.::
+
+ from django.utils.translation import ugettext_lazy as _
+ import horizon
+
+
+ class MyPanel(horizon.Panel):
+ name = _("My Panel")
+ slug = "mypanel"
+
+tests.py
+--------
+
+Write some tests for the Django portion of your plugin and place them here.
+
+urls.py
+-------
+
+Now that we have a panel, we need to provide a URL so that users can visit our
+new panel! This URL generally will point to a view.::
+
+ from django.conf.urls import patterns
+ from django.conf.urls import url
+
+ from myplugin.content.mypanel import views
+
+ urlpatterns = patterns(
+ url(r'^$', views.IndexView.as_view(), name='index'),
+ )
+
+views.py
+--------
+
+Because rendering is done client-side, all our view needs is to reference some
+HTML page. If you are writing a Python plugin, this view can be much more
+complex. Refer to the topic guides for more details.::
+
+ from django.views import generic
+
+
+ class IndexView(generic.TemplateView):
+ template_name = 'identity/mypanel/index.html'
+
+index.html
+----------
+
+The index HTML is where rendering occurs. In this example, we are only using
+Django. If you are interested in using Angular directives instead, read the
+AngularJS section below.::
+
+ {% extends 'base.html' %}
+ {% load i18n %}
+ {% block title %}{% trans "My plugin" %}{% endblock %}
+
+ {% block page_header %}
+ {% include "horizon/common/_domain_page_header.html"
+ with title=_("My Panel") %}
+ {% endblock page_header %}
+
+ {% block main %}
+ Hello world!
+ {% endblock %}
+
+At this point, you have a very basic plugin. Note that new templates are
+required to extend base.html. Including base.html is important for a number of
+reasons. It is the template that contains all of your static resources along
+with any functionality external to your panel (things like navigation, context
+selection, etc...). As of this moment, this is also true for Angular plugins.
+
+MANIFEST.in
+-----------
+This file is responsible for listing the paths you want included in your tar.::
+
+ include setup.py
+
+ recursive-include myplugin *.js *.html *.scss
+
+
+setup.py
+--------
+::
+
+ # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
+ import setuptools
+
+ # In python < 2.7.4, a lazy loading of package `pbr` will break
+ # setuptools if some other modules registered functions in `atexit`.
+ # solution from: http://bugs.python.org/issue15881#msg170215
+ try:
+ import multiprocessing # noqa
+ except ImportError:
+ pass
+
+ setuptools.setup(
+ setup_requires=['pbr>=1.8'],
+ pbr=True)
+
+setup.cfg
+--------
+::
+
+ [metadata]
+ name = myplugin
+ version = 0.0.1
+ summary = A panel plugin for OpenStack Dashboard
+ description-file =
+ README.rst
+ author = myname
+ author_email = myemail
+ home-page = http://www.openstack.org/
+ classifiers = [
+ Environment :: OpenStack
+ Framework :: Django
+ Intended Audience :: Developers
+ Intended Audience :: System Administrators
+ License :: OSI Approved :: Apache Software License
+ Operating System :: POSIX :: Linux
+ Programming Language :: Python
+ Programming Language :: Python :: 2
+ Programming Language :: Python :: 2.7
+ Programming Language :: Python :: 3.4
+
+ [files]
+ packages =
+ myplugin
+
+AngularJS Plugin
+================
+
+If you have no plans to add AngularJS to your plugin, you may skip this section.
+In the tutorial below, we will show you how to customize your panel using
+Angular.
+
+index.html
+----------
+
+The index HTML is where rendering occurs and serves as an entry point for
+Angular. This is where we start to diverge from the traditional Python plugin.
+In this example, we use a Django template as the glue to our Angular template.
+Why are we going through a Django template for an Angular plugin? Long story
+short, ``base.html`` contains the navigation piece that we still need for each
+panel.
+
+::
+
+ {% extends 'base.html' %}
+ {% load i18n %}
+ {% block title %}{% trans "My panel" %}{% endblock %}
+
+ {% block page_header %}
+