Quantcast
Channel: API – Marketo Developers
Viewing all 11 articles
Browse latest View live

Sending Transactional Emails with the Marketo REST API: Part 1

$
0
0

A common use case for the Marketo API is to trigger the sending of transactional emails to specific records via the Request Campaign API call.  You can find an example covering this use case with the SOAP API here.  There are a few configuration requirements within Marketo in order to execute the required call with the Marketo REST API.

  • The recipient must have a record within Marketo
  • There needs to be a Transactional Email created and approved in your Marketo instance.
  •  There needs to be an active trigger campaign with the Campaign is Requested, Source: Web Service API, that is set up to send the email

First create and approve your email.  If the email is truly transactional, you will likely need to set it to operational, but be sure that it legally qualifies as operational.  This is configured from with the Edit Screen under Email Actions > Email Settings:

Request Campaign - Email Settings

Request Campaign - Operational

Approve it and we’re ready to create our campaign:

RequestCampaign - Approve Draft

If you’re new to creating campaigns, check out the Create a New Smart Campaign article on docs.marketo.com.  Once you’ve created your campaign, we need to go through these steps.  Configure your Smart List with the Campaign is Requested trigger: Request Campaign - Smart List

Now we need to configure the flow to point a Send Email step to our email: Request Campaign - Flow

Before activation, you’ll need to decide on some settings in the Schedule tab.  If this particular email should only ever be sent once to a given record, then leave the qualification settings as is.  If it’s required that they receive the email multiple times, though, you’ll want to adjust this to either every time or to one of the available cadences: Qualification Rules

Now we’re ready to activate:

Request Campaign - Schedule

Sending the API Calls

Note: In the Java examples below, we’ll be using the minimal-json package to handle JSON representations in our code.  You can read more about this project here: https://github.com/ralfstx/minimal-json

The first part of sending a transactional email through the API is ensuring that a record with the corresponding email address exists in your Marketo instance and that we have access to its lead ID.  For the purposes of this post, we will assume that the email addresses are in Marketo already, and we only need to retrieve the ID of the record.  For this, we will be using the Get Multiple Leads by Filter Type call, and we will be reusing some of the Java code from the previous post on authenticating and retrieving lead data from Marketo. Let’s take a look at our Main method for to request the campaign:

package dev.marketo.blog_request_campaign;

import com.eclipsesource.json.JsonArray;

public class App 
{
    public static void main( String[] args )
    {
    	//Create an instance of Auth so that we can authenticate with our Marketo instance
        Leads leadsRequest = new Leads(auth).setFilterType("email").addFilterValue("requestCampaign.test@marketo.com");

        //Create and parameterize an instance of Leads
        //Set your email filterValue appropriately
        Leads leadsRequest = new Leads(auth).setFilterType("email").addFilterValue("test.requestCamapign@example.com");

        //Get the inner results array of the response
        JsonArray leadsResult = leadsRequest.getData().get("result").asArray();

        //Get the id of the record indexed at 0
        int lead = leadsResult.get(0).asObject().get("id").asInt();

        //Set the ID of your campaign from Marketo
        int campaignId = 0;
        RequestCampaign rc = new RequestCampaign(auth, campaignId).addLead(lead);

        //Send the request to Marketo
        rc.postData();
    }
}

To get to these results from the JsonObject response of leadsRequest, we’ll need to write some code .  To retrieve the first result in the Array, we need to extract the Array from the JsonObject and get the object indexed at 0:

JsonArray leadsResult = leadsRequest.getData().get("result").asArray();
int leadId = leadsResult.get(0).asObject().get("id").asInt();

From here now all we need to do is the Request Campaign call.  For this, the required parameters are ID in the URL of the request, and an array of JSON objects containing one member, “id.”  Let’s take a look at the code for this:

package dev.marketo.blog_request_campaign;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import javax.net.ssl.HttpsURLConnection;
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;

public class RequestCampaign {
	private String endpoint;
	private Auth auth;
	public ArrayList leads = new ArrayList();
	public ArrayList tokens = new ArrayList();
	
	public RequestCampaign(Auth auth, int campaignId) {
		this.auth = auth;
		this.endpoint = this.auth.marketoInstance + "/rest/v1/campaigns/" + campaignId + "/trigger.json";
	}
	public RequestCampaign setLeads(ArrayList leads) {
		this.leads = leads;
		return this;
	}
	public RequestCampaign addLead(int lead){
		leads.add(lead);
		return this;
	}
	public RequestCampaign setTokens(ArrayList tokens) {
		this.tokens = tokens;
		return this;
	}
	public RequestCampaign addToken(String tokenKey, String val){
		JsonObject jo = new JsonObject().add("name", tokenKey);
		jo.add("value", val);
		tokens.add(jo);
		return this;
	}
	public JsonObject postData(){
		JsonObject result = null;
		try {
			JsonObject requestBody = buildRequest(); //builds the Json Request Body
			String s = endpoint + "?access_token=" + auth.getToken(); //takes the endpoint URL and appends the access_token parameter to authenticate
			System.out.println("Executing RequestCampaign call\n" + "Endpoint: " + s + "\nRequest Body:\n"  + requestBody);
			URL url = new URL(s); 
			HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection(); //Return a URL connection and cast to HttpsURLConnection
			urlConn.setRequestMethod("POST");
			urlConn.setRequestProperty("Content-type", "application/json");
            urlConn.setRequestProperty("accept", "text/json");
            urlConn.setDoOutput(true);
			OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
			wr.write(requestBody.toString());
			wr.flush();
			InputStream inStream = urlConn.getInputStream(); //get the inputStream from the URL connection
			Reader reader = new InputStreamReader(inStream);
			result = JsonObject.readFrom(reader); //Read from the stream into a JsonObject
			System.out.println("Result:\n" + result);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	private JsonObject buildRequest(){
		JsonObject requestBody = new JsonObject(); //Create a new JsonObject for the Request Body
		JsonObject input = new JsonObject();
		JsonArray leadsArray = new JsonArray();
		for (int lead : leads) {
			JsonObject jo = new JsonObject().add("id", lead);
			leadsArray.add(jo);
		}
		input.add("leads", leadsArray);
		JsonArray tokensArray = new JsonArray();
		for (JsonObject jo : tokens) {
			tokensArray.add(jo);
		}
		input.add("tokens", tokensArray);
		requestBody.add("input", input);
		return requestBody;
	}

}

This class has one constructor taking an Auth, and the Id of the campaign.  Leads are added to the object either by passing an ArrayList<Integer> containing the Ids of the records to setLeads, or by using addLead, which takes one integer and appends it to the existing ArrayList in the leads property.  To trigger the API call to pass the lead records to the campaign, postData needs to be called, which returns a JsonObject containing the response data from the request.  When request campaign is called, every lead passed to the call will be processed by the target trigger campaign in Marketo and be sent the email which was created previously. Congratulations, you’ve triggered an email through the Marketo REST API.  Keep an eye out for Part 2 where we’ll look at dynamically customizing the content of an email through Request Campaign.

Read Part 2 here.


July 2015 Release Updates

$
0
0

REST API

  • Sales Person API

New sales person endpoints have been introduced that allow you to programmatically list, describe, and CRUD the data residing within a Marketo sales person object.  In addition, a sales person can be assigned to a lead, opportunity, or company.  This is done by specifying an “externalSalesPersonId” attribute when calling the Create/Update/Upsert endpoint for lead, opportunity, or company.

Note:  Role permissions were added to provide access to the Program endpoints: Read-Only Sales Person, Read-Write Sales Person.  If your API user role predates the release of the Sales Person APIs, then you will need to update your API user role with these permissions to enable access.  Otherwise, you will receive a 603 “Access Denied” error response.

  • Asset API – Landing Page Template

New landing page template endpoints have been introduced that allow you to programmatically list, create, and update the data associated with a landing page template.

  • Asset API – Segments

Two segment-related endpoints have been introduced:

Get Segments

Get Segmentation by Id

  • Fixed issue where Get Folder by Name was not honoring the “workSpace” parameter.  [LM-61059]
  • Made several performance improvements to the Custom Object APIs.

Sending Transactional Emails with the Marketo REST API: Part 2, Custom Content

$
0
0

Last time, we took a look at triggering transactional emails from outside of Marketo.  This week we’ll be looking at how to pass dynamic content to our emails via the Request Campaign API call.  Request Campaign not only allows the triggering of emails externally, but you can  also replace the content of My Tokens within an email.  My tokens are reusable content that can be customized at the program or marketing folder level.  These can also just exist as placeholders to be replaced through your request campaign call.  For instructions on configuring the smart campaign, see part one.

Building your Email

In order to customize our content, first we will need to configure a program and an email in Marketo.  To generate our custom content, we will need to create tokens inside of the program, and then place them into the email that we’re going to be sending.  For simplicity’s sake, we’ll be using just one token in this example, but you can replace any number of tokens in an email, in the From Email, From Name, Reply-to or any piece of content in the email.  So let’s create one token Rich Text for replacement and call it “bodyReplacement”.  Rich Text allows us to replace any content in the token with arbitrary HTML that we want to input. create a new token

Tokens can’t be saved while empty, so go ahead and insert some placeholder text here.  Now we need to insert our token into the email:

add token to email

This token will now be accessible for replacement through a Request Campaign call.  This token can be as simple as a single line of text which needs to be replaced on a per-email basis, or can include almost the entire layout of the email.

The Code

In previous posts, we’ve looked at authenticating and retrieving lead records, and then sending an email to those leads.  We’ll be expanding on the code from last week to pass in customized tokens to our request campaign call.

package dev.marketo.blog_request_campaign;

import com.eclipsesource.json.JsonArray;

public class App 
{
    public static void main( String[] args )
    {
    	//Create an instance of Auth so that we can authenticate with our Marketo instance
        Auth auth = new Auth("Client ID - CHANGE ME", "Client Secret - CHANGE ME", "Host - CHANGE ME");
        
        //Create and parameterize an instance of Leads
        Leads leadsRequest = new Leads(auth).setFilterType("email").addFilterValue("requestCampaign.test@marketo.com");
        
        //get the inner results array of the response
        JsonArray leadsResult = leadsRequest.getData().get("result").asArray();
        
        //get the id of the record indexed at 0
        int lead = leadsResult.get(0).asObject().get("id").asInt();
        
        //Set the ID of our campaign from Marketo
        int campaignId = 1578;
        RequestCampaign rc = new RequestCampaign(auth, campaignId).addLead(lead);

        //Create the content of the token here, and add it to the request
        String bodyReplacement = "<div class=\"replacedContent\"><p>This content has been replaced</p></div>";
        rc.addToken("{{my.bodyReplacement}}", bodyReplacement);
        rc.postData();
    }
}

If the code looks familiar, that’s because it only has two additional lines from the main method in the previous post.  This time we’re creating the content of our token in the bodyReplacement variable and then using the addToken method to add it to the request.  addToken takes a key and a value and then creates a JsonObject representation and adds it to the internal tokens array.  This is then serialized during the postData method and creates a body that looks like this:

{"input":{"leads":[{"id":1}],"tokens":[{"name":"{{my.bodyReplacement}}","value":"<div class=\"replacedContent\"><p>This content has been replaced</p></div>"}]}}

Combined, our console output looks like this:

Token is empty or expired. Trying new authentication
Trying to authenticate with ...
Got Authentication Response: {"access_token":"19d51b9a-ff60-4222-bbd5-be8b206f1d40:st","token_type":"bearer","expires_in":3565,"scope":"apiuser@mktosupport.com"}
Executing RequestCampaign call
Endpoint: .../rest/v1/campaigns/1578/trigger.json?access_token=19d51b9a-ff60-4222-bbd5-be8b206f1d40:st
Request Body:
{"input":{"leads":[{"id":1}],"tokens":[{"name":"{{my.bodyReplacement}}","value":"&lt;div class=\"replacedContent\"&gt;&lt;p&gt;This content has been replaced&lt;/p&gt;&lt;/div&gt;"}]}}
Result:
{"requestId":"1e8d#14eadc5143d","result":[{"id":1578}],"success":true}

Wrapping Up

This method is extensible in a multitude of ways, changing content in emails within individual layout sections, or outside emails, allowing custom values to be passed into tasks or interesting moments.  Anywhere a my token can be used from within a program can be customized using this method.  Similar functionality is also available with the Schedule Campaign call which will allow you to process tokens across an entire batch campaign.  These can’t be customized on a per lead basis, but are very useful for customizing content across a wide set of leads.

Synchronizing Lead Data Changes using REST API

$
0
0

Note: This is a blog post by David Everly. David is a Marketo Developer Evangelist based out of Portland, OR.

Over a year ago we posted Retrieving customer and prospect information from Marketo using the API.  That post presented a code sample that could be run on a recurring basis to poll Marketo for updates.  The idea was to use Marketo APIs to identify changes to lead data, and extract the lead data that had changed.  This data could then be pushed to an external system for synchronization purposes.

The code sample presented used our SOAP API.  Well we have a new way to walk, and that way is using the Marketo REST API.  This post will show you how to accomplish that same goal using two REST endpoints: Get Lead Changes, Get Lead by Id.

The program contains 2 main steps:

  1. Call Get Lead Changes to generate a list of all Lead Ids that either had specific lead fields changed, or were added during a given time period.
  2. Call Get Lead Id for each Lead Id in the list to retrieve field data from the lead record.

We’ll take the data retrieved in step 2 and format it for consumption by an external system.

Lead_Change_Sync_Overview

Program Input

By default the program “goes back” one day from the current date to look for changes.  So you could run this program at the same time each day for example.  To go farther back in time, you can specify the number of days as a command line argument, effectively increasing the time window.

The program contains several variables that you can modify:

CUSTOM_SERVICE_DATA – This contains your Marketo Custom Service data (Account Id, Client Id, Client Secret).

LEAD_CHANGE_FIELD_FILTER – This contains a comma seperated list of lead fields that we will inspect for changes.

READ_BATCH_SIZE – This is the number of records to retrieve at a time.  Use this to tune the response body size.

Program Output

The program gathers up all of the changed lead records and formats them in JSON as an array of lead objects as follows:

{
    "result": [
        {
            "leadId": "318592",
            "updatedAt": "2015-07-22T19:19:07Z",
            "firstName": "David",
            "lastName": "Everly",
            "email": "deverly@marketo.com"
        },

        ...more lead objects here...
    ]
}

The idea is that you could then pass this JSON as the request payload to an external web service to synchronize the data.

Program Logic

First we establish our time window, compose our REST endpoint URLS, and obtain our Authentication access token.  Next we fire up a Get Paging Token/Get Lead Changes loop in which runs until we exhaust the supply of lead changes.  The purpose of this loop is to accumulate a list of unique Lead Ids so that we can pass them to Get Lead by Id later in the program.  For this example, we tell Get Lead Changes to look for changes to the following fields: firstName, lastName, email.  You are free to select any combination of fields for your purposes.

Get Lead Changes returns “result” objects that contain an Activity Type Id, which we can use to filter results.  Note: You can get a list of Activity Types by calling Get Activity Types REST endpoint.  We are interested in 2 activity types that are returned:

1. New Lead (12)

{
    "id": 12024682,
    "leadId": 318581,
    "activityDate": "2015-03-17T00:18:41Z",
    "activityTypeId": 12,
    "primaryAttributeValueId": 318581,
    "primaryAttributeValue": "David Everly",
    "attributes": [
        {
            "name": "Created Date",
            "value": "2015-03-16"
        },
        {
            "name": "Source Type",
            "value": "New lead"
        }
    ]
}

2. Change Data Value (13)

We can tell which field changed by inspecting the “name” property in the Change Data Value response.

{
    "id": 12024689,
    "leadId": 318581,
    "activityDate": "2015-03-17T22:58:18Z",
    "activityTypeId": 13,
    "fields": [
        {
            "id": 31,
            "name": "lastName",
            "newValue": "Evely",
            "oldValue": "Everly"
        }
    ],
    "attributes": [
        {
            "name": "Source",
            "value": "Web form fillout"
        }
    ]
}

When either of these two types of activities are returned, we store the associated Lead Id in a list.  Once we have our list, we can iterate through it calling Get Lead by Id for each item.  This will retrieve the latest lead data for each lead in the list.  For this example we retrieve the following lead fields: leadId, updatedAt, firstName, lastName, email.  You are free to select any combination of fields for your purposes.  This is done by specifying the fields parameter to Get Lead by Id.  And finally we JSONify the results as an array of lead objects as described above.

Lead_Change_Sync_Detail

Program Code

package com.marketo;

// minimal-json library (https://github.com/ralfstx/minimal-json)
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import javax.net.ssl.HttpsURLConnection;

public class LeadChanges {
    //
    // Define Marketo REST API access credentials: Account Id, Client Id, Client Secret.  For example:
    //    public static String CUSTOM_SERVICE_DATA[] =
    //      {"111-AAA-222", "2f4a4435-f6fa-4bd9-3248-098754982345", "asdf6IVE9h4Jjcl59cOMAKFSk78ut12W"};
    //
    private static final String CUSTOM_SERVICE_DATA[] =
            {INSERT YOUR CUSTOM SERVICE DATA HERE};

    // Lead fields that we are interested in
    private static final String LEAD_CHANGE_FIELD_FILTER = "firstName,lastName,email";

    // Number of lead records to read at a time
    private static final String READ_BATCH_SIZE = "200";

    // Activity type ids that we are interested in
    private static final int ACTIVITY_TYPE_ID_NEW_LEAD = 12;
    private static final int ACTIVITY_TYPE_ID_CHANGE_DATA_VALE = 13;

    public static void main(String[] args) {
        // Command line argument to set how far back to look for lead changes (number of days)
        int lookBackNumDays = 1;
        if (args.length == 1) {
            lookBackNumDays = Integer.parseInt(args[0]);
        }

        // Establish "since date" using current timestamp minus some number of days (default is 1 day)
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_MONTH, -lookBackNumDays);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        String sinceDateTime = sdf.format(cal.getTime());

        // Compose base URL
        String baseUrl = String.format("https://%s.mktorest.com",
                CUSTOM_SERVICE_DATA[0]);

        // Compose Identity URL
        String identityUrl = String.format("%s/identity/oauth/token?grant_type=%s&client_id=%s&client_secret=%s",
                baseUrl, "client_credentials", CUSTOM_SERVICE_DATA[1], CUSTOM_SERVICE_DATA[2]);

        // Call Identity API
        JsonObject identityObj = JsonObject.readFrom(getData(identityUrl));
        String accessToken = identityObj.get("access_token").asString();

        // Compose URLs for Get Lead Changes, and Get Paging Token
        String leadChangesUrl = String.format("%s/rest/v1/activities/leadchanges.json?access_token=%s&fields=%s&batchSize=%s",
                baseUrl, accessToken, LEAD_CHANGE_FIELD_FILTER, READ_BATCH_SIZE);
        String pagingTokenUrl = String.format("%s/rest/v1/activities/pagingtoken.json?access_token=%s&sinceDatetime=%s",
                baseUrl, accessToken, sinceDateTime);

        HashSet leadIdList = new HashSet();

        // Call Get Paging Token API
        JsonObject pagingTokenObj = JsonObject.readFrom(getData(pagingTokenUrl));
        if (pagingTokenObj.get("success").asBoolean()) {

            String nextPageToken = pagingTokenObj.get("nextPageToken").asString();
            boolean moreResult;

            do {
                moreResult = false;

                // Call Get Lead Changes API
                JsonObject leadChangesObj = JsonObject.readFrom(getData(String.format("%s&nextPageToken=%s",
                        leadChangesUrl, nextPageToken)));
                if (leadChangesObj.get("success").asBoolean()) {
                    moreResult = leadChangesObj.get("moreResult").asBoolean();
                    nextPageToken = leadChangesObj.get("nextPageToken").asString();

                    if (leadChangesObj.get("result") != null) {
                        JsonArray resultAry = leadChangesObj.get("result").asArray();
                        for (JsonValue resultObj : resultAry) {
                            int activityTypeId = resultObj.asObject().get("activityTypeId").asInt();


                            // Store lead ids for later use
                            boolean storeThisId = false;
                            if (activityTypeId == ACTIVITY_TYPE_ID_NEW_LEAD) {
                                storeThisId = true;
                            } else if (activityTypeId == ACTIVITY_TYPE_ID_CHANGE_DATA_VALE) {
                                // See if any of the changed fields are of interest to us
                                JsonArray fieldsAry = resultObj.asObject().get("fields").asArray();
                                for (JsonValue fieldsObj : fieldsAry) {
                                    String name = fieldsObj.asObject().get("name").asString();
                                    if (LEAD_CHANGE_FIELD_FILTER.contains(name)) {
                                        storeThisId = true;
                                    }
                                }

                            }

                            if (storeThisId) {
                                leadIdList.add(resultObj.asObject().get("leadId").toString());
                            }
                        }
                    }
                }

            } while (moreResult);
        }

        JsonObject result = new JsonObject();
        JsonArray leads = new JsonArray();

        for (Object o : leadIdList) {
            String leadId = o.toString();

            // Compose Get Lead by Id URL
            String getLeadUrl = String.format("%s/rest/v1/lead/%s.json?access_token=%s",
                    baseUrl, leadId, accessToken);

            // Call Get Lead by Id API
            JsonObject leadObj = JsonObject.readFrom(getData(getLeadUrl));
            if (leadObj.get("success").asBoolean()) {
                if (leadObj.get("result") != null) {
                    JsonArray resultAry = leadObj.get("result").asArray();
                    for (JsonValue resultObj : resultAry) {

                        // Create lead object
                        JsonObject lead = new JsonObject();
                        lead.add("leadId", leadId);
                        lead.add("updatedAt", resultObj.asObject().get("updatedAt").asString());
                        lead.add("firstName", resultObj.asObject().get("firstName").asString());
                        lead.add("lastName", resultObj.asObject().get("lastName").asString());
                        lead.add("email", resultObj.asObject().get("email").asString());

                        // Add lead object to leads array
                        leads.add(lead);
                    }
                }
            }
        }

        // Add leads array to result object
        result.add("result", leads);

        // Print out result object
        System.out.println(result);

        System.exit(0);
    }

    // Perform HTTP GET request
    private static String getData(String endpoint) {
        String data = "";
        try {
            URL url = new URL(endpoint);
            HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
            urlConn.setRequestMethod("GET");
            urlConn.setAllowUserInteraction(false);
            urlConn.setDoOutput(true);
            int responseCode = urlConn.getResponseCode();
            if (responseCode == 200) {
                InputStream inStream = urlConn.getInputStream();
                data = convertStreamToString(inStream);
            } else {
                System.out.println(responseCode);
                data = "Status:" + responseCode;
            }
        } catch (MalformedURLException e) {
            System.out.println("URL not valid.");
        } catch (IOException e) {
            System.out.println("IOException: " + e.getMessage());
            e.printStackTrace();
        }
        return data;
    }

    private static String convertStreamToString(InputStream inputStream) {
        try {
            return new Scanner(inputStream).useDelimiter("\\A").next();
        } catch (NoSuchElementException e) {
            return "";
        }
    }
}

So there you have it, life’s a happy song.  Enjoy!

Create and Associate Leads, Companies, and Opportunities with the Marketo REST API

$
0
0

In order to take full advantage of Marketo analytics it is crucial to build out correct and robust associations between your lead, company and opportunity records.  When you are not leveraging a native CRM-sync, building these relationships can pose some difficulties, so today we’ll walk through building them.

Object Relationships

In Marketo, there are a few vital relationships to fully establish opportunity reporting:

  • Leads and Opportunities have a many to many relationship via the OpportunityRole object.
  • OpportunityRole has both a leadId and externalopportunityid field to create the relationship from lead to opportunity.
  • In order to qualify for a Has Opportunity smart list filter, a lead must have an OpportunityRole related to an opportunity.
  • Opportunities have a many-to-one relationship to the Company object via the externalCompanyId field.
  • Leads have a one-to-many relationship to Companies via the externalCompanyId field.
  • Opportunities are attributed to a program based on a lead’s Acquisition Program or their membership and success in a program (See Understanding Attribution).

Building out these relationships across your lead database will allow you to fully leverage Marketo analytics and see the influence that your programs have on opportunity creation and win rates.

Companies

The simplest way to build out these relationships is by starting with company creation.  This ensures that we can pass externalCompanyId to our opportunities during creation, instead of having to perform additional API calls to update opportunities after they’ve been created.  Depending on existing configuration, this may or may not be a necessary step, but net new leads and contacts with associated companies will need to have these records added to your Marketo instance in order for the relationships to be built, so let’s take a look at some code to create company records.

package dev.marketo.opportunities;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;

import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;

public class UpsertCompanies {
	public List<JsonObject> input; //a list of Companies to use for input.  Each must have a member "externalCompanyId".
	public String action; //specify the action to be undertaken, createOnly, updateOnly, createOrUpdate 
	public String dedupeBy; //select mode of Deduplication, dedupeFields for all dedupe parameters(externalCompanyId), idField for marketoId
	private String endpoint; //endpoint URL created with Constructor
	private Auth auth; //Marketo Auth Object
	
	
	//Constructs an UpsertOpportunities with Auth, but with no input set
	public UpsertCompanies(Auth auth){
		this.auth = auth;
		this.endpoint = this.auth.marketoInstance + "/rest/v1/companies.json";
	}
	//Constructs and UpsertOpportunities with Auth and input set
	public UpsertCompanies(Auth auth, List<JsonObject> input) {
		this(auth);
		this.input = input;
	}
	//adds input to existing list, creates arraylist if it was built without a list
	public UpsertCompanies addCompanies(JsonObject... companies){
		if (this.input == null){
			this.input = new ArrayList();
		}
		for (JsonObject jo : companies) {
			System.out.println(jo);
			this.input.add(jo);
		}
		return this;
	}
	
	public JsonObject postData(){
		JsonObject result = null;
		try {
			JsonObject requestBody = buildRequest(); //builds the Json Request Body
			String s = endpoint + "?access_token=" + auth.getToken(); //takes the endpoint URL and appends the access_token parameter to authenticate
			URL url = new URL(s); 
			HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection(); //Return a URL connection and cast to HttpsURLConnection
			urlConn.setRequestMethod("POST");
			urlConn.setRequestProperty("Content-type", "application/json");
            urlConn.setRequestProperty("accept", "text/json");
            urlConn.setDoOutput(true);
			OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
			wr.write(requestBody.toString());
			wr.flush();
			InputStream inStream = urlConn.getInputStream(); //get the inputStream from the URL connection
			Reader reader = new InputStreamReader(inStream);
			result = JsonObject.readFrom(reader); //Read from the stream into a JsonObject
			urlConn.disconnect();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	private JsonObject buildRequest(){
		JsonObject requestBody = new JsonObject(); //Create a new JsonObject for the Request Body
		JsonArray in = new JsonArray(); //Create a JsonArray for the "input" member to hold Opp records
		for (JsonObject jo : input) {
			in.add(jo); //add our company records to the input array
		}
		requestBody.add("input", in);
		if (this.action != null){
			requestBody.add("action", action); //add the action member if available
		}
		if (this.dedupeBy != null){
			requestBody.add("dedupeBy", dedupeBy); //add the dedupeBy member if available
		}
		return requestBody;
	}
	//Getters and Setters
	//Setters return the UpsertCompanies instance to allow simple formatting:
	public List<JsonObject> getInput() {
		return input;
	}
	//sets or replaces existing input with list
	public UpsertCompanies setInput(List input) {
		this.input = input;
		return this;
	}
	public String getAction() {
		return action;
	}
	public UpsertCompanies setAction(String action) {
		this.action = action;
		return this;
	}
	public String getDedupeBy() {
		return dedupeBy;
	}
	public UpsertCompanies setDedupeBy(String dedupeBy) {
		this.dedupeBy = dedupeBy;
		return this;
	}
	
}

The companies API provides two deduplication options, set by the dedupeBy parameter in the request, “dedupeFields” and “idField.”  These can be explicitly retrieved by calling Describe Companies.  If dedupeBy is unset, it defaults to dedupeFields.  In the case of company records, dedupeFields always corresponds to “externalCompanyId,” which is an arbitrary string set by an external source, and idField, corresponding to the “marketoId” field, which is an integer generated and returned by Marketo after creation.  Depending on the selection for dedupeBy, one of externalCompanyId or marketoId must be included in any upsert call for a company record.  These same requirements apply to the Opportunity and Opportunity Role object APIs.

Our code exposes two constructors: one accepting a single argument of an Auth object, and another which accepts Auth and a list of JsonObject company records.  If constructed without an input List, then company records must be added through the addCompanies method, which will check create a new ArrayList if the input is null, and then add all JsonObject arguments to the input List. Here’s an example usage:

        //Create a new company to associate to
        JsonObject myCompany = new JsonObject().add("externalCompanyId", "myCompany");
        UpsertCompanies upsertCompanies = new UpsertCompanies(auth).addCompanies(myCompany);
        JsonObject companiesResult = upsertCompanies.postData();
        System.out.println(companiesResult);

We’re creating a single company JsonObject with just one field, externalCompanyId, then constructing an instance of UpsertCompanies, and adding our company to the input list with addCompanies.

Opportunities

Similar to the company objects, the opportunity API has a dedupeBy parameter, accepting “dedupeFields” or “idField,” corresponding to “externalopportunityid” and “marketoGUID” respectively.  So here’s our code, which looks quite similar to the UpsertCompanies class:

package dev.marketo.opportunities;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;

import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;

public class UpsertOpportunities {
	public List<JsonObject> input; //a list of Opportunities to use for input.  Each must have a member "externalopportunityid".  Each can optionally include "externalCompanyId" for company association
	public String action; //specify the action to be undertaken, createOnly, updateOnly, createOrUpdate 
	public String dedupeBy; //select mode of Deduplication, dedupeFields for all dedupe parameters, idField for marketoId
	private String endpoint; //endpoint URL created with Constructor
	private Auth auth; //Marketo Auth Object
	
	
	//Constructs an UpsertOpportunities with Auth, but with no input set
	public UpsertOpportunities(Auth auth){
		this.auth = auth;
		this.endpoint = this.auth.marketoInstance + "/rest/v1/opportunities.json";
	}
	//Constructs and UpsertOpportunities with Auth and input set
	public UpsertOpportunities(Auth auth, List<JsonObject> input) {
		this(auth);
		this.input = input;
	}
	public UpsertOpportunities addOpportunities(JsonObject... opp){
		if (this.input == null){
			this.input = new ArrayList();
		}
		for (JsonObject jo : opp) {
			System.out.println(jo);
			this.input.add(jo);
		}
		return this;
	}
	
	public JsonObject postData(){
		JsonObject result = null;
		try {
			JsonObject requestBody = buildRequest(); //builds the Json Request Body
			String s = endpoint + "?access_token=" + auth.getToken(); //takes the endpoint URL and appends the access_token parameter to authenticate
			URL url = new URL(s); 
			HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection(); //Return a URL connection and cast to HttpsURLConnection
			urlConn.setRequestMethod("POST");
			urlConn.setRequestProperty("Content-type", "application/json");
            urlConn.setRequestProperty("accept", "text/json");
            urlConn.setDoOutput(true);
			OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
			wr.write(requestBody.toString());
			wr.flush();
			InputStream inStream = urlConn.getInputStream(); //get the inputStream from the URL connection
			Reader reader = new InputStreamReader(inStream);
			result = JsonObject.readFrom(reader); //Read from the stream into a JsonObject
			urlConn.disconnect();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	private JsonObject buildRequest(){
		JsonObject requestBody = new JsonObject(); //Create a new JsonObject for the Request Body
		JsonArray in = new JsonArray(); //Create a JsonArray for the "input" member to hold Opp records
		for (JsonObject jo : input) {
			in.add(jo); //add our Opportunity records to the input array
		}
		requestBody.add("input", in);
		if (this.action != null){
			requestBody.add("action", action); //add the action member if available
		}
		if (this.dedupeBy != null){
			requestBody.add("dedupeBy", dedupeBy); //add the dedupeBy member if available
		}
		return requestBody;
	}
	//Getters and Setters
	//Setters return the UpsertOpportunites instance to allow simple formatting:
	public List<JsonObject> getInput() {
		return input;
	}
	public UpsertOpportunities setInput(List<JsonObject> input) {
		this.input = input;
		return this;
	}
	public String getAction() {
		return action;
	}
	public UpsertOpportunities setAction(String action) {
		this.action = action;
		return this;
	}
	public String getDedupeBy() {
		return dedupeBy;
	}
	public UpsertOpportunities setDedupeBy(String dedupeBy) {
		this.dedupeBy = dedupeBy;
		return this;
	}
	
}

The same constructor options are provided, taking an Auth or Auth+List<JsonObject>, and an addOpportunities method to input JsonObject opportunities.  Here’s a usage example:

        //Create some JsonObjects for Opportunity Data
        JsonObject opp1 = new JsonObject().add("name", "opportunity1")
        			.add("externalopportunityid", "Opportunity1Test")
        			.add("externalCompanyId", "myCompany")
        			.add("externalCreatedDate", "2015-01-01T00:00:00z");
        JsonObject opp2 = new JsonObject().add("name", "opportunity2")
        			.add("externalopportunityid", "Opportunity2Test")
        			.add("externalCompanyId", "myCompany")
        			.add("externalCreatedDate", "2015-01-01T00:00:00z");

        //Create an Instance of UpsertOpportunities and POST it
        UpsertOpportunities upsertOpps = new UpsertOpportunities(auth)
                                .setAction("createOnly")
                                .addOpportunities(opp1, opp2);
        JsonObject oppsResult = upsertOpps.postData();
        System.out.println(oppsResult);

Here, we’re creating two example opportunities and then giving them values for the name, externalopportunityid, externalCompanyId, and externalCreatedDate fields.  We haven’t discussed externalCreatedDate yet, but it is important to utilize since it is treated as the master field in RCE for when an opportunity was created, making it important for correct attribution.  You can use your organization’s business logic to determine what you input in this field, based on whether you’re backfilling existing opportunity data, or creating new ones on the fly.  We’ll create our instance of UpsertOpportunities and then add our JsonObjects via addOpportunities.  Now that the instance is configured you can push this to Marketo with postData and print out your result

Roles

Roles are quite similar to the preceding two objects, except that they have a slightly different requirement when setting dedupeBy to dedupeFields.  Roles require that three fields be included when creating or updating a record via this method, “leadId,” “role,” and”externalopportunityid.”  “role” may be any string value, but the other two must refer to a valid Id of a lead, and a valid Id of an opportunity respectively.

package dev.marketo.opportunities;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HttpsURLConnection;

import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;

public class UpsertOpportunityRoles {
	public List<JsonObject> input; //Array of Opportunity Roles as JsonObjects, must have "leadId", "role" and "externalopprtunityid"
	public String action; //specify the action to be undertaken, createOnly, updateOnly, createOrUpdate, defaults to createOrUpdate if unset
	public String dedupeBy;//select mode of Deduplication, dedupeFields for all dedupe parameters, idField for marketoId
	private String endpoint; //endpoint URL created with Constructor
	private Auth auth; //Marketo Auth Object
	
	//Constructs an UpsertOpportunityRoles with Auth, but with no input set
	public UpsertOpportunityRoles(Auth auth) {
		this.auth = auth;
		this.endpoint = this.auth.marketoInstance + "/rest/v1/opportunities/roles.json";
	}
	//Constructs and UpsertOpportunities with Auth and input set
	public UpsertOpportunityRoles(Auth auth, List<JsonObject> input) {
		this(auth);
		this.input = input;
	}
	public UpsertOpportunityRoles addRoles(JsonObject... role){
		if (this.input == null){
			this.input = new ArrayList();
		}
		for (JsonObject jo : role) {
			System.out.println(jo);
			this.input.add(jo);
		}
		return this;
	}
	//executes the request to Marketo, body will be empty if input is not set
	public JsonObject postData(){
		JsonObject result = null;
		try {
			JsonObject requestBody = buildRequest(); //builds the Json Request Body
			String s = endpoint + "?access_token=" + auth.getToken(); //takes the endpoint URL and appends the access_token parameter to authenticate
			URL url = new URL(s);
			HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection(); //Return a URL connection and cast to HttpsURLConnection
			urlConn.setRequestMethod("POST");
			urlConn.setRequestProperty("Content-type", "application/json");//"application/json" content-type is required.
            urlConn.setRequestProperty("accept", "text/json");
            urlConn.setDoOutput(true);
			OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
			wr.write(requestBody.toString());
			wr.flush();
			InputStream inStream = urlConn.getInputStream();  //get the inputStream from the URL connection
			Reader reader = new InputStreamReader(inStream);
			result = JsonObject.readFrom(reader); //Read from the stream into a JsonObject
			urlConn.disconnect();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public JsonObject buildRequest(){
		JsonObject requestBody = new JsonObject();
		JsonArray in = new JsonArray();
		for (JsonObject jo : input) {
			in.add(jo);
		}
		requestBody.add("input", in);
		if (this.action != null){
			requestBody.add("action", action);
		}
		if (this.dedupeBy != null){
			requestBody.add("dedupeBy", dedupeBy);
		}
		return requestBody;
	}
	//Getters and Setters
	//Setters return the UpsertOpportunites instance to allow simple formatting:
	public List<JsonObject> getInput() {
		return input;
	}
	public UpsertOpportunityRoles setInput(List<JsonObject> input) {
		this.input = input;
		return this;
	}
	public String getAction() {
		return action;
	}
	public UpsertOpportunityRoles setAction(String action) {
		this.action = action;
		return this;
	}
	public String getDedupeBy() {
		return dedupeBy;
	}
	public UpsertOpportunityRoles setDedupeBy(String dedupeBy) {
		this.dedupeBy = dedupeBy;
		return this;
	}
	
}

We’re following a similar pattern for the constructors and addRoles method as the previous examples.  Here’s an example.

        //Create Some opp roles now
        JsonObject opp1Role = new JsonObject()
                                .add("role", "Captain")
                                .add("externalopportunityid", opp1.get("externalopportunityid").asString())
                                .add("leadId", 318794);
        JsonObject opp2Role = new JsonObject()
                                .add("role", "Commander")
                                .add("externalopportunityid", opp2.get("externalopportunityid").asString())
                                .add("leadId", 318795);
        
        //Create an Instance of UpsertOpportunityRoles and POST it
        UpsertOpportunityRoles upsertRoles = new UpsertOpportunityRoles(auth)
                                .setAction("createOnly")
                                .addRoles(opp1Role, opp2Role);
        JsonObject rolesResult = upsertRoles.postData();
        System.out.println(rolesResult);

Here we’re creating the new JsonObjects for our 2 example roles, and adding their required dedupeFields, pulling the externalopportunityid from the opportunities we already created, then pushing them down to Marketo.

Putting It All Together

Here is the complete example of our main method:

package dev.marketo.opportunities;

import com.eclipsesource.json.JsonObject;

public class App 
{
    public static void main( String[] args )
    {
    	//create an Instance of Auth 
        Auth auth = new Auth("CLIENT_ID_CHANGE_ME", "CLIENT_SECRET_CHANGE_ME", "MARKETO_HOST_CHANGE_ME");
        
        //Create a new company to associate to
        JsonObject myCompany = new JsonObject().add("externalCompanyId", "myCompany");
        UpsertCompanies upsertCompanies = new UpsertCompanies(auth).addCompanies(myCompany);
        JsonObject companiesResult = upsertCompanies.postData();
        System.out.println(companiesResult);
        
        //Create some JsonObjects for Opportunity Data
        JsonObject opp1 = new JsonObject().add("name", "opportunity1")
        			.add("externalopportunityid", "Opportunity1Test")
        			.add("externalCompanyId", "myCompany")
        			.add("externalCreatedDate", "2015-01-01T00:00:00z");
        JsonObject opp2 = new JsonObject().add("name", "opportunity2")
        			.add("externalopportunityid", "Opportunity2Test")
        			.add("externalCompanyId", "myCompany")
       				.add("externalCreatedDate", "2015-01-01T00:00:00z");

        //Create an Instance of UpsertOpportunities and POST it
        UpsertOpportunities upsertOpps = new UpsertOpportunities(auth)
                                .setAction("createOnly")
                                .addOpportunities(opp1, opp2);
        JsonObject oppsResult = upsertOpps.postData();
        System.out.println(oppsResult);
        
        //Create Some opp roles now
        JsonObject opp1Role = new JsonObject()
        			.add("role", "Captain")
        			.add("externalopportunityid", opp1.get("externalopportunityid").asString())
        			.add("leadId", 318794);
        JsonObject opp2Role = new JsonObject()
        			.add("role", "Commander")
        			.add("externalopportunityid", opp2.get("externalopportunityid").asString())
        			.add("leadId", 318795);
        
        //Create an Instance of UpsertOpportunityRoles and POST it
        UpsertOpportunityRoles upsertRoles = new UpsertOpportunityRoles(auth)
        			.setAction("createOnly")
        			.addRoles(opp1Role, opp2Role);
        JsonObject rolesResult = upsertRoles.postData();
        System.out.println(rolesResult);

    }
}

You can see the sequence of creating companies, opportunities, and roles.  Now you’re all set to sync your Company and Opportunity data to Marketo.

Updating Lead Data in Marketo using the API

$
0
0

Note: This is a blog post by David Everly. David is a Marketo Developer Evangelist based out of Portland, OR.

Over a year ago we posted Updating customer and prospect information in Marketo using the API.  That post presented a code sample that could be run on a recurring basis to poll a external system for updates.  The idea was to retrieve the external data and push it into Marketo to update lead information.  The code sample presented used our SOAP API.  This post will show you how to use Create/Update Leads REST endpoint to accomplish the same goal.

Updating_Lead_Data_Overview

Program Input

It is likely that you will need to transform the data from the external system into a format that is consumable by Marketo REST APIs.  Since we’re using the Create/Update Leads API, the data must be formatted as JSON which is sent in the request body.  This is best explained by an example.

In the Java sample code below, we have placed mock lead data in an array of strings.  Each string following data for each lead: first name, last name, email address, job title.

private static String externalLeadData[] = {
        "Henry, Adams, henry@superstar.com, Director of Demand Generation",
        "Suzie, Smith, ssmith@gmail.com, VP Marketing"
};

The sample code transforms the array into the JSON block below.

{
    "input":
        [
            {"firstName":"Henry", "lastName":"Adams", "email":"henry@superstar.com", "title":"Director of Demand Generation"},
            {"firstName":"Suzie", "lastName":"Smith", "email":"ssmith@gmail.com", "title":"VP Marketing"}
        ]
}

Each “input” array item corresponds to an individual lead in Marketo.  The array items are JSON objects that contain one or more Marketo lead field names and their respective values.

The field names that you decide to specify (in this case firstName, lastName, email, and title) must match the REST API name defined for the Marketo subscription.  You can find the REST API Names in the field management section within Marketo admin panel by exporting the field names.

field_management_export_field_names

The field names will be exported into an excel file as seen below.

field_names_in_excel

Alternatively, you can find field names programmatically by calling the Describe Lead API.  For example, here is a Describe response snippet that contains the REST API name for the First Name field.

{
    "id": 29,
    "displayName": "First Name",
    "dataType": "string",
    "length": 255,
    "soap": {
        "name": "FirstName",
        "readOnly": false
    },
    "rest": {
        "name": "firstName",
        "readOnly": false
    }
},

Program Logic

Once the payload is in the proper format, we are ready to call Create/Update Leads.  Note that in this sample we do not specify any of the optional parameters.  The default behavior is to create or update lead records (upsert), use email for deduplication purposes, and use synchronous processing.

If the call to Create/Update leads is successful, then the response body contains a “result” array containing the Marketo Lead Id and the status of the Create/Update operation.  Depending on the “action” parameter you passed in the request, the status can be either “updated”, “created”, or “skipped”.

Continuing with our example, suppose the first lead did not exist (Henry Adams), and the second lead did exist (Suzie Smith).  A response similar to the following would indicate the first lead was created, and the second lead was updated.

{
    "success":true,
    "requestId":"118f8#14f1a0b82fc",
    "result":[
        {
            "id":318798,
            "status":"created"
        },
        {
            "id":318797,
            "status":"updated"
        }
    ]
}

That’s it for now.  Happy coding!

Program Code

package com.marketo;

// minimal-json library (https://github.com/ralfstx/minimal-json)
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;

import javax.net.ssl.HttpsURLConnection;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;

public class CreateUpdateLeads {
    //
    // Define Marketo REST API access credentials: Account Id, Client Id, Client Secret.  For example:
    //    public static String CUSTOM_SERVICE_DATA[] =
    //      {"111-AAA-222", "2f4a4435-f6fa-4bd9-3248-098754982345", "asdf6IVE9h4Jjcl59cOMAKFSk78ut12W"};
    //
    private static final String CUSTOM_SERVICE_DATA[] =
            { INSERT YOUR CUSTOM SERVICE DATA HERE };

    //
    // Mock up data that could be read from an external data source.
    // An array of CSV strings the form:
    //   "firstName, lastName, email, title"
    private static String externalLeadData[] = {
        "Henry, Adams, henry@superstar.com, Director of Demand Generation",
        "Suzie, Smith, ssmith@gmail.com, VP Marketing"
    };

    public static void main(String[] args) {
        // Compose base URL
        String baseUrl = String.format("https://%s.mktorest.com",
                CUSTOM_SERVICE_DATA[0]);

        // Compose Identity URL
        String identityUrl = String.format("%s/identity/oauth/token?grant_type=%s&client_id=%s&client_secret=%s",
                baseUrl, "client_credentials", CUSTOM_SERVICE_DATA[1], CUSTOM_SERVICE_DATA[2]);

        // Call Identity API
        JsonObject identityObj = JsonObject.readFrom(httpRequest("GET", identityUrl, null));
        String accessToken = identityObj.get("access_token").asString();

        // Compose URL for Create/Update Leads
        String createUpdateLeadsUrl = String.format("%s/rest/v1/leads.json?access_token=%s", baseUrl, accessToken);

        // Convert String array into JsonArray to pass as part of request body
        JsonArray input = convertExternalLeadDataToJson(externalLeadData);

        // Build request body JSON
        JsonObject requestObj = new JsonObject();
        requestObj.add("input", input);

        // Call Create/Update Lead API
        JsonArray result = new JsonArray();
        JsonObject leadObj = JsonObject.readFrom(httpRequest("POST", createUpdateLeadsUrl, requestObj.toString()));
        if (leadObj.get("success").asBoolean()) {
            if (leadObj.get("result") != null) {
                result = leadObj.get("result").asArray();
            }
        }

        // Print out results object
        System.out.println(result);
        System.exit(0);
    }

    // Convert array of CSV formatted Strings into an array of JSON objects
    private static JsonArray convertExternalLeadDataToJson(String input[]) {
        JsonArray output = new JsonArray();

        // Loop through each CSV row in array
        for (int i = 0; i < input.length; i++) {
            // Split CSV row into separate fields
            List items = Arrays.asList(input[i].split(","));

            // Add fields to JSON object
            JsonObject lead = new JsonObject();
            lead.add("firstName", items.get(0));
            lead.add("lastName", items.get(1));
            lead.add("email", items.get(2));
            lead.add("title", items.get(3));
            output.add(lead);
        }
        return output;
    }

    // Perform HTTP request
    private static String httpRequest(String method, String endpoint, String body) {
        String data = "";
        try {
            URL url = new URL(endpoint);
            HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
            urlConn.setDoOutput(true);
            urlConn.setRequestMethod(method);
            switch (method) {
                case "GET":
                    break;
                case "POST":
                    urlConn.setRequestProperty("Content-type", "application/json");
                    urlConn.setRequestProperty("accept", "text/json");
                    OutputStreamWriter wr = new OutputStreamWriter(urlConn.getOutputStream());
                    wr.write(body);
                    wr.flush();
                    break;
                default:
                    System.out.println("Error: Invalid method.");
                    return data;
            }
            int responseCode = urlConn.getResponseCode();
            if (responseCode == 200) {
                InputStream inStream = urlConn.getInputStream();
                data = convertStreamToString(inStream);
            } else {
                System.out.println(responseCode);
                data = "Status:" + responseCode;
            }
        } catch (MalformedURLException e) {
            System.out.println("URL not valid.");
        } catch (IOException e) {
            System.out.println("IOException: " + e.getMessage());
            e.printStackTrace();
        }
        return data;
    }

    private static String convertStreamToString(InputStream inputStream) {
        try {
            return new Scanner(inputStream).useDelimiter("\\A").next();
        } catch (NoSuchElementException e) {
            return "";
        }
    }
}

 

Add SalesPerson data to Marketo

$
0
0

With the new SalesPerson APIs, you can freely associate Marketo leads to SalesPerson records in instances without a native CRM integration. This allows usage of {{lead.Lead Owner Email Address}} and related fields and tokens within Marketo.

Creating SalesPerson records

In order to associate leads to SalesPerson records, we first need to input our SalesPerson records into Marketo. This is done with the Create/Update/Upsert SalesPerson endpoint. Here’s an example class in PHP:

<?php

class SalesPerson{
	private $auth;//auth object
	private $action;// string designating request action, createOnly, updateOnly, createOrUpdate
	private $dedupeBy;//dedupeFields or idField
	private $input;//array of salesperson objects for input
	
	//takes an Auth object as the first argument
	public function __construct($auth, $input){
		$this->auth = $auth;
		$this->input = $input;
	}
	
	//constructs the json request body
	private function bodyBuilder(){
		$body = new stdClass();
		$body->input = $this->input;
		if (isset($this->action)){
			$body->action = $this->action;
		}
		if (isset($this->dedupeBy)){
			$body->dedupeBy = $this->dedupeBy;
		}
		return json_encode($body);
	}
	//submits the request to Marketo and returns the response as a string
	public function postData(){
		$url = $this->auth->getHost() . "/rest/v1/salespersons.json?access_token=" . $this->auth->getToken();
		$ch = curl_init($url);
		$requestBody = $this->bodyBuilder();
		curl_setopt($ch,  CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array('accept: application/json','Content-Type: application/json'));
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
		curl_getinfo($ch);
		$response = curl_exec($ch);
		curl_close($ch);
		return $response;
	}
	//getters and setters
	public function setAction($action){
		$this->action = $action;
	}
	public function getAction($action){
		return $this->action;
	}
	public function setDedupeBy($dedupeBy){
		$this->dedupeBy = $dedupeBy;
	}
	public function getDedupeBy($dedupeBy){
		return $this->dedupeBy;
	}
	public function addSalesPersons($input){
		if($this->input != null){
			if (is_array($input)){
				foreach($input as $item){
					array_push($this->input, $item);
				}
			}else{
				array_push($this->input, $input);
			}
		}else{
			$this->input = $input;
		}
	}
	public function getInput(){
		return $this->input;
	}
	
}

In the above class, the $input variable is an array of stdClass objects with possible members:

  • externalSalesPersonId(only valid on create)
  • id(only as key in updateOnly mode)
  • email
  • fax
  • firstName
  • lastName
  • mobilePhone
  • phone
  • title

More detailed information about types and fields lengths can be retrieved through the Describe SalesPerson call.

Synching Leads

Here’s a quick example class for synching the leads we need:

<?php

class Leads{
	private $auth;//auth object
	private $action;// string designating request action, createOnly, updateOnly, createOrUpdate
	private $lookupField;//dedupeFields or idField
	private $input;//array of salesperson objects for input
	private $asyncProcessing;//specify whether input should be processed asynchronously
	private $partitionName;//partition name for update if requires

	//takes an Auth object as the first argument
	public function __construct($auth, $input){
		$this->auth = $auth;
		$this->input = $input;
	}

	//constructs the json request body
	private function bodyBuilder(){
		$body = new stdClass();
		$body->input = $this->input;
		if (isset($this->action)){
			$body->action = $this->action;
		}
		if (isset($this->lookupField)){
			$body->lookupField = $this->lookupField;
		}
		return json_encode($body);
	}
	//submits the request to Marketo and returns the response as a string
	public function postData(){
		$url = $this->auth->getHost() . "/rest/v1/leads.json?access_token=" . $this->auth->getToken();
		$ch = curl_init($url);
		$requestBody = $this->bodyBuilder();
		curl_setopt($ch,  CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array('accept: application/json','Content-Type: application/json'));
		curl_setopt($ch, CURLOPT_POST, 1);
		curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
		curl_getinfo($ch);
		$response = curl_exec($ch);
		curl_close($ch);
		return $response;
	}
	//getters and setters
	public function setAction($action){
		$this->action = $action;
	}
	public function getAction(){
		return $this->action;
	}
	public function setDedupeBy($lookupField){
		$this->dedupeBy = $lookupField;
	}
	public function getDedupeBy(){
		return $this->dedupeBy;
	}
	public function addLeads($input){
		if($this->input != null){
			if (is_array($input)){
				foreach($input as $item){
					array_push($this->input, $item);
				}
			}else{
				array_push($this->input, $input);
			}
		}else if (is_array($input)){
			$this->input = $input;
		}else{
			$this->input = [$input];
		}
	}
	public function getInput(){
		return $this->input;
	}
	public function setAsyncProcessing($asyncProcessing){
		$this->asyncProcessing = $asyncProcessing;
	}
	public function getAsyncProcessing() {
		return $this->asyncProcessing;
	}
	public function setPartitionName($partitionName){
		$this->partitionName = $partitionName;
	}
	public function getPartitionName() {
		return $this->partitionName;
	}
	
}

Process

Here’s an example creating two salesperson records and associating them to two lead records:

<?php

require 'Auth.php';
require 'SalesPerson.php';
require 'Leads.php';

//Create an Auth object for authentication
$auth = new Auth("https://284-RPR-133.mktorest.com", "7f99bd49-0e0e-4715-a72a-0c9319f75552", "O5lZHhrQDfDKRhulY89IU42PfdhRSe6m");

//Compose new SalesPerson Records
$sales1 = new stdClass();
$sales1->externalSalesPersonId = "SalesPerson 1";
$sales1->email = "sales1@example.com";
$sales1->firstName = "Jane";
$sales1->lastName = "Doe";

$sales2 = new stdClass();
$sales2->externalSalesPersonId = "SalesPerson 2";
$sales2->email = "sales2@example.com";
$sales2->firstName = "John";
$sales2->lastName = "Doe";

//Compose lead records
$lead1 = new stdClass();
$lead1->email = "marketoDev@example.com";
//Add the external id of the desired salesperson
$lead1->externalSalesPersonId = $sales1->externalSalesPersonId;

$lead2 = new stdClass();
$lead2->email = "devBlog@example.com";
$lead2->externalSalesPersonId = $sales2->externalSalesPersonId;

//Create a new instance of SalesPerson to submit records
$salesPerson = new SalesPerson($auth, [$sales1, $sales2]);
$spResponse = $salesPerson->postData();
print_r($spResponse . "\r\n");
$json = json_decode($spResponse);

//Check for success on synching SalesPersons
if ($json->success){
	$leads = new Leads($auth, [$lead1, $lead2]);
	$leadsResponse = $leads->postData();
	print_r($leadsResponse . "\r\n");
}else{
	print_r("SalesPerson request failed with errors:\r\n");
	foreach ($json->errors as $error){
		print_r("code: " . $error->code . ", message: " . $error->message . "\r\n");
	}
}

For our example classes, we’re just creating stdClass objects to represent our SalesPerson and Lead records which need to be synched, with each desired field added as a member. After execution of this code, the leads marketoDev@example.com and devBlog@example.com will both have the Lead Owner Email, Lead Owner First Name, and Lead Owner Last name fields populated, affording them the ability to use the relevant tokens for those fields and be filtered by the relevant smart list filters.

Auth Class

<?php

class Auth{
	private $host;//host of the target Marketo instance
	private $clientId;//client Id
	private $clientSecret;//client secret
	private $token;//access_token
	private $expiry;
	
	function __construct($host, $clientId, $clientSecret){
		$this->host = $host;
		$this->clientId = $clientId;
		$this->clientSecret = $clientSecret;
	}
	public function getToken(){
		if (!isset($this->token) || $this->expiry > time()){
			$data = $this->getData();
			$this->expiry = time() + $data->expires_in;
			$this->token = $data->access_token;
			return $this->token;
		}else{
			return $this->token;
		}
	}
	private function getData(){
		$url = $this->host . "/identity/oauth/token?grant_type=client_credentials&client_id=" . $this->clientId . "&client_secret="
					. $this->clientSecret;
		$ch = curl_init($url);
		curl_setopt($ch,  CURLOPT_RETURNTRANSFER, 1);
		curl_setopt($ch, CURLOPT_HTTPHEADER, array('accept: application/json','Content-Type: application/json'));
		$response = curl_exec($ch);
		$json = json_decode($response);
		curl_close($ch);
		return $json;
	}
	public function getHost(){
		return $this->host;
	}
}

Best Practices for API Users and Custom Services

$
0
0

Marketo’s REST APIs use custom services for authentication and each of these services is owned by an API-only Marketo user. The capabilities of each custom service are determined by the permissions of each role assigned to that user. Marketo allows for an unlimited number of users and custom services per account, so there’s no reason to be stingy with your users and services if you have multiple web services or third-party integrations making REST calls to your Marketo instance. Allocating individual users and custom services to your integrations gives you multiple benefits:

  • You can fine-tune the permissions given to each individual service through the role given to your user.
  • You can disable individual web services from making calls to your instance by deleting the corresponding custom service, without disabling others.
  • Reporting on API call usage will be broken down by user, allowing you to identify high and abnormal utilization
  • It is easier to determine what data each web service is being given access to
  • Workspace-enabled instances can restrict access to specific business units, by only awarding roles to accessible workspaces

API Usage

Each of your API users is reported individually in the API usage report, so splitting up your web services by user allows you to easily account for the usage of each of your integrations. If the number of API calls to your instance are exceeding the limit and causing subsequent calls to fail, using this practice will allow you to account for the volume from each of your services and let you evaluate how to resolve the issue. See your usage by going to Admin -> Web Services and clicking on the number of calls in the past 7 days. You can also get the same information from the Usage APIs

API
Per Users

Disable a Service

If an integration is having undesirable effects, it can be tedious and difficult to disable if you have not assigned each one an individual custom service. Having them broken out one by one makes it as easy as deleting the offending service in your Admin -> Launchpoint.

Delete Service

Workspace Management

For Marketo Enterprise subscriptions, it is common for a service to only need access to a single workspace, and this can be enforced by role assignment to the API User. Each user role can be assigned either globally, or on a per-workspace basis, so access can be restricted in workspaces wherever appropriate, providing the most minimal permissions set possible.


How to Specify Lead Partitions Using the REST API

$
0
0

Note: This is a blog post by David Everly. David is a Marketo Developer Evangelist based out of Portland, OR.

Lead Partitioning

Marketo Lead Partitions provide a convenient way to isolate leads.  Partitions can allow different marketing groups within your organization to share a single Marketo instance.  For more information, see Understanding Workspaces and Lead Partitions.

Suppose that you are using lead partitions and creating leads programmatically using the Marketo REST API.  How do you ensure the leads that you create will end up in the correct partition?  This post will show you how!

For the sake of this example, we’ll use Workspaces and Partitions to isolate our leads based on geography.  First we’ll define a workspace called “Country”.  Next, we’ll create two partitions within that workspace called “Mexico” and “Canada”.

edit_workspace

Create Lead in Partition

Suppose now that we want to create two leads in the “Mexico” partition.  To create leads, we call the Create/Update Leads API.  To specify the partition, we must include the “partitionName” attribute in the request body.

How do we know what to use for the partitionName value?  We can retrieve a list of valid partition name values for our instance by calling the Get Lead Partitions API as follows:

GET /rest/v1/leads/partitions.json

{
    "requestId": "20ae#14f9a1a5a30",
    "result": [
        {
            "id": 1,
            "name": "Default",
            "description": "Initial system lead partition"
        },
        {
            "id": 2,
            "name": "Mexico",
            "description": "Leads that live in Mexico"
        },
        {
            "id": 3,
            "name": "Canada",
            "description": "Leads that live in Canada"
        }
    ],
    "success": true
}

In this case, the correct value is “Mexico”, so we then pass that to Create/Update Leads as follows:

POST /rest/v1/leads.json

{
    "input": [
        {"email":"enrique.pena-nieto@gob.mx"},
        {"email":"angelica.rivera@gob.mx"}
    ],
    "action":"createOrUpdate",
    "partitionName":"Mexico"
}

Here are our newly created leads in Marketo.
lead_create

Update Lead in Partition

To update existing leads in a partition, we simply call Create/Update Leads and specify partitionName the same as we did before. For this update, we’ll assign first name, last name, and title to the leads that we created above.

POST /rest/v1/leads.json

{
"input": [
        {"email":"enrique.pena-nieto@gob.mx", "firstName":"Enrique", "lastName":"Pena Neito", "title": "El Presidente"},
        {"email":"angelica.rivera@gob.mx", "firstName":"Angelica", "lastName": "Rivera", "title": "Primera Dama"}
    ],
    "action":"createOrUpdate",
    "partitionName":"Mexico"
}

Here are our newly updated leads in Marketo.
lead_update

Identify Partition for a Lead

How do we know which partition that a lead is in?  For this we use the Get Lead by Id API and specify “leadPartitionId” in the “fields” query parameter.  In this case we will retrieve the information for lead id 318816 that we created above.

GET /rest/v1/lead/318816.json?fields=leadPartitionId,email,firstName,lastName,title
{
    "requestId": "5c57#14f9a495b1f",
    "result": [
        {
            "id": 318816,
            "lastName": "Pena Neito",
            "title": "El Presidente",
            "email": "enrique.pena-nieto@gob.mx",
            "firstName": "Enrique",
            "leadPartitionId": 2
        }
    ],
    "success": true
}

Note that we get back leadPartitionId rather than the partitionName.  To get the partitionName we need to revisit the output from Get Lead Partitions API from above.

{
    "id": 2,
    "name": "Mexico",
    "description": "Leads that live in Mexico"
},

In this case a leadPartitionId value of 2 maps to partitionName of “Mexico”.

That’s all for now.  Happy partitioning!

Polling for Activities using REST API

$
0
0

Note: This is a blog post by David Everly. David is a Marketo Developer Evangelist based out of Portland, OR.

Earlier this year we published this post that described how to poll for lead changes within Marketo.  This post is similar, but this time we’ll poll for activities.

Activities are a core object in the Marketo Platform. Activities are the behavioral data that is stored about every webpage visit, email open, webinar attendance, or trade show attendance.  A common use case is combining activity data with data from other parts of an organization.

This sample program contains 6 steps:

  1. Call Get Lead Activities to generate a list of all activity records created since a given date/time. We use a filter to limit the type of activity records that are returned.
  2. Extract fields of interest from each activity record.
  3. Call Get Multiple Leads by Filter Type to generate a list of lead records that correspond with the activities from Step 1. We use the leadId field extracted from activity record in Step 2 as our filter to specify which leads are returned.
  4. Extract fields of interest from each lead record.
  5. Join the activity data from step 2 with the lead data from Step 4.
  6. Transform the data from Step 5 into a format that is consumable by an external system.

As the diagram below shows, for this example we have chosen to capture email-related activities.

Activity_Sync_Overview
Program Input

By default the program goes back in time one day from the current date to look for changes.  So you could run this program at the same time each day for example.  To go farther back in time, you can specify the number of days as a command line argument, effectively increasing the window of time.

The program contains several variables that you can modify:

CUSTOM_SERVICE_DATA – This contains your Marketo Custom Service data (Account Id, Client Id, Client Secret).

READ_BATCH_SIZE – This is the number of records to retrieve at a time.  Use this to tune the response body size.

LEAD_FIELDS – This contains a list of lead fields that we want to collect.

ACTIVITY_TYPES – This contains a list of activity types that we want to collect.

Program Logic

First we establish our time window, compose our REST endpoint URLS, and obtain our Authentication access token.  Next we fire up a Get Paging Token/Get Lead Activities loop which runs until we exhaust the supply of activities.  The purpose of this loop is to retrieve activity records, and extract fields of interest from those records.

We tell Get Lead Activities to look only for the following activity types:

  • Email Delivered
  • Unsubscribe Email
  • Open Email
  • Click Email.

We extract the following fields of interest from each activity record:

  • leadId
  • activityType
  • activityDate
  • primaryAttributeValue

You are free to select any combination of activity types and activity fields to suit your purpose.

Next we fire up a Get Multiple Leads by Filter Type loop which runs until we exhaust the supply of leads.  Note that we use the “filterType=id” parameter in combination with a series of “filterValues” parameters to limit the lead records retrieved to only those leads associated with activities that we retrieved earlier.

We extract the following fields of interest from each lead record:

  • firstName
  • lastName
  • email

Again, you are feel to select any lead fields that you like.

Next we join the lead fields with the activity fields by using the lead id to link them together.

Finally, we loop through all of the data, transform it into JSON format, and print it to the console.

Program Output

Here is an example of output from the sample program.  This shows the activity fields and the lead fields combined together as JSON “result” objects.  The idea here is that you could pass this JSON as a request payload to an external web service.

{

    "result": [
        {
            "leadId": 318581,
            "activityType": "Email Delivered",
            "activityDate": "2015-03-17T20:00:06Z",
            "primaryAttributeValue": "My Email Program",
            "firstName": "David",
            "lastName": "Everly",
            "email": "everlyd@marketo.com"
        },
        {
            "leadId":318581,
            "activityType":"Open Email",
            "activityDate":"2015-03-17T23:23:12Z",
            "primaryAttributeValue":"My Email Program - Auto Response",
            "firstName":"David",
            "lastName":"Everly",
            "email":"everlyd@marketo.com"
       },
        ... more result objects here...
    ]
}

Program Code

package com.marketo;

// minimal-json library (https://github.com/ralfstx/minimal-json)
import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import javax.net.ssl.HttpsURLConnection;

public class LeadActivities {
//
// Define Marketo REST API access credentials: Account Id, Client Id, Client Secret.  For example:
    private static Map<String, String> CUSTOM_SERVICE_DATA;
    static {
        CUSTOM_SERVICE_DATA = new HashMap<String, String>();
//        CUSTOM_SERVICE_DATA.put("accountId", "111-AAA-222");
//        CUSTOM_SERVICE_DATA.put("clientId", "2f4a4435-f6fa-4bd9-3248-098754982345");
//        CUSTOM_SERVICE_DATA.put("clientSecret", "asdf6IVE9h4Jjcl59cOMAKFSk78ut12W");
    }

    // Number of lead records to read at a time
    private static final String READ_BATCH_SIZE = "200";

    // Lookup lead records using lead id as primary key
    private static final String LEAD_FILTER_TYPE = "id";

    // Lead record lookup returns these fields
    private static final String LEAD_FIELDS = "firstName,lastName,email";

    // Lookup activity records for these activity types
    private static Map<Integer, String> ACTIVITY_TYPES;
    static {
        ACTIVITY_TYPES = new HashMap<Integer, String>();
        ACTIVITY_TYPES.put(7, "Email Delivered");
        ACTIVITY_TYPES.put(9, "Unsubscribe Email");
        ACTIVITY_TYPES.put(10, "Open Email");
        ACTIVITY_TYPES.put(11, "Click Email");
    }

    public static void main(String[] args) {
        // Command line argument to set how far back to look for lead changes (number of days)
        int lookBackNumDays = 1;
        if (args.length == 1) {
            lookBackNumDays = Integer.parseInt(args[0]);
        }

        // Establish "since date" using current timestamp minus some number of days (default is 1 day)
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_MONTH, -lookBackNumDays);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        String sinceDateTime = sdf.format(cal.getTime());

        // Compose base URL
        String baseUrl = String.format("https://%s.mktorest.com",
            CUSTOM_SERVICE_DATA.get("accountId"));

        // Compose Identity URL
        String identityUrl = String.format("%s/identity/oauth/token?grant_type=%s&client_id=%s&client_secret=%s",
                baseUrl, "client_credentials", CUSTOM_SERVICE_DATA.get("clientId"), CUSTOM_SERVICE_DATA.get("clientSecret"));

        // Call Identity API
        JsonObject identityObj = JsonObject.readFrom(getData(identityUrl));
        String accessToken = identityObj.get("access_token").asString();

        // Compose URLs for Get Lead Changes, and Get Paging Token
        String activityTypesUrl = String.format("%s/rest/v1/activities/types.json?access_token=%s",
                baseUrl, accessToken);
        String pagingTokenUrl = String.format("%s/rest/v1/activities/pagingtoken.json?access_token=%s&sinceDatetime=%s",
                baseUrl, accessToken, sinceDateTime);

        // Use activity ids to create filter parameter
        String activityTypeIds = "";
        for (Integer id : ACTIVITY_TYPES.keySet()) {
            activityTypeIds += "&activityTypeIds=" + id.toString();
        }

        // Compose URL for Get Lead Activities
        // Only retrieve activities that match our defined activity types
        String leadActivitiesUrl = String.format("%s/rest/v1/activities.json?access_token=%s%s&batchSize=%s",
                baseUrl, accessToken, activityTypeIds, READ_BATCH_SIZE);

        Map<Integer, List> activitiesMap = new HashMap<Integer, List>();
        Set leadsSet = new HashSet();

        // Call Get Paging Token API
        JsonObject pagingTokenObj = JsonObject.readFrom(getData(pagingTokenUrl));
        if (pagingTokenObj.get("success").asBoolean()) {
            String nextPageToken = pagingTokenObj.get("nextPageToken").asString();
            boolean moreResult;
            do {
                moreResult = false;

                // Call Get Lead Activities API to retrieve activity data
                JsonObject leadActivitiesObj = JsonObject.readFrom(getData(String.format("%s&nextPageToken=%s",
                        leadActivitiesUrl, nextPageToken)));
                if (leadActivitiesObj.get("success").asBoolean()) {
                    moreResult = leadActivitiesObj.get("moreResult").asBoolean();
                    nextPageToken = leadActivitiesObj.get("nextPageToken").asString();

                    if (leadActivitiesObj.get("result") != null) {
                        JsonArray activitiesResultAry = leadActivitiesObj.get("result").asArray();
                        for (JsonValue activitiesResultObj : activitiesResultAry) {

                            // Extract activity fields of interest (leadID, activityType, activityDate, primaryAttributeValue)
                            JsonObject leadObj = new JsonObject();
                            int leadId = activitiesResultObj.asObject().get("leadId").asInt();
                            leadObj.add("activityType", ACTIVITY_TYPES.get(activitiesResultObj.asObject().get("activityTypeId").asInt()));
                            leadObj.add("activityDate", activitiesResultObj.asObject().get("activityDate").asString());
                            leadObj.add("primaryAttributeValue", activitiesResultObj.asObject().get("primaryAttributeValue").asString());

                            // Store JSON containing activity fields in a map using lead id as key
                            List leadLst = activitiesMap.get(leadId);
                            if (leadLst == null) {
                                activitiesMap.put(leadId, new ArrayList());
                                leadLst = activitiesMap.get(leadId);
                            }
                            leadLst.add(leadObj);

                            // Store unique lead ids for use as lead filter value below
                            leadsSet.add(leadId);
                        }
                    }
                }
            } while (moreResult);
        }

        // Use unique lead id values to create filter parameter
        String filterValues = "";
        for (Object object : leadsSet) {
            if (filterValues.length() > 0) {
                filterValues += ",";
            }
            filterValues += String.format("%s", object);
        }

        // Compose Get Multiple Leads by Filter Type URL
        // Only retrieve leads that match the list of lead ids that was accumulated during activity query
        String getMultipleLeadsUrl = String.format("%s/rest/v1/leads.json?access_token=%s&filterType=%s&fields=%s&filterValues=%s&batchSize=%s",
                baseUrl, accessToken, LEAD_FILTER_TYPE, LEAD_FIELDS, filterValues, READ_BATCH_SIZE);
        String nextPageToken = "";

        do {
            String gmlUrl = getMultipleLeadsUrl;

            // Append paging token to Get Multiple Leads by Filter Type URL
            if (nextPageToken.length() > 0) {
                gmlUrl = String.format("%s&nextPageToken=%s", getMultipleLeadsUrl, nextPageToken);
            }

            // Call Get Multiple Leads by Filter Type API to retrieve lead data
            JsonObject multipleLeadsObj = JsonObject.readFrom(getData(gmlUrl));
            if (multipleLeadsObj.get("success").asBoolean()) {
                if (multipleLeadsObj.get("result") != null) {
                    JsonArray multipleLeadsResultAry = multipleLeadsObj.get("result").asArray();

                    // Iterate through lead data
                    for (JsonValue leadResultObj : multipleLeadsResultAry) {
                        int leadId = leadResultObj.asObject().get("id").asInt();

                        // Join activity data with lead fields of interest (firstName, lastName, email)
                        List leadLst = activitiesMap.get(leadId);
                        for (JsonObject leadObj : leadLst) {
                            leadObj.add("firstName", leadResultObj.asObject().get("firstName").asString());
                            leadObj.add("lastName", leadResultObj.asObject().get("lastName").asString());
                            leadObj.add("email", leadResultObj.asObject().get("email").asString());
                        }
                    }
                }
            }

            nextPageToken = "";
            if (multipleLeadsObj.asObject().get("nextPageToken") != null) {
                nextPageToken = multipleLeadsObj.get("nextPageToken").asString();
            }
        } while (nextPageToken.length() > 0);

        // Now place activity data into an array of JSON objects
        JsonArray activitiesAry = new JsonArray();
        for (Map.Entry<Integer, List> activity : activitiesMap.entrySet()) {
            int leadId = activity.getKey();
            for (JsonObject leadObj : activity.getValue()) {
                // do something with leadId and each leadObj
                leadObj.add("leadId", leadId);
                activitiesAry.add(leadObj);
            }
        }

        // Print out result objects
        JsonObject result = new JsonObject();
        result.add("result", activitiesAry);
        System.out.println(result);

        System.exit(0);
    }

    // Perform HTTP GET request
    private static String getData(String endpoint) {
        String data = "";
        try {
            URL url = new URL(endpoint);
            HttpsURLConnection urlConn = (HttpsURLConnection) url.openConnection();
            urlConn.setRequestMethod("GET");
            urlConn.setAllowUserInteraction(false);
            urlConn.setDoOutput(true);
            int responseCode = urlConn.getResponseCode();
            if (responseCode == 200) {
                InputStream inStream = urlConn.getInputStream();
                data = convertStreamToString(inStream);
            } else {
                System.out.println(responseCode);
                data = "Status:" + responseCode;
            }
        } catch (MalformedURLException e) {
            System.out.println("URL not valid.");
        } catch (IOException e) {
            System.out.println("IOException: " + e.getMessage());
            e.printStackTrace();
        }
        return data;
    }

    private static String convertStreamToString(InputStream inputStream) {
        try {
            return new Scanner(inputStream).useDelimiter("\\A").next();
        } catch (NoSuchElementException e) {
            return "";
        }
    }
}

That’s it.  Happy coding!

Quick Start Guide for Marketo REST API

$
0
0

This guide will show you how to make your first call to the Marketo REST API in ten minutes.

We’ll show you how to retrieve a single lead using the Get Lead by Id REST API endpoint. To do this, we will walk you through the authentication process to generate an access token, which you will use to make a HTTP GET request to Get Lead by Id. Then we’ll provide you with the code to make the request that returns lead information formatted as JSON.

How to Generate an Authentication Token

A Custom Service in Marketo allows you to describe and define what data your application will have access to. You need to be logged in as a Marketo administrator to create a Custom Service and associate that service with a single API-Only user.

1. Navigate to the admin area of the Marketo application.

navigate_to_admin

2. Click on the Users & Roles node on the left panel.

users_and_roles

3. Create a new role.

create_new_role

4. The next step is to create an API-Only user and associating it with the API role that you created in step 3. You can do so by checking the API-Only user checkbox at the time of user creation.

invite_new_user

5. A Custom service is required to uniquely identify your client application. To create a custom application, go to the Admin > LaunchPoint screen and create a new service.

launchpoint_new_service

6. Provide the Display Name, choose “Custom” Service type, provide Description, and the user email address created in step 4. We recommend using a descriptive Display Name that represents either the company or purpose of this Custom REST API Service.

new_service

7. Click on “View Details” link on the grid to get the Client Id and Client Secret.

view_details

Your client application will be able to use the Client Id and Client Secret to generate an access token.

details

8. Copy and paste your authentication token into a text editor. Your authentication token will look similar to the example below:

cdf01657-110d-4155-99a7-f986b2ff13a0:int

How to Determine the Endpoint URL

In making a request to Marketo API, you will need to specify your Marketo instance in the endpoint URL. All requests to the Marketo REST API will follow the format below:

<REST API Endpoint URL>/rest/

The REST API Endpoint URL can be found within the Marketo Admin > Web Services panel.

rest_api_endpoint

Your Marketo endpoint URL structure should look similar to the example below:

http://100-AEK-913.mktorest.com/rest/v1/lead/{id}.json

How to use Authentication Token to Call Get Lead by Id API

In the previous sections, we generated an authentication token and found the endpoint URL. We will now make a request to REST API endpoint called Get Lead by Id.

The easiest way to make your request to Marketo REST API is to paste the URL into your web browser address bar. Follow the format below:

http://<REST API Endpoint URL for your Marketo instance>/rest/v1/<API that you are calling>?access_token=<access_token>

Example

http://100-AEK-913.mktorest.com/rest/v1/lead/318581.json?access_token=cdf01657-110d-4155-99a7-f986b2ff13a0:int

If your call is successful, then it will return JSON with the format below:

{
    "requestId": "d82a#14e26755a9c",
    "result": [
        {
            "id": 318581,
            "updatedAt": "2015-06-11T23:15:23Z",
            "lastName": "Doe",
            "email": "jdoe@marketo.com",
            "createdAt": "2015-03-17T00:18:40Z",
            "firstName": "John"
        }
    ],
    "success": true
}

If you are interested in learning more about the Marketo REST APIs, this is a good place to start.

Viewing all 11 articles
Browse latest View live