Archive by Author | Rich

Get on the phone!

There have been a couple of interesting articles lately about the things startups can and should do to get and keep customers, and to build loyalty.

In particular I thought that this article – (Dial up: Why startups should use the phone) was interesting, and highlights something we try to do at InGenius.

We definitely use the phone here at InGenius – we call potential customers as quickly as possible when they contact us, and we walk every customer through installing and configuring the software (which takes about an hour in most cases), and help them get up and running. We’ve found that this personal touch really makes a difference and starts to build a bond of trust between our customers and ourselves.

Another recent post by Paul Graham (Do things that don’t scale) and 37Signal’s Jason Fried’s excellent post highlight the same ideas – get out there and get in touch with your customers!

 

Displaying BinaryCanary.com Web Site Status on a Geckoboard

Our company uses Geckoboard.com to display the status of a ton of Salesforce.com related information, as well as info we pull from Trello.com, Fogbugz.com, and BinaryCanary.com.

Geckoboard

BinaryCanary.com is a website monitoring company that allows us to monitor all our online properties, and alerts us if there are any issues. BinaryCanary.com has an API that we can query to determine the status of each of our monitors.

We wrote a small PHP script which is polled by Geckoboard.com every 3 minutes, and checks the status of all our online properties. The returns a simple XML file, which is then displayed by Geckoboard.com in a text window.

We are able to use simple HTML like <table> and <font> to make the text display green for sites that are up, and red for any sites that are down.

The widget isn’t very pretty, but it packs a ton of information into a small space, and we can easily see any sites that are red. (Sorry, I had to blank out some of the text…)

Binary Canary

Binary Canary Widget

Here’s the PHP file we use to generate the XML for this widget.

And, the Geckoboard Widget settings are:

Geckoboard Text Widget Properties

Geckoboard Text Widget Properties

Adding a phone number field to Salesforce.com Opportunities

We recently had an InGenius Connector Enterprise customer who wanted to have clickable phone numbers associated with each opportunity.  By default, opportunities don’t have any phone number fields.

The customer’s workflow was that they wanted their workers to go through the opportunities, and call each one every few days, until the sales was closed.

The Solution:

We advised the customer to add a field to the opportunity of type “Phone.”  We called this field “Account Phone.” Since this field is of type “Phone,” it will automatically be converted to a “click-to-dial” field by InGenius Connector Enterprise.

We then added a trigger to the Account that detects any changes, and then loops through all the opportunities associated with this account, and updates the “Account Phone” field in all the opportunities.

This way, the opportunities all have a clickable phone number field that stays in sync with the phone number field in the Account.

The Trigger Apex Code:

trigger UpdateOpportunityPhoneField on Account (after insert, after update) {
  // Loop through all account updates in this trigger (there will generally be one)
  for(Account acct: Trigger.new) {
    List<Opportunity> oppList = [SELECT id, Account_Phone__c FROM Opportunity WHERE Accountid = :acct.id AND Account_Phone__c != :acct.Phone]; // Find all the opportunities for this account.
    for(integer i = 0 ; i < oppList.size(); i++){
      oppList[i].Account_Phone__c = acct.Phone; // Update all opportunities with the new phone number.
    }
    update oppList;
  }
}

Things to be aware of:

  • The solution uses some Apex code to accomplish its magic. You’ll need an appropriate Salesforce.com subscription to use this solution.
  • The code loops through all the Opportunities each time an Account is changed. This could use up your Apex allowance.
  • If the user edits the Account Phone field in the Opportunity, this is not copied back up to the Account. You could add a second trigger for this, if needed. If the user edits the Account Phone field, the changes will be wiped out the next time the Account is edited. Another alternative is to make the Account Phone field read-only in the Opportunity Layout.

Nice overview of all of Microsoft Dynamic’s 4.0, and 2011 Update Rollup releases

If you use Microsoft Dynamics, you’ll understand that it can be complicated to know if you are running the latest version, and when to update. Additionally, Microsoft released a broken update Jan 12, 2012, which forced some users to have to completely re-install.

Microsoft released a blog entry recently, in order to clear all this up:

Microsoft Dynamics CRM 4.0 and 2011 Update Rollup Release Dates, Build Numbers, and Collateral
http://blogs.msdn.com/b/crminthefield/archive/2012/02/10/microsoft-dynamics-crm-4-0-and-2011-update-rollup-release-dates-build-numbers-and-collateral.aspx

Project Management with Trello

At InGenius we typically have quite a few projects on the go, and a lot of very busy developers.

One of the difficulties we face is project tracking. Making sure that:

  • every developer is working on the most pressing items,
  • our QA department knows what to focus their testing efforts on, for each build,
  • our sales people know when new builds, with new features are coming out.

We’ve tried many tools for keeping track of our projects, including:

  • Complicated spreadsheets, with a grid showing each developer, and all projects, saved in a common directory,
  • using Yammer.com and asking developers to yammer about what they’re working on,
  • Daily meetings, making sure everyone’s focused,
  • Online tools like Basecamp.com,
  • Using our Wiki to say what developers are up to, and should be doing,
  • Whiteboards at each office, with individual developer priorities,

And, none of it worked very well. Developers don’t like meetings, don’t like yammering, don’t like anything that gets in the way of what they should be focused on.

Last fall, we started using Trello.com, a new FREE online product from Fog Creek Software – the same people that build the terrific Fogbugz bug tracking system we use here at InGenius. We’re all thrilled with the results!

Trello is a deceptively simple application. It presents you with a page called a “board”, and the board contains multiple lists.

Each list can have a title, and multiple entries, known as cards. You can learn how to build a website, and you can re-order lists by dragging them as well.

Cards are the basic unit within Trello. Cards generally move up and down in a list, and from list to list, within a board.

Each card has a title, and when you click on it, it flips over to reveal a description, and a set of notes. People can add comments to a card, assign labels, assign a card to a particular worker, or vote on a card.

At InGenius, we use a similar setup to Fog Creek’s development board – we have a main board for all the development in our company, and have lists for:

  • Known Issues,
  • Ideas,
  • Next Up,
  • In Progress,
  • Implemented,
  • a list for each release.

Items generally move across, from left to right as they progress from ideas, to being implemented, to getting moved to a release.

Using Trello has been a godsend for both the developers and senior staff at InGenius. We can now easily see, in a single view, everything that’s in progress in the company, and can easily move cards around to indicate their priority.

Developers can easily make notes about their progress, which we review in a once-a-week meeting to make sure everyone’s on track. Each card contains a description of the bug or feature. Developers can easily add notes about their progress, and can move a card to the “Implemented” list when they’re done.

And, our QA staff can easily look at the lists for each release, to see which new features have been implemented, so they know where to focus their testing efforts.

Trello has become the focus of our developer meetings, allowing us to keep the meeting to 15-20 minutes and make sure everyone is on the right track.

We’ve learned a few tricks as we’ve used Trello.com:

  • Don’t overload Trello with too many items. In our case, we keep the “high level” items in Trello, and leave most of the detail work in FogBugz. The items we track in Trello are generally the things that we’ll highlight in each product release, and major, customer-affecting issues. The balance between Trello and your bug tracking tool is hard to determine.
  • make sure that card titles are succinct and descriptive
  • Make sure that developers update the cards regularly
  • Use labels to show which cards are bugs, and which features have been tested, or need documentation updates,
  • Assign cards to developers by dragging developer icons over to a card,
  • Use Trello in developer meetings as a focal point, and to show the team how the tool should be used,
  • Encourage higher management and sales to use Trello too.
  • Have a look at Trello’s development board for ideas and tips – and it’s fun to watch features move across, and out into a release!

Of course, Trello is not yet perfect, but new features are being implemented every week.

Our wish list includes:

  • Ability to move lists from board to board,
  • Better functionality in the iPhone and iPad applications,
  • Better management of company members, and management of which company members are allowed access to a board (this is already much improved)
  • Tighter integration with FogBugz

 

Automatic creation of Salesforce.com leads from product registrations

Our company recently started using Salesforce.com as its CRM system. We’ve all been astonished at the change it has made to almost every facet of our business. We’re on top of leads, interacting with customers, and tracking every call now using our integration between InGenius Connector and Salesforce.com.

But, we wanted to do more – we wanted to set up a system where we could call customers who have downloaded our software, and help them with the installation and configuration of the application. As with most telephony applications, it can be complicated to set up because of the many different types of phone systems out there.

So, we came up with a plan:

  1. User obtains a trial key from any of our download pages,
  2. The registration system sends an email to the user, with their trial key,
  3. The registration system also sends an email to a special email address at Salesforce.com, containing the registration info,
  4. Some custom Apex code at Salesforce.com parses the email, and creates a lead, and a task showing what the user downloaded.

Details:

So, here’s what we did, in more detail:

1) Create Email

We modified our registration system to send an email when a trial key is requested. The email looks like this:

Subject: [Trial Key]
Source:AUTOMATION
 Campaign:
 FirstName:Bob
 LastName:Smith
 Email:bob@bob.com
 Phone:555-1212
 Company:Bob's Baker
 Country:Canada
 Industry:
 Dealer:YES
 Product:IC
 Coupon:11111-22222-33333-44444-5555
 Platform_OS: Windows
 Platform_CRM:Salesforce.com
 Platform_TELEPHONY:Asterisk

2) Create Apex Class

We created an Apex Class to handle the incoming mail. The class parses the incoming email, then does the following:

  1. Checks to see if there’s a contact with this email address,
  2. Checks to see if there’s a lead with this email address,
  3. If not found, then create a lead with the user’s info,
  4. Create a task with the details of the product the user has downloaded. Creating a task is convenient because we can automatically handle the case where a user trials more than one product – they just get created as tasks on a lead/contact.

The Apex code looks like this:

Note that the variables ending in __c are custom variables in our Salesforce.com installation.

global class InGenius_Integration_Registration_Email implements Messaging.InboundEmailHandler {
 global Messaging.InboundEmailResult handleInboundEmail(
   Messaging.InboundEmail email,
   Messaging.InboundEnvelope envelope) {
    Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();
    String EmailPlainText= '';
    string EmailSource;
    string EmailCampaign;
    string EmailFirstName;
    string EmailLastName;
    string EmailEmail;
    string EmailPhone;
    string EmailCompany;
    string EmailCountry;
    string EmailIndustry;
    string EmailDealer;
    string EmailProduct;
    string EmailCoupon;
    string EmailPlatform_OS;
    string EmailPlatform_CRM;
    string EmailPlatform_TELEPHONY;
    Boolean found = false;
    ID ContactId;
    Lead insertedLead;
    System.debug('Email Received from: ' + envelope.fromAddress + ' (' + email.subject + ')' );
    // Ignore emails with the incorrect subject
    if( email.subject != '[TRIAL KEY]' ) {
        System.debug('Email rejected: Wrong Subject(' + email.subject + ')' );
        return result;
    }
    try {
        EmailPlainText = email.plainTextBody;
        String[] emailbody=email.plainTextBody.split('\n',0);
        System.debug(emailbody);
        // THIS IS NOT a terrific way to parse the email, but it works...
        // If the email format changes, by even ONE character, this will fail.
        EmailSource =             emailbody[0].substring(7);
        EmailCampaign =           emailbody[1].substring(9);
        EmailFirstName =          emailbody[2].substring(10);
        EmailLastName =           emailbody[3].substring(9);
        EmailEmail =              emailbody[4].substring(6);
        EmailPhone =              emailbody[5].substring(6);
        EmailCompany =            emailbody[6].substring(8);
        EmailCountry =            emailbody[7].substring(8);
        EmailIndustry =           emailbody[8].substring(9);
        EmailDealer =             emailbody[9].substring(7);
        EmailProduct =            emailbody[10].substring(8);
        EmailCoupon =             emailbody[11].substring(7);
        EmailPlatform_OS =        emailbody[12].substring(12);
        EmailPlatform_CRM =       emailbody[13].substring(13);
        EmailPlatform_TELEPHONY = emailbody[14].substring(19);
        if( EmailDealer=='YES' )
            EmailDealer='Dealer';
        else
            EmailDealer='End User';
        System.debug(EmailSource);
        System.debug(EmailCampaign);
        System.debug(EmailFirstName);
        System.debug(EmailLastName);
        System.debug(EmailEmail);
        System.debug(EmailPhone);
        System.debug(EmailCompany);
        System.debug(EmailCountry);
        System.debug(EmailIndustry);
        System.debug(EmailDealer);
        System.debug(EmailProduct);
        System.debug(EmailCoupon);
        System.debug(EmailPlatform_OS);
        System.debug(EmailPlatform_CRM);
        System.debug(EmailPlatform_TELEPHONY);
    }
    catch( exception e4 ) {
        System.Debug('ProcessTrial Failure: Could Not parse email');
        return result;
    }
    // Try to lookup any contacts based on the email from address
    // If there is more than 1 contact with the same email address,
    // we just use the first match
    try {
        // Does a contact exist with this email?
        List<Contact> contactList = [Select Id, Name, Email From Contact Where Email = :EmailEmail Limit 1];
        if( !contactList.IsEmpty()) {
            ContactId = contactList[0].Id;
            System.Debug('Found Existing Contact:' + contactList[0].Name);
            found = true;
        }
        else {
            // Does a Lead exist with this email?
            List<Lead> leadList = [Select Id, Name, Email From Lead Where Email = :EmailEmail Limit 1];
            if( !leadList.IsEmpty()) {
                ContactId = leadList[0].Id;
                System.Debug('Found Existing Lead:' + leadList[0].Name);
                found = true;
            }
            else {
                // Create a Lead with this email
                Lead leadToInsert = new lead(
                    FirstName=EmailFirstName,
                    LastName=EmailLastName,
                    email=EmailEmail,
                    Company=EmailCompany,
                    Country=EmailCountry,
                    Industry=EmailDealer,
                    Status='Working - Key Issued',
                    Phone=EmailPhone,
                    LeadSource=EmailCampaign,
                    Product__c = EmailProduct,
                    TrialKey__c = EmailCoupon,
                    Phone_System__c=EmailPlatform_TELEPHONY,
                    Users_CRM__c=EmailPlatform_CRM);
                insert(leadToInsert);
                ContactId = leadToInsert.Id;
                System.Debug('Created Lead' + EmailFirstName + ' ' + EmailLastName + ' ' + EmailCompany + ' ' + ContactId);
                found = true;
            }
        }
    }
    catch (exception e2) {
        System.Debug('ProcessTrial Failure: Could Not Create Lead:' +  EmailFirstName + ' ' + EmailLastName + ' ' + e2);
    }
    if( found ) {
        try {
            // New Task object to be created
            Task[] newTask = new Task[0];
            // Add a new Task to the contact record we just found or created above.
            newTask.add(new Task(
                Description =  EmailPlainText,
                Priority = 'Normal',
                Status = 'Key Issued',
                Subject = EmailProduct,
                Product__c = EmailProduct,
                RegistrationCoupon__c = EmailCoupon,
                //IsReminderSet = true, // optional, set a reminder
                //ReminderDateTime = System.now()+2, // Set reminder for 2 days in the future.
                WhoId =  ContactID));
            insert newTask;
            System.debug('New Task: ' + newTask );
        }
        catch (exception e3) {
            System.debug('ProcessTrial Failure: Create New Task Failed: ' + e3 );
        }
    }
    else {
        System.Debug('ProcessTrial Failure: Could Not Create Lead:' +  EmailFirstName + ' ' + EmailLastName);
        return result;
    }
    result.success = true;
    return result;
 }

3) Configure Email Service

Once the Apex class is created, then you need to go to Develop -> Email Services and add a new Email Service to handle the incoming mail. This is where you’ll get the unique email address. Note also, that you may have to adjust the permissions, and adjust settings for which emails you accept mail from. By default this is quite locked down by Salesforce.com.

4) Last Step:

We hired Katy! Katy looks after calling customers when they’ve downloaded a trial version of our software, and make sure that the install goes smoothly. Thanks Katy!

Further Notes:

  • We are planning to add additional code to update the contact once a user purchases our software.
  • We are planning to improve the email parsing.
  • We had quite a few issues getting the debugging to work reliably at Salesforce.com. It is not clear how to enable debugging, and how to extract debug logs.

InGenius Connector and TAPI

TAPI (Telephony Application Programming Interface) is an API provided by Windows computers for the control of telephones connected to your PC. There’s a great overview of TAPI at Wikipedia if you want to learn more.

InGenius Connector, and its cousin Mitel’s Unified Communicator Express can use TAPI in two ways.

  1. InGenius Connector can appear as a TAPI telephony provider to CRM systems,
  2. InGenius Connector can make use of TAPI to control your desktop phone (Connector as a TAPI consumer).

InGenius Connector as a TAPI provider for connecting to CRM systems

 

This is also called the TAPI Plug-in, and is an optional component of InGenius Connector and Unified Communicator Express. This feature is enabled for an existing installation of our product by simply entering a license key. When you’ve done this, InGenius Connector will now appear in the list of TAPI devices that are available on your computer, when you use a TAPI enabled CRM.

This mode is commonly used with CRMs such as Act, Maximizer, and Outlook and many custom CRM systems. From within your CRM, select the InGenius Connector from the list of TAPI devices on your computer, and your CRM will now be able to place calls using the telephone connected to InGenius Connector. You’ll also be able to see the caller ID for incoming calls, and the CRM will be able to pop the appropriate CRM contact automatically. Note that the functionality provided varies between CRM’s. Some CRM’s may not provide the screen-pop functionality, for instance.

On the telephone side of things, the TAPI Plugin is commonly used with Mitel phone systems (using Unified Communicator Express or Unified Communicator Advanced, with the appropriate InGenius plugin), as well as users of Polycom phones, Aastra Phones, Asterisk PBX’s, and Broadsoft PBX’s – systems which are generally quite difficult to control via TAPI.

Using InGenius Connector with a TAPI-enabled phone system

The other way to use TAPI with InGenius Connector is to set up a phone profile within InGenius using the TAPI phone profile (which will be available within InGenius Connector in the first Quarter of 2012). In this case, the customer has a phone system that provides a TAPI interface, and want to use the features of InGenius Connector to control their phone system, or they want to use an InGenius Connector CRM integration with their phone system.

Users will have to install InGenius Connector, as well as the TAPI service provider which is provided by the telephone system manufacturer. In the case of Cisco, this is available for free, and can be downloaded by the Cisco Call Manager administrator.

We are currently developing TAPI support for Cisco phone systems, and will be certifying other phone systems soon.

Our customers generally use this option to connect their TAPI-enabled phone system to Salesforce.com, Microsoft Dynamics CRM, and Sugar CRM.

Follow

Get every new post delivered to your Inbox.