Integrate Umbraco Contour with third party service, post to Podio App

Just a quick blog post with an example on how to attach a third party service to Contour (the Umbraco form builder).

You can attach extra functionality to a Contour form by setting up workflows,

So by default a Contour form will store it’s data but if you want extra stuff (like sending an email) you can do that by adding a workflow..

There are several out of the box workflows you can use…now in my case I wanted to extend Contour to post it’s record date to a podio app

So first of all I need a podio API key, you can get that here

Once that is settled we can use the API, there is even a .net library available

Next of course is to setup an app in podio, that’s pretty easy with the app builder,

So I just created a new app that has a single item and that item has a single text block (that’s the text block we’ll populate with contour record data)


Now I’m all set the create the workflow, here is the code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.XPath;
using Podio.API;
using Podio.API.Model;
using Podio.API.Utils.ItemFields;
using Umbraco.Forms.Core;
using Umbraco.Forms.Core.Enums;
using Umbraco.Forms.Data;
using Umbraco.Forms.Data.Storage;
namespace Contour.Addons.Podio
    public class AddRecordToPodioSpace : WorkflowType
        [Umbraco.Forms.Core.Attributes.Setting("Client id")]
        public string ClientId { get; set; }
        [Umbraco.Forms.Core.Attributes.Setting("Client secret ")]
        public string ClientSecret { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string AppId  { get; set; }
        public string PodioTextFieldId { get; set; }
        public AddRecordToPodioSpace()
            this.Id = new Guid("55517EB2-8D61-483C-A3DA-7850FCE05996");
            this.Name = "Send record to podio space";
            this.Description = "Send record to podio space";
        public override List<Exception> ValidateSettings()
            List<Exception> l = new List<Exception>();
            if (string.IsNullOrEmpty(ClientId))
                l.Add(new Exception("’ClientId’ setting has not been set"));
            if (string.IsNullOrEmpty(ClientSecret))
                l.Add(new Exception("’ClientSecret’ setting has not been set’"));
            if (string.IsNullOrEmpty(UserName))
                l.Add(new Exception("’UserName’ setting has not been set’"));
            if (string.IsNullOrEmpty(Password))
                l.Add(new Exception("’Password’ setting has not been set’"));
            return l;
        public override WorkflowExecutionStatus Execute(Record record, RecordEventArgs e)
                RecordsViewer viewer = new RecordsViewer();
                XmlNode xml = viewer.GetSingleXmlRecord(record, new System.Xml.XmlDocument());
                XPathNavigator navigator = xml.CreateNavigator();
                XPathExpression selectExpression = navigator.Compile("//fields/child::*");
                selectExpression.AddSort("@pageindex", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
                selectExpression.AddSort("@fieldsetindex", XmlSortOrder.Ascending, XmlCaseOrder.None, "",
                selectExpression.AddSort("@sortorder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number);
                XPathNodeIterator nodeIterator = navigator.Select(selectExpression);
                string list = "<dl>";
                while (nodeIterator.MoveNext())
                    list += "<dt><strong>" +
                                nodeIterator.Current.SelectSingleNode("caption").Value) + ": </strong><dt><dd>";
                    XPathNodeIterator values = nodeIterator.Current.Select(".//value");
                    while (values.MoveNext())
                        list +=
                                .Replace("\n", "<br/>") + "<br/>";
                    list += "</dd>";
                list += "</dl>";
                Client client = Client.ConnectAsUser(ClientId,
                Item i = new Item();
                var textField = i.Field<TextItemField>(PodioTextFieldId);
                textField.ExternalId = PodioTextFieldId; 
                textField.Value = list;
                client.ItemService.AddNewItem(Convert.ToInt32(AppId), i);
            catch (Exception ex)
                return WorkflowExecutionStatus.Failed;
            return WorkflowExecutionStatus.Completed;

So all the settings I need for podio are public properties marked with the settings attribute (so I can set those up when inserting the workflow)

THen the actuall workflow code will loop through all the record data and create html list. So you see it doesn’t matter how many fields you have it will pass all of them, this code is actually borrowed from the default send email workflow code, you can get all default providers code here (sourcecode tab at the bottom)

THen it wil make use of the podio api to create a new item and add it …

When I know deploy this to the site, I see a new workflow type


When choosing that type I can setup the settings


So when the settings are valid and the workflow is attached the form will now post to podio when it’s submitted (if workflow is attached to submitted stage)


So now I have a reusable component that I can deploy whenever I need a form that needs to integrate with podio!

1 Comment so far

  1. martin griffiths on April 30th, 2014

    Hi Tim

    What about in the opposite direction? Does contour expose any kind of webservice that I can subscribe to, from another application and pull down already submitted records?


Leave a Reply