UPDATE: 9/26/2012: After requesting an API Key with the demo account I set up, they sent me one along with may account_id. I dropped the code in below and it worked like a charm for adding a member.
Let me start by saying that none of this code is tested (see update above). I don't have a MyEmma account, and apparently you have to be a paying customer for an account_id to use the API. That blows! But this should get you real close and may give you some ideas for encapsulating logic, which has become my obsession.
Secondly, I realize this post is 9 months old and you have probably either long figured it out, or won the lottery and are running the place by now. So no one may ever even see this post. But I was looking for some answers myself and ran across it... and since formulating and parsing JSON is part of my daily life, this is something I always need to keep setting myself straight on. So what turned out to be a quick answer to your question, became a late night, self serving, obsessive challenge. At any rate...
...what you are doing with JSON, is creating client side nested structures. You have the root structure with two key-value pairs (fields and email). Then the structure 'fields' holds a structure with the a key-value pair you are sending over for that email address (first_name). Presumably you can send more.
You are building nested structures. Remember that a key in a structure can hold a structure. And those keys can hold structures, and so on. It can get as dark and nasty as you want to go. But that's all JSON is... it's a client side object.
So here is your data build and JSON object...
<cfscript>
variables.dataFields = {};
variables.dataFields['fields'] = {};
variables.dataFields['email'] = "[email protected]";
variables.dataFields.fields['first_name'] = "myFirstName";
variables.dataFields = serializejson(variables.dataFields);
</cfscript>
Note that I'm explicitly setting the structure key names with array notation. We have to do this to control the case with Coldfusion. Otherwise, the keys will be in all caps... not want we want for case sensitive JavaScript. This could be part of the problem you are having.
If Emma doesn't understand because of case, then you would get your...
{"error": "Unable to parse JSON request"}
But when we explicitly set our key names using array notation, and then serialize our object, we get nice and pretty, good ol' fashion JSON...
{"fields":{"first_name":"myFirstName"},"email":"[email protected]"}
So below, I put our http request to Emma in a function. It is also very important to set the Content-Type header as application/json, so the browser will send it as a object and not just a text string. And we are sending our JSON as the body of our request, not in a form field called 'fields'... hopefully that makes sense when you say it out loud. Here's the function...
<cffunction name="callEmma" access="private" displayname="CallEmma" description="This makes an HTTP REQUEST to MyEmma" returnformat="JSON" output="false" returntype="Any">
<cfargument name="endpoint" required="true" type="string" displayname="EndPoint">
<cfargument name="PUBLIC_API_KEY" required="true" type="string" displayname="PUBLIC_API_KEY">
<cfargument name="PRIVATE_API_KEY" required="true" type="string" displayname="PRIVATE_API_KEY">
<cfargument name="dataFields" required="true" type="struct" displayname="DataFields">
<cfscript>
local = {};
local.baseURL = "https://api.e2ma.net/";
local.account_id = "12345";
local.phoneNumber = local.baseURL & local.account_id & arguments.endPoint;
local.connection = new http();
local.connection.setMethod("POST");
local.connection.setUrl(local.phoneNumber);
local.connection.setUsername(arguments.PUBLIC_API_KEY);
local.connection.setPassword(arguments.PRIVATE_API_KEY);
local.connection.setUserAgent(cgi.http_user_agent);
local.connection.addParam(type="header",name="Content-Type", value="application/json");
local.connection.addParam(type="body", value=arguments.dataFields);
local.objGet = local.connection.send().getPrefix();
local.content = local.objGet.filecontent;
return local.content
</cfscript>
</cffunction>
Then once again, here is our JSON build (nested structures)...
<cfscript>
variables.dataFields = {};
variables.dataFields['fields'] = {};
variables.dataFields['email'] = "[email protected]";
variables.dataFields.fields['first_name'] = "myFirstName";
variables.dataFields = serializejson(variables.dataFields);
</cfscript>
Then we set the variables to pass to the function...
<cfscript>
variables.entryPoint = "/members/add";
variables.PUBLIC_API_KEY= "PUBLIC_API_KEY";
variables.PRIVATE_API_KEY= "PRIVATE_API_KEY";
</cfscript>
Then make the phone call...
<cfscript>
variables.myResponse = callEmma(variables.entryPoint,variables.PUBLIC_API_KEY,variables.PRIVATE_API_KEY,variables.dataFields);
variables.myResponse = deserializejson(variables.myResponse);
</cfscript>
We then take our response, deserialize it, and output the variables however we want.
<cfscript>
if(variables.myResponse.added){
writeoutput("Member " & variables.myResponse.member_id & " added!");
}
else{
writeoutput("There was an error adding this member");
}
</cfscript>
Anymore, I generally use <cfscript>
as much as I can. It's easier to read and it makes me feel much smarter than I really am. So when we put it all together, for cut-and-paste, we have this...
<cfscript>
// Function to make our calls to Emma
private any function callEmma(required string endPoint,required string PUBLIC_API_KEY,required string PRIVATE_API_KEY,required string dataFields)
description="This makes an HTTP REQUEST to MyEmma"
displayname="CallEmma"
returnformat="JSON"
output="false"
{
local = {};
local.baseURL = "https://api.e2ma.net/";
local.account_id = "12345";
local.phoneNumber = local.baseURL & local.account_id & arguments.endPoint;
local.connection = new http();
local.connection.setMethod("POST");
local.connection.setUrl(local.phoneNumber);
local.connection.setUsername(arguments.PUBLIC_API_KEY);
local.connection.setPassword(arguments.PRIVATE_API_KEY);
local.connection.setUserAgent(cgi.http_user_agent);
local.connection.addParam(type="header",name="Content-Type", value="application/json");
local.connection.addParam(type="body",value=arguments.dataFields);
local.objGet = local.connection.send().getPrefix();
local.content = local.objGet.filecontent;
return local.content;
}
// Put our data together
variables.dataFields = {};
variables.dataFields['fields'] = {};
variables.dataFields['email'] = "[email protected]";
variables.dataFields.fields['first_name'] = "myFirstName";
variables.dataFields = serializejson(variables.dataFields);
// Define the parameters for our call to Emma
variables.entryPoint = "/members/add";
variables.PUBLIC_API_KEY= "PUBLIC_API_KEY";
variables.PRIVATE_API_KEY= "PRIVATE_API_KEY";
// Call Emma
variables.myResponse = callEmma(variables.entryPoint,variables.PUBLIC_API_KEY,variables.PRIVATE_API_KEY,variables.dataFields);
variables.myResponse = deserializejson(variables.myResponse);
//Output to browser
if(variables.myResponse.added){
writeoutput("Member " & variables.myResponse.member_id & " added!");
}
else{
writeoutput("There was an error adding this member");
}
</cfscript>
My GOD! I've been writing WAY too many API's... I clearly need therapy!