<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Chewing the Cud (Posts about Work)</title><link>https://jamesaimonetti.com/</link><description></description><atom:link href="https://jamesaimonetti.com/categories/work.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Wed, 26 Mar 2025 20:45:28 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Sponsor Me!</title><link>https://jamesaimonetti.com/posts/sponsor-me/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
Recently learned that I was accepted to Github's beta sponsorship program which allows me to define sponsorship tiers!
&lt;/p&gt;

&lt;p&gt;
&lt;a href="https://github.com/sponsors/jamesaimonetti"&gt;https://github.com/sponsors/jamesaimonetti&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
My hope is that folks that have appreciated my work with KAZOO and help in the forums, documentation, and IRC, might throw a bone or two of thanks my way. If there's a better tier that would get you on board, let me know.
&lt;/p&gt;</description><category>Work</category><guid>https://jamesaimonetti.com/posts/sponsor-me/</guid><pubDate>Thu, 31 Oct 2019 22:55:23 GMT</pubDate></item><item><title>Comparing Kazoo and other CPaaS solutions</title><link>https://jamesaimonetti.com/posts/comparing-kazoo-and-other-cpaas-solutions/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;div id="outline-container-org5e1b253" class="outline-2"&gt;
&lt;h2 id="org5e1b253"&gt;Introduction&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org5e1b253"&gt;
&lt;p&gt;
First, what is a CPaaS? Communication Platform as a Service is the expanded acronym but it boils down to APIs and programmatic control of the phone system.
&lt;/p&gt;

&lt;p&gt;
The power of CPaaS is giving you the control of each call in a way that makes sense for your (or your business') needs. It lets you access information outside of the phone system (such as CRMs and other databases) to make decisions about how to route a particular call.
&lt;/p&gt;

&lt;p&gt;
Another feature of CPaaS is the ability to get events out of the phone system - via webhooks or over a websocket typically. This gives you the ability to track a call's progress through the callflow, updating information in CRMs and other databases and lets you build robust applications on top of the phone network.
&lt;/p&gt;

&lt;p&gt;
CPaaS providers often offer API access to manipulate running calls (such as programmatically hanging up a call, initiating a transfer, etc), fetching CDRs (call detail records - typically used when billing for calls), initiating new calls, and more.
&lt;/p&gt;

&lt;p&gt;
A newer feature, a requirement of CPaaS providers these days, is having WebRTC-based phones (or a phone in your browser), minimizing the software needed to be installed and allowing companies to provide a branded phone in their web apps.
&lt;/p&gt;

&lt;p&gt;
Most CPaaS providers allow you to process phone calls without being a telecom junkie by providing a simplified set of actions that may be performed on a given call. No longer do you need to know what the PSTN is or how SIP or SDP work - you get to build on top of an abstracted phone system and focus more on your business needs.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-org93f8fe5" class="outline-2"&gt;
&lt;h2 id="org93f8fe5"&gt;The competitive landscape&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org93f8fe5"&gt;
&lt;p&gt;
When you view the competitive landscape in telecom, it is easy to see that the big players (traditional telcos and UCaaS providers like Avaya, Broadsoft, and others) are recognizing the value of adding a CPaaS-type offering by purchasing CPaaS providers like Zang and Nexmo (via Cisco acquisition). Twilio stands apart as an independent CPaaS provider that IPO'd in 2016.
&lt;/p&gt;

&lt;p&gt;
The market has shifted and CPaaS + UCaaS is the norm going forward; UCaaS companies are racing to catch up while CPaaS companies are trying to expand into more traditional telephony environments.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-org8cdbf1c" class="outline-2"&gt;
&lt;h2 id="org8cdbf1c"&gt;Technical Evaluation&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org8cdbf1c"&gt;
&lt;p&gt;
Let's dive into a bit of the nitty-gritty and see how a handful of providers compare.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id="outline-container-org2362b51" class="outline-3"&gt;
&lt;h3 id="org2362b51"&gt;Real-time Call Control&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org2362b51"&gt;
&lt;p&gt;
Most CPaaS offer a small list of actions/verbs that can be returned from your program. The basics are covered by most providers.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id="outline-container-org82c942b" class="outline-4"&gt;
&lt;h4 id="org82c942b"&gt;Connecting callers to endpoints&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org82c942b"&gt;
&lt;p&gt;
When a caller calls in, typically you want them to talk to someone (or several someones). Below is a quick chart on the various endpoints supported by CPaaS providers for connecting the caller:
&lt;/p&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-left"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-left"&gt;Action&lt;/td&gt;
&lt;td class="org-center"&gt;Kazoo&lt;/td&gt;
&lt;td class="org-center"&gt;Twilio&lt;/td&gt;
&lt;td class="org-center"&gt;Plivo&lt;/td&gt;
&lt;td class="org-center"&gt;Nexmo&lt;/td&gt;
&lt;td class="org-center"&gt;Tropo&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Registered Device&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;SIP Endpoint&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Conference&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;DID(s)&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Call Queue&lt;/td&gt;
&lt;td class="org-center"&gt;-&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;User (find-me/follow-me)&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Group&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Page group&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Some notes:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;Kazoo is the only platform that allows devices to register directly to the platform to receive calls. Registration is the phone's way of saying "Send calls for me to this IP/port please". Devices can then move around (say a softphone on a laptop) but still be able to receive calls easily. The SIP endpoint, in contrast, is typically in a fixed location and is harder to move once setup. An existing PBX is the typical destination for SIP endpoints.&lt;/li&gt;

&lt;li&gt;Twilio is the only provider that offers a "Queue" option in the public docs. Kazoo has a community-supported queuing applications called ACDc as well as a commercial offering called Qubicle, which can be leveraged for call queues. You can also simulate queues with conferences.&lt;/li&gt;

&lt;li&gt;The 'User' endpoint is the ability to associate multiple devices with a user and ring all relevant devices when the user is dialed. Most providers allow you to specify multiple endpoints to dial but that association must be tracked on your side. With Kazoo, you can create a User and use that in your callflow instead.&lt;/li&gt;

&lt;li&gt;&lt;p&gt;
The 'Group' endpoint is similar to the User in that you can add devices and users to a group and all associated devices will be rung.
&lt;/p&gt;

&lt;p&gt;
The biggest differentiate among the providers in this case comes down to the need for registration, insight into why a call failed to connect (some providers are quite opaque about the reason for failure), and additional features offered on each endpoint type (like setting custom ringtones).
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-orgea777cb" class="outline-4"&gt;
&lt;h4 id="orgea777cb"&gt;Caller interactions&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-orgea777cb"&gt;
&lt;p&gt;
Most providers allow you to interact with the caller by playing audio files, generating dynamic text to read to the caller (TTS - text to speech), and collecting touch tones (DTMF) from the caller.
&lt;/p&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-left"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-left"&gt;Action&lt;/td&gt;
&lt;td class="org-center"&gt;Kazoo&lt;/td&gt;
&lt;td class="org-center"&gt;Twilio&lt;/td&gt;
&lt;td class="org-center"&gt;Plivo&lt;/td&gt;
&lt;td class="org-center"&gt;Nexmo&lt;/td&gt;
&lt;td class="org-center"&gt;Tropo&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Collect DTMF&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Play Audio (MP3 or WAV)&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;TTS&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-org1940181" class="outline-4"&gt;
&lt;h4 id="org1940181"&gt;Other basics offered&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org1940181"&gt;
&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-left"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;

&lt;col class="org-center"&gt;
&lt;/colgroup&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-left"&gt;Action&lt;/td&gt;
&lt;td class="org-center"&gt;Kazoo&lt;/td&gt;
&lt;td class="org-center"&gt;Twilio&lt;/td&gt;
&lt;td class="org-center"&gt;Plivo&lt;/td&gt;
&lt;td class="org-center"&gt;Nexmo&lt;/td&gt;
&lt;td class="org-center"&gt;Tropo&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Hangup a call&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Pause execution&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Record the caller&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Redirect to different URL&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-left"&gt;Reject a call&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;td class="org-center"&gt;X&lt;/td&gt;
&lt;td class="org-center"&gt;✔&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-org5d78cf4" class="outline-4"&gt;
&lt;h4 id="org5d78cf4"&gt;Kazoo Differentiators&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org5d78cf4"&gt;
&lt;p&gt;
While CPaaS allows you to interact with phone calls at a higher level than processing SIP packets, the features above are fairly low-level as far as business concerns go. They make for great demos and getting a prototype out the door quickly.
&lt;/p&gt;

&lt;p&gt;
Invariably, once you gain traction, feature requests come in and more functionality is needed. The question to ask is, do you have to do the work to implement the functionality or is it provided already?
&lt;/p&gt;

&lt;p&gt;
For instance, say your prototype connects the caller, based on some fancy criteria, to an agent/sales person/fortune teller. You gain traction and now your callees (the agents/sales people/fortune tellers) would love to have a voicemail service in case they can't answer the phone (since most are using a personal mobile phone and don't want the caller to hear their personal voicemail).
&lt;/p&gt;

&lt;p&gt;
Do you want to build a voicemail box?
&lt;/p&gt;

&lt;p&gt;
With Kazoo, you can configure a voicemail box via API and add it to the callflow with minimal effort. With the other providers, you're looking at having to get the IVR right, get the prompts recorded, test all the paths through a voicemail box, figure out storage of the voicemail recordings, build an API for providing the voicemail messages, build voicemail-&amp;gt;email and attach the recording, archive/delete old/deleted messages, and more. This is not quick to build and, frankly, most people don't consider voicemail a feature worth paying much, if anything, for; is it a good use of your time?
&lt;/p&gt;

&lt;p&gt;
Kazoo offers a number of callflow actions, like voicemail, that allow you to add higher level, business-focused, actions to your call processing capabilities. A short list of highlights:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;Call Parking&lt;/li&gt;
&lt;li&gt;Directed / Group Call Pickup&lt;/li&gt;
&lt;li&gt;Eavesdrop&lt;/li&gt;
&lt;li&gt;Faxing&lt;/li&gt;
&lt;li&gt;Hotdesking&lt;/li&gt;
&lt;li&gt;IVRs&lt;/li&gt;
&lt;li&gt;Manual presence updates&lt;/li&gt;
&lt;li&gt;Time-of-day routing&lt;/li&gt;
&lt;li&gt;Voicemail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Each of these features represents days to weeks of work to implement yourself (if you even can given the primitives offered by the other providers) and each has its own set of edge cases and intricacies that come with it. Instead, why not use the pre-built, heavily-tested callflow actions provided by Kazoo?
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-org4437983" class="outline-3"&gt;
&lt;h3 id="org4437983"&gt;API control&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org4437983"&gt;
&lt;p&gt;
The single largest difference between Kazoo's REST API and other providers is the sheer breadth of APIs exposed by Kazoo. Remember, Kazoo is both a CPaaS and UCaaS platform; on top of that, everything is API driven so you while you might start with the CPaaS-specific features, you can easily grow into UCaaS type services (like office PBX features).
&lt;/p&gt;
&lt;/div&gt;

&lt;div id="outline-container-org9677849" class="outline-4"&gt;
&lt;h4 id="org9677849"&gt;Call Control APIs&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org9677849"&gt;
&lt;p&gt;
Each provider allows you to list and control active calls. What controls you have vary; some allow you to redirect the executing callflow to your server for new callflow instructions; others expose the actions you can overlay on the call via the API and that's it.
&lt;/p&gt;

&lt;p&gt;
Similarly, conferences and conference participants can be managed through most providers' APIs.
&lt;/p&gt;

&lt;p&gt;
All in all, there isn't much that differentiates the basics among providers.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-org9c57188" class="outline-4"&gt;
&lt;h4 id="org9c57188"&gt;Kazoo API Differentiators&lt;/h4&gt;
&lt;div class="outline-text-4" id="text-org9c57188"&gt;
&lt;/div&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;&lt;a id="org1627114"&gt;&lt;/a&gt;Multi-tenancy&lt;br&gt;
&lt;div class="outline-text-6" id="text-org1627114"&gt;
&lt;p&gt;
Kazoo is designed to be multi-tenant; you can have as deep or as broad of an account tree as you like. Most providers, like Twilio, Nexmo, and Tropo, don't provide multi-tenancy at all (Plivo does appear to though). As a service integrator, you will have to manage that hierarchy on your side. This means you have to build infrastructure to manage billing, resource usage, configuration, etc.
&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;

&lt;li&gt;&lt;a id="org2df1e35"&gt;&lt;/a&gt;Carrier management&lt;br&gt;
&lt;div class="outline-text-6" id="text-org2df1e35"&gt;
&lt;p&gt;
Kazoo allows you to define your own upstream carriers, so even if you're using a hosted instance of Kazoo (versus running it yourself), you should be able to add your own carriers to you account and use them instead of the system's carriers. You can even setup hybrid situations where you route certain destinations to your carriers and use the system's as the default carrier group.
&lt;/p&gt;

&lt;p&gt;
None of the other CPaaS providers offer BYOC (Bring Your Own Carrier). This means that as you grow your voice traffic, you will not have levers to pull to manage costs. With BYOC, when it makes sense to, you can find your own deals on connectivity and seamlessly switch to your own carriers to save costs.
&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;

&lt;li&gt;&lt;a id="org996d0ae"&gt;&lt;/a&gt;Finer-grained features&lt;br&gt;
&lt;div class="outline-text-6" id="text-org996d0ae"&gt;
&lt;p&gt;
Each API entity (such as devices or conferences) in Kazoo offers significantly more functionality compared to the other providers' equivalent entities. Of course there are some similarities but, for instance looking at a "device" in Kazoo, you can optionally define:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;Call forwarding settings, including for failover (or just straight call-forwarding)&lt;/li&gt;
&lt;li&gt;Call recording (both inbound and outbound) for the device&lt;/li&gt;
&lt;li&gt;Call restrictions based on number classifiers (can call US DIDs, can't call international, etc)&lt;/li&gt;
&lt;li&gt;Caller ID settings&lt;/li&gt;
&lt;li&gt;Dialplan settings (allow users to dial shorter numbers and "fix" them - for instance the US used to allow 7-digit dialing and the area code was assumed based on the phone's location).&lt;/li&gt;
&lt;li&gt;Do Not Disturb settings&lt;/li&gt;
&lt;li&gt;Codecs available (both audio and video)&lt;/li&gt;
&lt;li&gt;Custom music on hold&lt;/li&gt;
&lt;li&gt;Custom presence ID (instead of the SIP username)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
There are other Kazoo-specific features on devices as well. Since users can own devices, you can set similar properties on the user and have them propagate to all devices (and define properties on the devices to override user settings).
&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;

&lt;li&gt;&lt;a id="orgc303c77"&gt;&lt;/a&gt;Class-4 and Class-5 Switching&lt;br&gt;
&lt;div class="outline-text-6" id="text-orgc303c77"&gt;
&lt;p&gt;
CPaaS providers are typically concerned with a handful of phone system features - dialing endpoints, conferences, call recording, call queue, and phone number management most commonly.
&lt;/p&gt;

&lt;p&gt;
Kazoo, being a hybrid class-4 and class-5 switch, offers so much more:
&lt;/p&gt;

&lt;p&gt;
PBX functionality includes configuring users, devices, and callflows, blacklists, directories, faxes and faxboxes, IVRs, voicemail, and more.
&lt;/p&gt;

&lt;p&gt;
Configure connectivity features like carriers (resources in Kazoo parlance), ratedecks, PBX trunking.
&lt;/p&gt;

&lt;p&gt;
Configure reseller features like branding email notification templates and white-labeling Kazoo services.
&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;

&lt;div id="outline-container-orgaa9e36a" class="outline-3"&gt;
&lt;h3 id="orgaa9e36a"&gt;Questions to ask a CPaaS provider&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-orgaa9e36a"&gt;
&lt;p&gt;
These are some good questions to ask your CPaaS provider(s) and to ask yourself how important the answers are to your needs:
&lt;/p&gt;

&lt;ul class="org-ul"&gt;
&lt;li&gt;Where are the voice servers located, geographically?&lt;/li&gt;
&lt;li&gt;Where are the API servers located? Hint, some providers may have voice spread out around the world but API servers only in geographic location.&lt;/li&gt;
&lt;li&gt;Where are the servers located (the data centers) relative to your users? To your carriers?&lt;/li&gt;
&lt;li&gt;How is the networking built? Some hosted infrastructure providers, like AWS/GCE aren't well suited to VoIP traffic - are CPaaS providers piggybacking off that infrastructure or are they building out their own racks?&lt;/li&gt;
&lt;li&gt;Is the CPaaS provider reselling another provider's service with better lipstick?&lt;/li&gt;
&lt;li&gt;Can you extend the platform?&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id="outline-container-org455829e" class="outline-2"&gt;
&lt;h2 id="org455829e"&gt;Conclusion&lt;/h2&gt;
&lt;div class="outline-text-2" id="text-org455829e"&gt;
&lt;p&gt;
The market has spoken and they want UCaaS+CPaaS offered under one roof. The big UCaaS players have bought their CPaaS plays and are busy integrating them into the fold with varying success. Meanwhile Twilio remains an outlier and looks to be doubling down on being the CPaaS for the masses.
&lt;/p&gt;

&lt;p&gt;
Kazoo offers you CPaaS + UCaaS from day one in a cohesive system. You can use as many or as few APIs and callflow actions as you need to accomplish your goals, while knowing that as you grow and need new functionality, Kazoo is there ready to provide it. On the off chance Kazoo isn't able to provide a feature, you can rest easy knowing the open-source nature of the project gives you the option to build it yourself and keep it private, build it and release it upstream for all to benefit, or support the project by contracting with 2600Hz to build the functionality for you.
&lt;/p&gt;
&lt;/div&gt;

&lt;div id="outline-container-org19b8876" class="outline-3"&gt;
&lt;h3 id="org19b8876"&gt;Ready to kick the tires?&lt;/h3&gt;
&lt;div class="outline-text-3" id="text-org19b8876"&gt;
&lt;ul class="org-ul"&gt;
&lt;li&gt;Check out the &lt;a href="https://2600hz.com/architecture"&gt;architecture page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Spin up a CentOS 7 virtual machine and &lt;a href="https://docs.2600hz.com/sysadmin/doc/install/install_via_centos7/"&gt;install Kazoo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Contact &lt;a href="mailto:sales@2600hz.com"&gt;sales&lt;/a&gt; for a demo.&lt;/li&gt;
&lt;li&gt;Check out the &lt;a href="https://github.com/2600hz/kazoo"&gt;Git repo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Sign up for the &lt;a href="https://forums.2600hz.com/forums"&gt;forums&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description><category>2600Hz</category><category>CPaaS</category><category>kazoo</category><category>Work</category><guid>https://jamesaimonetti.com/posts/comparing-kazoo-and-other-cpaas-solutions/</guid><pubDate>Thu, 31 May 2018 22:07:29 GMT</pubDate></item><item><title>Using ibrowse to POST form data</title><link>https://jamesaimonetti.com/posts/2012/04/26/using-ibrowse-to-post-form-data/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
It is not immediately obvious how to use
&lt;a href="https://github.com/cmullaparthi/ibrowse"&gt;ibrowse&lt;/a&gt; to send an HTTP
POST request with form data (perhaps to simulate a web form post). Turns
out its pretty simple:
&lt;/p&gt;

&lt;pre class="example" id="org334f119"&gt;
ibrowse:send_req(URI, [{"Content-Type", "application/x-www-form-urlencoded"}], post, FormData)
&lt;/pre&gt;

&lt;p&gt;
Where URI is where you want to send the request
("&lt;a href="http://some.server.com/path/to/somewhere.php"&gt;http://some.server.com/path/to/somewhere.php&lt;/a&gt;") and FormData is an
iolist() of URL-encoded values ("foo=bar&amp;amp;fizz=buzz"). There's obviously
a lot more that can be done, but for a quick snippet, this is pretty
sweet.
&lt;/p&gt;</description><category>Research</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2012/04/26/using-ibrowse-to-post-form-data/</guid><pubDate>Thu, 26 Apr 2012 15:22:31 GMT</pubDate></item><item><title>Emulating Webmachine's {halt, StatusCode} in Cowboy</title><link>https://jamesaimonetti.com/posts/2012/03/15/emulating-webmachines-halt-statuscode-in-cowboy/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
At &lt;a href="http://2600hz.org"&gt;2600Hz&lt;/a&gt;, we recently converted our REST
webserver from
&lt;a href="https://github.com/mochi/mochiweb"&gt;Mochiweb&lt;/a&gt;/&lt;a href="https://github.com/basho/webmachine"&gt;Webmachine&lt;/a&gt;
to &lt;a href="https://github.com/extend/cowboy"&gt;Cowboy&lt;/a&gt;, with
&lt;a href="https://github.com/extend/cowboy/blob/master/src/cowboy_http_rest.erl"&gt;cowboy\&lt;sub&gt;http&lt;/sub&gt;\&lt;sub&gt;rest&lt;/sub&gt;&lt;/a&gt;
giving us a comparable API to process our REST requests with. One
feature that was missing, however, was an equivalent to Webmachine's
&lt;a href="http://wiki.basho.com/Webmachine-Resource.html#Halting-Resources"&gt;{halt,
StatusCode}&lt;/a&gt; return. While there has been
&lt;a href="https://github.com/extend/cowboy/pull/144"&gt;chatter&lt;/a&gt; about adding this
to cowboy\&lt;sub&gt;http&lt;/sub&gt;\&lt;sub&gt;rest&lt;/sub&gt;, we've got a function that emulates the behaviour
pretty well (this is cleaned up a bit from our actual function, removing
project-specific details).
&lt;/p&gt;

&lt;pre class="example" id="org567cd62"&gt;
-spec halt/4 :: (#http_req{}, integer(), iolist(), #state{}) -&amp;gt; {'halt', #http_req{}, #state{}}.
halt(Req0, StatusCode, RespContent, State) -&amp;gt;
    {ok, Req1} = cowboy_http_req:set_resp_body(Content, Req0),
    {ok, Req2} = cowboy_http_req:reply(StatusCode, Req1),
    {halt, Req2, State}.
&lt;/pre&gt;

&lt;p&gt;
Obviously you can omit setting the response body if you don't plan to
return one.
&lt;/p&gt;</description><category>Geekdom</category><category>Research</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2012/03/15/emulating-webmachines-halt-statuscode-in-cowboy/</guid><pubDate>Thu, 15 Mar 2012 12:26:05 GMT</pubDate></item><item><title>CouchDB/BigCouch Bulk Insert/Update</title><link>https://jamesaimonetti.com/posts/2012/01/27/couchdbbigcouch-bulk-insertupdate/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
While writing a bulk importer for Crossbar, I took a look at squeezing
some performance out of BigCouch for the actual inserting of documents
into the database. My first time running all the documents into BigCouch
at the same time resulted in some poor performance, so I went digging
around for some ideas on how to improve the insertions. Reading up on
the &lt;a href="http://guide.couchdb.org/draft/performance.html"&gt;High Performance
Guide&lt;/a&gt; for CouchDB (which BigCouch is API-compliant with), I started to
play with chunking my inserts up to get better overall execution time.
&lt;b&gt;Note&lt;/b&gt;: the following are very unscientific results, but I think are
fairly instructive for what one might expect.
&lt;/p&gt;

&lt;table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides"&gt;


&lt;colgroup&gt;
&lt;col class="org-right"&gt;

&lt;col class="org-right"&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope="col" class="org-right"&gt;Docs Per Insertion&lt;/th&gt;
&lt;th scope="col" class="org-right"&gt;Elapsed Time (ms)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class="org-right"&gt;26618&lt;/td&gt;
&lt;td class="org-right"&gt;107176&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;1000&lt;/td&gt;
&lt;td class="org-right"&gt;8325&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;1500&lt;/td&gt;
&lt;td class="org-right"&gt;5679&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;2000&lt;/td&gt;
&lt;td class="org-right"&gt;3087&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;2500&lt;/td&gt;
&lt;td class="org-right"&gt;1644&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class="org-right"&gt;Docs Per Insertion&lt;/td&gt;
&lt;td class="org-right"&gt;Elapsed Time (ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Based on the CouchDB guide, I decided to not pursue this further, as
dropping insertion time 2 orders of magnitude was fine enough for me! I
may have to bake this into the platform natively. For those interested
in the Erlang code, it is pretty simple. Taking a list of documents to
save, use lists:split/2 to try and split the list. By catching the
error, we can know that the list is less than our threshold, and can
save the remaining list to BigCouch. Otherwise, lists:split/2 chunks our
list into one for saving, and one for recursing back into the function.
Since we don't really care about the results of couch\&lt;sub&gt;mgr&lt;/sub&gt;:save\&lt;sub&gt;docs&lt;/sub&gt;/2,
we could put the calls in the second clause of the case in a spawn to
speed this up (relative to the calling process).
&lt;/p&gt;

&lt;pre class="example" id="orgf019908"&gt;
-spec save_bulk_rates/1 :: (wh_json:json_objects()) -&amp;gt; no_return().
save_bulk_rates(Rates) -&amp;gt;
    case catch(lists:split(?MAX_BULK_INSERT, Rates)) of
        {'EXIT', _} -&amp;gt;
            couch_mgr:save_docs(?WH_RATES_DB, Rates);
        {Save, Cont} -&amp;gt;
            couch_mgr:save_docs(?WH_RATES_DB, Save),
            save_bulk_rates(Cont)
    end.
&lt;/pre&gt;</description><category>Geekdom</category><category>Research</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2012/01/27/couchdbbigcouch-bulk-insertupdate/</guid><pubDate>Fri, 27 Jan 2012 19:57:28 GMT</pubDate></item><item><title>cURL stripping newlines from your CSV or other file?</title><link>https://jamesaimonetti.com/posts/2012/01/26/curl-stripping-newlines-from-your-csv-or-other-file/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
I'm in the process of writing a REST endpoint for uploading CSVs to
&lt;a href="http://wiki.2600hz.org/display/docs/REST+APIs+%28High-Level+Functionality%29"&gt;Crossbar&lt;/a&gt;
as part of &lt;a href="http://github.com/2600hz/2600hz-platform"&gt;our
communications platform&lt;/a&gt; at &lt;a href="http://2600hz.org"&gt;2600hz&lt;/a&gt;. Not wanting
to invoke the full REST client interface, I generally use
&lt;a href="http://curl.haxx.se/"&gt;cURL&lt;/a&gt; to send the HTTP requests. Today,
however, I had quite the time figuring out why my CSV files were being
stripped of their newline characters. The initial invocation:
&lt;/p&gt;

&lt;pre class="example" id="org25a3b6b"&gt;
$&amp;gt; curl http://localhost:8000/v1/path/to/upload -H "Content-Type: text/csv" -X POST -d @file.csv
&lt;/pre&gt;

&lt;p&gt;
Walking through the code, from where I was processing the CSV down to
the webserver handling the connection itself, looking for who was
stripping the newlines, I determined it was coming in sans-newlines and
decided to check out &lt;a href="http://curl.haxx.se/docs/manpage.html"&gt;cURL's man
pages&lt;/a&gt; for what might be amiss. I quickly found that the
&lt;a href="http://curl.haxx.se/docs/manpage.html#-d"&gt;-d option&lt;/a&gt; was treating the
file as ascii, and although the docs don't explicitly say so, it appears
this option will strip the newlines. The resolution is to use the
&lt;a href="http://curl.haxx.se/docs/manpage.html#--data-binary"&gt;–data-binary
flag&lt;/a&gt; so cURL doesn't touch the file before sending it to the server.
&lt;/p&gt;</description><category>Geekdom</category><category>Gotchas</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2012/01/26/curl-stripping-newlines-from-your-csv-or-other-file/</guid><pubDate>Thu, 26 Jan 2012 19:33:43 GMT</pubDate></item><item><title>Cron and infinite loops do not mix</title><link>https://jamesaimonetti.com/posts/2011/03/09/cron-and-infinite-loops-do-not-mix/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
More "expert" code time! From the "expert":
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;
Please put this script in a cron to run every minute
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;pre class="example" id="org76dd61f"&gt;
while true; do
  rsync -a server:remote_dir local_dir
  sleep $freq
done
&lt;/pre&gt;

&lt;p&gt;
local\&lt;sub&gt;dir&lt;/sub&gt; is going to be really, really, really up to date after a few
minutes…the server crash will be epic. Perhaps we should write a
script to find and kill these rogue processes and run it every minute
too, but stagger it with the other cron…
&lt;/p&gt;</description><category>Geekdom</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2011/03/09/cron-and-infinite-loops-do-not-mix/</guid><pubDate>Wed, 09 Mar 2011 17:50:00 GMT</pubDate></item><item><title>You get paid for this?</title><link>https://jamesaimonetti.com/posts/2011/03/07/you-get-paid-for-this/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
Spotted in some high-priced "expert"'s code:
&lt;/p&gt;

&lt;pre class="example" id="org8b4185f"&gt;
switch ($retcode)
{
    case -1:
    case -3:
        if ($retcode==-1)
            log("SOME_CODE", "SOME MSG");
        else
            log("SOME_OTHER_CODE", "SOME OTHER MSG");
...
&lt;/pre&gt;</description><category>Fun</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2011/03/07/you-get-paid-for-this/</guid><pubDate>Mon, 07 Mar 2011 13:08:37 GMT</pubDate></item><item><title>Resolving Dialyzer "Function foo/n has no local return" errors</title><link>https://jamesaimonetti.com/posts/2010/11/23/resolving-dialyzer-function-foon-has-no-local-return-errors/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
&lt;a href="http://www.erlang.org/doc/man/dialyzer.html"&gt;Dialyzer&lt;/a&gt; is a great
static analysis tool for &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; and has
helped me catch many bugs related to what types I thought I was passing
to a function versus what actually gets passed. Some of the errors
Dialyzer emits are rather cryptic at first (as seems commonplace in the
Erlang language/environment in general) but after you understand the
causes of the errors, the fix is easily recognized. My most common error
is Dialyzer inferring a different return type that what I put in my
-spec, followed by Dialyzer telling me the same function has no local
return. An example:
&lt;/p&gt;

&lt;pre class="example" id="orgcc211f2"&gt;
foo.erl:125: The specification for foo:init/1 states that the function might also return {'ok',tuple()} but the inferred return is none()
foo.erl:126: Function init/1 has no local return
&lt;/pre&gt;

&lt;p&gt;
The init/1 function (for a gen\&lt;sub&gt;server&lt;/sub&gt;, btw):
&lt;/p&gt;

&lt;pre class="example" id="org59c92ff"&gt;
-spec(init/1 :: (Args :: list()) -&amp;gt; tuple(ok, tuple())).
init(_) -&amp;gt;
  {ok, #state{}}.
&lt;/pre&gt;

&lt;p&gt;
And the state record definition:
&lt;/p&gt;

&lt;pre class="example" id="orge27d458"&gt;
-record(state, {
  var_1 = {} :: tuple(string(), tuple())
  ,var_2 = [] :: list(tuple(string(), tuple()))
}).
&lt;/pre&gt;

&lt;p&gt;
Spot the error? In the record definition, var\&lt;sub&gt;1&lt;/sub&gt; is initialized to an
empty tuple and var\&lt;sub&gt;2&lt;/sub&gt; is initialized to an empty list, yet the spec
typing for the record does not take that into account. The corrected
version:
&lt;/p&gt;

&lt;pre class="example" id="org79b1e1f"&gt;
-record(state, {
  var_1 = {} :: tuple(string(), tuple()) | {}
  ,var_2 = [] :: list(tuple(string(), tuple())) | []
}).
&lt;/pre&gt;

&lt;p&gt;
And now Dialyzer stops emitting the spec error and the no local return
error.
&lt;/p&gt;</description><category>Geekdom</category><category>Research</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2010/11/23/resolving-dialyzer-function-foon-has-no-local-return-errors/</guid><pubDate>Tue, 23 Nov 2010 12:03:31 GMT</pubDate></item><item><title>Still Kicking</title><link>https://jamesaimonetti.com/posts/2010/09/17/still-kicking/</link><dc:creator>James Aimonetti</dc:creator><description>&lt;p&gt;
I am still alive and well; just busy. I did write a
&lt;a href="http://www.2600hz.org/blog/"&gt;blog entry&lt;/a&gt; for my company,
&lt;a href="http://www.2600hz.com"&gt;2600hz&lt;/a&gt;. More to come…eventually.
&lt;/p&gt;</description><category>News</category><category>Work</category><guid>https://jamesaimonetti.com/posts/2010/09/17/still-kicking/</guid><pubDate>Fri, 17 Sep 2010 13:06:14 GMT</pubDate></item></channel></rss>