Just over 5 weeks ago I moved to the San Francisco Bay Area to seek new opportunities. I had no idea what I wanted to shift my full-time focus to and began connecting with people, researching and interviewing with a handful of companies — ranging from huge web presences all the way down to tiny startups. I considered companies in different spaces from finance all the way to video. I met with dozens of folks to see what they were up to and what their company was about. That led to me being wired on caffeine for a few weeks when I had 3-4 coffee meetings scheduled each day, but I digress. I am elated to announce, likely to the surprise of many, that last week I jumped onboard the smallest company I chatted with — Notifo.
Notifo recently graduated from the Winter 2010 batch of the Y Combinator startup incubator run by Paul Graham, Jessica Livingston, Robert Morris (rtm) and Trevor Blackwell (who spends his time working on Anybots). My title is Co-Founder alongside Chad Etzel (also a Georgia Tech Alum). We recently closed an angel round.
What's it do?
Notifo is a mobile notifications platform that consists of two parts: the backend to serve up hot PUSH notifications rapidly and mobile clients to interact with them. Notifo currently has an iPhone app but will be expanding to Android next, then other smartphones. The biggest draw of Notifo is that people can send notifications to mobile clients without having to personally develop mobile apps, a time consuming and expensive process. Instead, users just have the free Notifo app which can be used to receive notifications from a number of services that use the simple API (post over https with basic auth).
An example notification coming from GitHub and then the listing of other notifications in the Notifo app.
Version 2 of the iPhone app is currently under beta testing with a scheduled release in the next month (after compatibility testing with iOS 4). It will feature some interesting features with utility adjacent to notifications that will be driving our early user adoption.
Doing mobile dev means we will need to have many devices on hand to test various builds of the application. Ex: for Android we need something that runs 2.2 for c2dm (Android's push) as well as some older versions to test a pre-c2dm workaround.
The flow of things is that the user creates an account on the site or within the application (currently only the iPhone app is public and released) then signs up for services and grants them permission to notify them. At the moment there aren't too many services, which explains why I will be doing some sorts of developer evangelism with various how to articles and coding up various neat scripts as mentioned below.
Below you'll see some of the individual controls users have on each service they receive notifications from. There is also a page (not shown) where you can set things like quiet hours.
The Hacker News service (add Notifo username to HN user profile) notifies you of replies to your HN comments.
What do you do?
I will be doing what I love doing — a bit of everything. Traditionally this has been mainly front-end tasks but as I have never touched mobile development, I'm eager to learn and help out where I can. For example, random things I have been working on include a Google Chrome extension to send the current page to the phone (similar to the chrome to phone functionality but that was Android-only), a Safari 5 Extension, adding functionality to the Notifo WordPress plugin created by Hacker News user ErrantX in addition to various things around the Notifo web app, including some PHP/Redis tinkering and lots of aesthetic improvements. I also have been planning on making a section of the site to promote interesting uses of the Notifo API and services from those in the development community.
How you can get started with the Notifo API
On Monday GitHub added my Notifo service hook. This allows any repository admin to specify a CSV list of Notifo usernames to be notified upon each commit to that repository. GitHub has information on how to start working with service hooks on their github-services
repository page lthat they want people to fork.
Notifo Service Hook now live on GitHub. Just click the Admin button for a repo you own then scroll to Service Hooks.
I'll briefly go over what was involved with creating a Notifo service hook and using the API. First off, there are two types of accounts on Notifo: user accounts and service accounts. User accounts may subscribe to service accounts in addition to being able to send notifications to themselves without needing to send a subscription request first. Service accounts may send messages to any user but those users must first accept a subscription request sent by the service. Notifications sent from a user account to that user cannot change the avatar shown when the notification is received. Both types of accounts have their own API secret key that must be used in conjunction with the username before interacting with the API. In addition, interacting with the API must be done over HTTPS.
In the case of the GitHub service hook, we decided to use a service account for GitHub so that there can be multiple subscribers and so that everyone would see the lovely squidcat icon (avatar set inside the Notifo website). ;-)
service :notifo do |data, payload|
subscribe_url = URI.parse('https://api.notifo.com/v1/subscribe_user')
data['subscribers'].gsub(/\s/, '').split(',').each do |subscriber|
req = Net::HTTP::Post.new(subscribe_url.path)
req.basic_auth('github', secrets['notifo']['apikey'])
req.set_form_data('username' => subscriber)
net = Net::HTTP.new(subscribe_url.host, 443)
net.use_ssl = true
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
net.start {|http| http.request(req)}
notification_url = URI.parse('https://api.notifo.com/v1/send_notification')
payload['commits'].each do |commit|
req = Net::HTTP::Post.new(notification_url.path)
req.basic_auth('github', secrets['notifo']['apikey'])
req.set_form_data(
'to' => subscriber,
'msg' => URI.escape("#{commit['author']['name']}: \"#{commit['message']}\"", Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")),
'title' => URI.escape("#{payload['repository']['name']}/#{payload['ref_name']}", Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")),
'uri' => URI.escape(commit['url'], Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
)
net = Net::HTTP.new(notification_url.host, 443)
net.use_ssl = true
net.verify_mode = OpenSSL::SSL::VERIFY_NONE
net.start {|http| http.request(req)}
end
end
end
This particular script is in Ruby and two loops are taking place. In the first loop I run through the CSV list of subscribers (Notifo usernames) that is specified on an external file, open an HTTP connection and specify basic authentication and SSL to the subscribe_user
API URL and send a POST consisting of the username. That sends a subscription request to the user — required or the user won't receive subsequent notifications. In this case the user will end up missing the first commit notification but it will work for the second after they accept the request.
The second loop is nested within the first and sends the actual notification to the user. In this case we want to sent information about the repository commit. The external payload contains that information so I'm merely passing along that data in some of the parameters for a send_notification
:
- to - the Notifo username
- msg - the commit message
- title - repository name and branch
- uri - the GitHub commit page, opened when the user double-taps on the notification (a single tap expands the message; useful for long commit messages)
Parameters must be URL encoded so that explains the long URI.escape chunks. There are tons of ways of URL encoding and escaping strings though. In summary: for a service account establish HTTPS connection with basic auth utilizing service name and API secret key, loop through users, send subscription notification request then the send message.
How about an example for sending a notification to the user from their own user account? Let's tackle that in JavaScript:
function sendNotifo(msg,title,uri,label) {
var https = new XMLHttpRequest();
var auth = "Basic " + Base64.encode(localStorage.notifo_username+':'+localStorage.notifo_apisecret);
var push = 'to=' + encodeURIComponent(localStorage.notifo_username) + '&msg=' + encodeURIComponent(msg) + '&uri=' + encodeURIComponent(uri) + '&label=' + encodeURIComponent(label)+ '&title=' + encodeURIComponent(title);
https.open('POST', 'https://api.notifo.com/v1/send_notification', true);
https.setRequestHeader('Authorization', auth);
https.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
https.send(push);
}
This example comes from the Chrome extension I was hacking away on so that localStorage stuffprobably isn't relevant to you. Just hardcode or require parameters for the username and API secret key instead of taking them from localStorage. Creating basic authentication in JavaScript requires setting the authorization header and sending the base64 encoded hash of the "username:key" token. Calling Base64.encode()
requires a base64 encode/decode library to be included within the page. I open the connection to send a POST to send_notification() with some parameters. The only difference about sending a notification from a user account is an optional label
to describe the application. Note that this was simply a quick JS example and there is more that should be done for production code. Also, due to same-origin security policy there is a JavaScript dilemma with doing cross-domain POST requests that involves a tricky setup. A quick search on StackOverflow should yield a few of the ways to get past that.
How about an example in PHP? Here's the function WP-Notifo uses to send notifications:
<?php
function notifo_message($title,$msg,$uri = false,$to = false)
{
$notifo_pass = get_option('notifo_api_key');
$notifo_user = get_option('notifo_username');
$data = array('label' => get_bloginfo('name'), 'title' => $title, 'msg' => $msg);
if($uri)
{
$data['uri'] = $uri;
}
if($to)
{
$data['to'] = $to;
}
$ch = curl_init("https://api.notifo.com/v1/send_notification");
curl_setopt($ch, CURLOPT_USERPWD, $notifo_user.":".$notifo_pass);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
}?>
You can start to see similarities between all these pieces of code. The only real difference between everything is how each particular language sends the actual POST. The above examples were the vanilla plain examples and did not make use of any handy API libraries. Notifo currently has a few API libraries on this GitHub repo page with libraries for more languages to come. Don't see a library for your language of choice? Fork it, code it and submit a pull request.
The Office
Chad and I work out of a garage in Menlo Park. I brought my own desk to work the first day. As for what the startup life is like — it's absolutely brilliant working next to a smart person for hours on end. I woke up at 5:50am my first full day, went to work and got home at 11pm. The second day started just as early. In a startup no one benefits from you slacking. It's not about reaching 5pm or 6pm and clocking out; it's about moving fast, completing your objectives and creating passionate users. (and then exiting, working for Google and enjoying their sweet, sweet froyo machine in Building 43 everyday).
The current Notifo office.. in a garage in Menlo Park, CA. Just gotta watch out for those big spiders and you'll be fine.
The other wall (not pictured) serves as our own write-once note keeping device.
Never underestimate the importance of a great coffee maker in a startup setting. COD (Coffee-On-Demand) units like this Breville serve up K-cup goodness in seconds.
There's a lot more I could say here but this blog post started out with the intent of just being a simple announcement. I'll save the more thought-out blog posts about product development and technical talk for the near future. Keep up-to-date on Notifo happenings with the company blog and Twitter.
Thoughts? Questions about working with the Notifo and/or the API? What would you like to build out to test Notifo? Any improvements to the API or product concept in general?
Update: Checkout the coverage on ReadWriteWeb and the Notifo blog.