diff --git a/docs/api/DesktopAgent.md b/docs/api/DesktopAgent.md
index 897b44579..867390a7d 100644
--- a/docs/api/DesktopAgent.md
+++ b/docs/api/DesktopAgent.md
@@ -12,6 +12,21 @@ A Desktop Agent can be connected to one or more App Directories and will use dir
 
 ## Methods
 
+### `broadcast`
+```typescript
+broadcast(context: Context): void;
+```
+
+Publishes context to other apps on the desktop.
+
+#### Examples
+```javascript
+fdc3.broadcast(context);
+```
+#### See also
+* [addContextListener](#addcontextlistener)
+
+
 ### `open`
 
 ```typescript
@@ -108,19 +123,6 @@ A promise resolving to all the intents, their metadata and metadata about the ap
 #### See also
    * [`ResolveError`](Errors#resolveerror)
 
-### `broadcast`
-```typescript
-broadcast(context: Context): void;
-```
-
-Publishes context to other apps on the desktop.
-
-#### Examples
-```javascript
-agent.broadcast(context);
-```
-#### See also
-* [addContextListener](#addcontextlistener)
 
 ### `raiseIntent`
 
@@ -138,6 +140,66 @@ agent.raiseIntent("StartChat", newContext, intentR.source);
 #### See also
 * [`IntentResolution`](#intentresolution)
 
+
+
+
+### `getOrCreateChannel`
+```typescript
+getOrCreateChannel(channelId: string): Promise<Channel>;
+```
+Returns a Channel object for the specified channel, creating it (as an _App_ channel) - if it does not exist.
+ `Error` with a string from the [`ChannelError`](Errors#channelerror) enumeration if channel could not be created or access was denied.
+
+#### Examples
+```javascript
+
+try {
+  let myChannel = await fdc3.getOrCreateChannel("myChannel");
+  myChannel.addContextListener(listener);
+}
+catch (err){
+  //app could not register the channel
+}
+
+```
+#### See also
+*[`Channel`](#channel)
+
+
+### `getSystemChannels`
+```typescript
+getSystemChannels() : Promise<Array<Channel>>;
+```
+Retrieves a list of the System channels available for the app to join
+
+#### See also
+*[`Channel`](#channel)
+
+### `joinChannel`
+```typescript
+joinChannel(channelId: string) : Promise<void>;
+```
+Joins the app to the specified channel.
+If an app is joined to a channel, all _fdc3.broadcast_ calls will go to the channel, and all listeners assigned via _fdc3.addContextListener_ will listen on the channel.
+An app can only be joined to one channel at a time.
+Rejects with error if the channel is unavailable or the join request is denied.
+ `Error` with a string from the [`ChannelError`](Errors#channelerror) enumeration.
+
+#### Examples
+```javascript
+  //get all system channels
+  const channels = await fdc3.getSystemChannels();
+
+  //create UI to pick from the system channels
+
+  //join the channel on selection
+  fdc3.joinChannel(selectedChannel.id);
+
+```
+#### See also
+*[`getSystemChannels`](#getSystemChannels)
+
+
 ### `addIntentListener`
 ```typescript
 addIntentListener(intent: string, handler: (context: Context) => void): Listener;
@@ -157,6 +219,9 @@ Adds a listener for incoming context broadcast from the Desktop Agent.
 * [`Listener`](#listener)
 * [`Context`](Context)
 
+
+
+
 ## Return Types
 
 ### `AppIntent`
@@ -234,3 +299,37 @@ The `unsubscribe` method on the listener object allows the application to cancel
 * [`DesktopAgent.addIntentListener`](#addintentlistener)
 * [`DesktopAgent.addContextListener`](#addcontextlistener)
 
+### `Channel`
+```typescript
+ Channel {
+  id: string;
+  type: string;
+  displayMetadata?: DisplayMetadata;
+  broadcast(context: Context): Promise<void>;
+  getCurrentContext(): Promise<Context|null>;
+  addBroadcastListener(listener: (event: {channel: Channel; context: Context}) => void): DesktopAgent.Listener;
+}
+```
+Object representing a context channel.
+* __id__ uniquely identifies the channel. It is either assigned by the desktop agent (system channel) or defined by an application (app channel)
+* __type__ may be _system_ or _app_
+* __dispalyMetadata__   DisplayMetaData can be used to provide display hints for channels intended to be visualized and selectable by end users.
+* __broadcast__  Broadcasts a context on the channel. This function can be used without first joining the channel, allowing applications to broadcast on channels that they aren't a member of.  `Error` with a string from the [`ChannelError`](Errors#channelerror) enumeration.
+* __getCurrentContext__ Returns the last context that was broadcast on the channel. If no context has been set on the channel, this will return `null`.  `Error` with a string from the [`ChannelError`](Errors#channelerror) enumeration.
+* __addBroadcastListener__  Event that is fired whenever a window broadcasts on this channel. The `channel` property within the event will always be this channel instance.  `Error` with a string from the [`ChannelError`](Errors#channelerror) enumeration.
+
+#### See also
+[`DisplayMetadata`](#DisplayMetadata)
+
+### `DisplayMetadata`
+```typescript
+DisplayMetadata{
+  name?: string;
+  color?: string;
+  glyph?: string;
+}
+```
+A desktop agent (typically for _system_ channels) may want to provide additional information about how a channel can be represented in a UI.  A common use case is for color linking.
+* __name__ The display name for the channel
+* __color__ A name, hex, rgba, etc that should be associated within this channel when displaying this channel in a UI
+* __glyph__  A URL of an image that can be used to display this channel
diff --git a/docs/api/Errors.md b/docs/api/Errors.md
index 06b4616aa..27b34c3b5 100644
--- a/docs/api/Errors.md
+++ b/docs/api/Errors.md
@@ -36,4 +36,15 @@ Contains constants representing the errors that can be encountered when calling
 
 #### See also
 * [`DesktopAgent.findIntent`](DesktopAgent#findintent)
-* [`DesktopAgent.findIntentsByContext`](DesktopAgent#findintentsbycontext)
\ No newline at end of file
+* [`DesktopAgent.findIntentsByContext`](DesktopAgent#findintentsbycontext)
+
+## `ChannelError`
+
+```typescript
+enum ChannelError {
+  NoChannelFound = "NoChannelFound",
+  AccessDenied = "AccessDenied",
+  CreationFailed = "CreationFailed"
+}
+```
+Contains constants representing the errors that can be encountered when calling channels using the [`joinChannel`](DesktopAgent#joinchannel) or [`getOrCreateChannel`](DesktopAgent#getorcreatechannel) methods, or the [`getCurrentContext`](DesktopAgent#channel), [`broadcast`](DesktopAgent#channel) or [`addBroadcastListener`](DesktopAgent#channel) methods on the Channel object.
diff --git a/docs/api/api-spec.md b/docs/api/api-spec.md
index 4d9d588b5..8dac3cf61 100644
--- a/docs/api/api-spec.md
+++ b/docs/api/api-spec.md
@@ -134,31 +134,61 @@ Intents functionality is dependent on resolver functionality to map the intent t
 
 ## Context channels
 
-The context channel api allows a set of apps to share a stateful piece of data between them, and be alerted when it changes.
+Context channels allows a set of apps to share a stateful piece of data between them, and be alerted when it changes.  Use cases for channels include color linking between applications to automate the sharing of context and topic based pub/sub such as theme.
 
-There are two types of channels, which are functionally identical, but have different visibility and discoverability semantics.
+There are two types of channels, which are functionally identical, but have different visibility and discoverability semantics.  
 
 1. The 'system' ones, which have a well understood identity. One is called 'default'.
 2. The 'app' ones, which have a transient identity and need to be revealed
 
 The 'system' channels include a 'default' channel which serves as the backwards compatible layer with the 'send/broadcast context' above. There are some reserved channel names for future use. Currently this is just 'global'.
 
+### Joining Channels
+Apps can join channels.  An app can only be joined to one channel at a time.  When an app joins a channel it will automatically recieve the current context for that channel, except if the channel joined is the 'default'.
+
+When an app is joined to a channel, calls to fdc3.broadcast and listeners added through fdc3.addContextListener will be routed to that channel.  If an app is not explicitly joined to a channel, it is on the 'default' channel.  It is up to the desktop agent to determine the behavior of the 'default' channel, and if context is automaticaly broadcast over it or not.
+
+It is possible that a call to join a channel could be rejected.  If for example, the desktop agent wanted to implement controls around what data apps can access.  
+
+### Direct Listening and Broadcast on Channels
+While joining channels automates a lot of the channel behavior for an app, it has the limitation in that an app can belong to only one channel at a time.  Listening and Broadcasting to channels using the _Channel.addBroadcastListener_ and the _Channel.broadcast_ APIs provides an app with fine-grained controls for specific channels.  This is especially useful for working with dynamic _App Channels_.
+
+### Examples
 To find a system channel, one calls
 
-    let allChannels = await channels.getSystemChannels();
-    let myChannel = allChannels.find(c=>???);
+```javascript
+    //returns an array of channels
+    let allChannels = await fdc3.getSystemChannels(); 
+    let redChannel = allChannels.find(c => c.id === 'red');
+```
+#### Joining channels
+
+To join a channel. one calls
 
-To broadcast one calls
+```javascript
+    fdc3.joinChannel(redChannel.id);
+```
+
+Calling _fdc3.broadcast_ will now route context to the joined channel.
+
+#### App Channels
 
-    myChannel.broadcast(context);
+App channels are topics dynamically created by applications connected via FDC3. For example, an app may create a channel to broadcast to others data or status specific to that app.
 
-To subscribe one calls
+To get (or create) a channel reference, then interact with it
 
-    let listener = myChannel.addBroadcastListener((c,e)=>{some code});
+```javascript
+    const appChannel = await fdc3.getOrCreateChannel('my_custom_channel');
+    //get the current context of the channel
+    let current = await appChannel.getCurrentContext();
+    //add a listener
+    appChannel.addBroadcastListener(event => {...});
+    //broadcast to the channel
+    appChannel.broadcast(context);
+
+```
 
-App channels are created and obtained as thus:
 
-    let ac = await channels.getOrCreate("a-channel-name");
     
 ## APIs
 The APIs are defined in TypeScript in [src], with documentation generated in the [docs] folder.
diff --git a/src/api/channel/channel.ts b/src/api/channel/channel.ts
deleted file mode 100644
index e181eaa17..000000000
--- a/src/api/channel/channel.ts
+++ /dev/null
@@ -1,152 +0,0 @@
-type ChannelId = string;
-
-
-/**
- * Object representing a context channel.
- * 
- * All interactions with a context channel happen through the methods on here.
- */
-declare class Channel {
-    /**
-     * Constant that uniquely identifies this channel. Will be generated by the service, and guarenteed to be unique
-     * within the set of channels registered with the service.
-     * 
-     * In the case of `system` channels (see {@link SystemChannel}), these id's _should_ persist across sessions. The 
-     * channel list is defined by the service, but can be overridden by a desktop agent. If the desktop agent keeps 
-     * this list static (which is recommended), then id's will also persist across sessions.
-     */
-    public readonly id: ChannelId;
-
-    /**
-     * Uniquely defines each channel type.
-     * 
-     * See overrides of this class for list of allowed values.
-     */
-    public readonly type: string;
-
-    /**
-     * Broadcasts the given context on this channel. This is equivalent to joining the channel and then calling the 
-     * top-level FDC3 `broadcast` function.
-     * 
-     * Note that this function can be used without first joining the channel, allowing applications to broadcast on
-     * channels that they aren't a member of.
-     * 
-     * This broadcast will be received by all windows that are members of this channel, *except* for the window that
-     * makes the broadcast. This matches the behaviour of the top-level FDC3 `broadcast` function.
-     */
-    public broadcast(context: Context): Promise<void>;
-
-    /**
-     * Returns the last context that was broadcast on this channel. All channels initially have no context, until a 
-     * window is added to the channel and then broadcasts. If there is not yet any context on the channel, this method
-     * will return `null`. The context is also reset back into it's initial context-less state whenever a channel is
-     * cleared of all windows.
-     * 
-     * The context of a channel will be captured regardless of how the context is broadcasted on this channel - whether
-     * using the top-level FDC3 `broadcast` function, or using the channel-level {@link broadcast} function on this 
-     * object.
-     * 
-     * NOTE: Only non-default channels are stateful, for the default channel this method will always return `null`.
-     */
-    public getCurrentContext(): Promise<Context|null>;
-
-    /**
-     * Event that is fired whenever a window broadcasts on this channel.
-     * 
-     * The `channel` property within the event will always be this channel instance.
-     */
-    public addBroadcastListener(listener: (event: {channel: Channel; context: Context}) => void): DesktopAgent.Listener;
-}
-
-/**
- * A system channel will be global enough to have a presence across many apps. This gives us some hints
- * to render them in a standard way. It is assumed it may have other properties too, but if it has these, 
- * this is their meaning.
- */
-declare interface DisplayMetadata{
-    /**
-     * A user-readable name for this channel, e.g: `"Red"`
-     */
-    name?: string;
-
-    /**
-     * The color that should be associated within this channel when displaying this channel in a UI, e.g: `0xFF0000`.
-     */
-    color?: string;
-
-    /**
-     * A URL of an image that can be used to display this channel
-     */
-    glyph?: string;
-}
-
-/**
- * User-facing channels, to display within a colour picker or channel selector component.
- * 
- * This list of channels should be considered fixed by applications - the service will own the list of user channels,
- * making the same list of channels available to all applications, and this list will not change over the lifecycle of
- * the service.
- * 
- * We do not intend to support creation of 'user' channels at runtime, as this then adds considerable complexity when
- * implementing a channel selector component (must now support events, 'create channel' UI, reflowing of channel 
- * list, etc).
- */
-declare class SystemChannel extends Channel {
-    type: 'system';
-
-    /**
-     * SystemChannels may well be selectable by users. Here are the hints on how to see them.
-     */
-    visualIdentity: DisplayMetadata;
-}
-
-
-/**
- * Applications can create custom channels for specialised use-cases. Note that these channels would only be known
- * about to the app that created them. They can be joined by any window, but there would be no way to discover them
- * from the service's API - it would be up to applications to decide how to share the channel ID with other
- * windows/applications.
- */
-declare class AppChannel extends Channel {
-
-
-    // Possibly some additional fields, TBD.
-}
-
-/**
- * Channels API is namespaced under a `channels` object.
- * 
- * ```
- * import {channels} from '???';
- * 
- * channels.getDesktopChannels().then((channels: Channel[]) => {
- *     channels[0].joinChannel();
- * });
- * ```
- * 
- */
-declare module channels {
-    /**
-     * Allows access to the default channel. All windows will start out in this channel, and will remain in that 
-     * channel unless they explicitly {@link Channel.join | joining} another.
-     * 
-     * Applications can leave a {@link DesktopChannel} by re-joining the default channel.
-     */
-    const defaultChannel: Channel;
-
-    /**
-     * 
-     */
-    function getSystemChannels(): Promise<SystemChannel[]>;
-    
-     /**
-     * Returns an app channel with the given identity. Either stands up a new channel or returns an existing channel.
-     * 
-     * It is up to applications to manage how to share knowledge of these custom channels across windows and to manage
-     * channel ownership and lifecycle. 
-     * @param channelId the identity of this channel
-     */
-
-    public static getOrCreate(channelId: ChannelId): Promise<AppChannel>;
-    type: 'app';
-}
diff --git a/src/api/interface.ts b/src/api/interface.ts
index 4ce1411ba..34f45433a 100644
--- a/src/api/interface.ts
+++ b/src/api/interface.ts
@@ -18,6 +18,12 @@ enum ResolveError {
   ResolverTimeout = "ResolverTimeout"
 }
 
+enum ChannelError {
+  NoChannelFound = "NoChannelFound",
+  AccessDenied = "AccessDenied",
+  CreationFailed = "CreationFailed"
+}
+
 /**
 * Intent descriptor
 */
@@ -34,6 +40,7 @@ interface AppIntent {
   apps: Array<AppMetadata>;
 }
 
+
 /**
  * App metadata is Desktop Agent specific - but should support a name property.
  */
@@ -64,6 +71,90 @@ interface Listener {
   unsubscribe();
 }
 
+
+/**
+ * Object representing a context channel.
+ */
+declare class Channel {
+  /**
+   * Constant that uniquely identifies this channel. Will be generated by the service, and guarenteed to be unique
+   * within the set of channels registered with the service.
+   * 
+   * In the case of `system` channels, these id's _should_ persist across sessions. 
+   */
+  public readonly id: string;
+
+  /**
+   * Uniquely defines each channel type.
+   */
+  public readonly type: string;
+
+  /**
+   * Channels may be visualized and selectable by users. DisplayMetaData may be used to provide hints on how to see them.
+   * For app channels, displayMetadata would typically not be present
+   */
+  displayMetadata?: DisplayMetadata;
+
+   /**
+   * Broadcasts the given context on this channel. This is equivalent to joining the channel and then calling the 
+   * top-level FDC3 `broadcast` function.
+   * 
+   * Note that this function can be used without first joining the channel, allowing applications to broadcast on
+   * channels that they aren't a member of.
+   * 
+   * This broadcast will be received by all windows that are members of this channel, *except* for the window that
+   * makes the broadcast. This matches the behaviour of the top-level FDC3 `broadcast` function.
+   * `Error` with a string from the `ChannelError` enumeration.
+   */
+  public broadcast(context: Context): Promise<void>;
+
+  /**
+   * Returns the last context that was broadcast on this channel. All channels initially have no context, until a 
+   * window is added to the channel and then broadcasts. If there is not yet any context on the channel, this method
+   * will return `null`. The context is also reset back into it's initial context-less state whenever a channel is
+   * cleared of all windows.
+   * 
+   * The context of a channel will be captured regardless of how the context is broadcasted on this channel - whether
+   * using the top-level FDC3 `broadcast` function, or using the channel-level {@link broadcast} function on this 
+   * object.
+   * 
+   * NOTE: Only non-default channels are stateful, for the default channel this method will always return `null`.
+   * `Error` with a string from the `ChannelError` enumeration.
+   */
+  public getCurrentContext(): Promise<Context|null>;
+
+  /**
+   * Event that is fired whenever a window broadcasts on this channel.
+   * 
+   * The `channel` property within the event will always be this channel instance.
+   * `Error` with a string from the `ChannelError` enumeration.
+   */
+  public addBroadcastListener(listener: (event: {channel: Channel; context: Context}) => void): Listener;
+}
+
+/**
+* A system channel will be global enough to have a presence across many apps. This gives us some hints
+* to render them in a standard way. It is assumed it may have other properties too, but if it has these, 
+* this is their meaning.
+*/
+declare interface DisplayMetadata{
+  /**
+   * A user-readable name for this channel, e.g: `"Red"`
+   */
+  name?: string;
+
+  /**
+   * The color that should be associated within this channel when displaying this channel in a UI, e.g: `0xFF0000`.
+   */
+  color?: string;
+
+  /**
+   * A URL of an image that can be used to display this channel
+   */
+  glyph?: string;
+}
+
+
 /**
  * A Desktop Agent is a desktop component (or aggregate of components) that serves as a
  * launcher and message router (broker) for applications in its domain.
@@ -75,7 +166,7 @@ interface Listener {
  */
 interface DesktopAgent {
   /**
-   * Launches/links to an app by name.
+   * Launches an app by name.
    * 
    * If a Context object is passed in, this object will be provided to the opened application via a contextListener.
    * The Context argument is functionally equivalent to opening the target app with no context and broadcasting the context directly to it.
@@ -179,4 +270,29 @@ interface DesktopAgent {
    * Adds a listener for incoming context broadcast from the Desktop Agent.
    */
   addContextListener(handler: (context: Context) => void): Listener;
-}
\ No newline at end of file
+
+  /**
+   * Retrieves a list of the System channels available for the app to join
+   */
+  getSystemChannels(): Promise<Array<Channel>>;
+
+  /**
+   * Joins the app to the specified channel
+   * An app can only be joined to one channel at a time
+   * rejects with error if the channel is unavailable or the join request is denied
+   * `Error` with a string from the `ChannelError` enumeration.
+   */
+  joinChannel(channelId: string) : Promise<void>;
+
+  /**
+   * Returns a channel with the given identity. Either stands up a new channel or returns an existing channel.
+   * 
+   * It is up to applications to manage how to share knowledge of these custom channels across windows and to manage
+   * channel ownership and lifecycle. 
+   * 
+   * `Error` with a string from the `ChannelError` enumeration.
+   */
+  getOrCreateChannel(channelId: string): Promise<Channel>;
+
+
+