Introduction
API Endpoint
https://api.mailshake.com/2017-04-01
// Install our node package
npm install mailshake-node --save
// See source here
https://github.com/mailshake/mailshake-node
Thank you for checking out the Mailshake API! We ♥️ devs because that’s who we are, so we hope you’ll find our API enjoyable. If you notice a typo or have a suggestion on making this documentation better, feel free to contact us.
You’ll interact with the Mailshake API by making GET
or POST
requests (POST
is recommended when sending larger payloads). All responses are JSON-formatted, and each application has its own quota limits based on your Mailshake subscription.
Getting your API key
To get your API key, go to the Extensions > API page in Mailshake and create your key. If your team is looking for higher limits, you can contact us to request an increase.
Making requests
When making a POST
request you can send content as a typical form payload by using the header:
Content-Type: application/x-www-form-urlencoded
or you can write JSON data to the request by using the header:
Content-Type: application/json
See authentication for examples of how to make requests and limits to understand your app’s constraints.
Responses
Single-item responses will be in this format:
{
"object": "campaign",
"id": 1,
"title": "My campaign",
"created": "2017-08-19T02:31:22.218Z"
}
Most endpoints return a single model of data. Check out our models section for specific examples and be sure to check out the errors section section, too.
Pagination
Paginated data will look like this:
{
"nextToken": "...",
"results": [
{ ... },
{ ... }
]
}
Get the next page of data like so:
mailshake.campaigns.list()
.then(result => {
console.log(`Page 1: ${JSON.stringify(result.results, null, 2)}`);
// Just call `next` on the result to fetch the next page.
return result.next();
})
.then(result => {
console.log(`Page 2: ${JSON.stringify(result.results, null, 2)}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/list" \
-u "my-api-key:" \
-d nextToken=...
Endpoints that return multiple results will include a nextToken
parameter that you can pass into another request to fetch the next results. These endpoints will also accept a perPage
parameter to control the size of the record sets.
If nextToken
is null then you’re looking at the last page of data.
Versioning
As we develop future versions of our API that are not backwards-compatible, we will leave the old version running and create a new url for the latest version. We will retain support for obsolete versions for a generous period of time and will send email notifications of any changes.
Current version:https://api.mailshake.com/2017-04-01
Future version format:https://api.mailshake.com/XXXX-YY-ZZ
Authentication
Most of the apps authorized to use the Mailshake API can only access their own team’s data. For these apps, you can use our simple authentication. Any app can use our OAuth version 2 authentication.
Simple
var mailshake = require('mailshake-node')('my-api-key');
mailshake.me()
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl uses the -u flag to pass basic auth credentials
(adding a colon after your API key prevents cURL from asking for a password).
curl "https://api.mailshake.com/2017-04-01/me" \
-u "my-api-key:"
Simply include your API key as a querystring parameter (apiKey
), part of your body json (apiKey
), or via an http Authorization
header that looks like:
Authorization: Basic [base-64 encoded version of your api key]
Make sure to replace
my-api-key
with your API key.
OAuth2
// mailshake-node has hooks to support most any OAuth library.
// You can either customize the request with `customizeRequest`
// or outright replace how the request is made with `overrideCreateRequest`
var mailshake = require('mailshake-node')({
customizeRequest(options) => {
// options.headers.authorization = [...oauth header...]
return options;
}),
// or
overrideCreateRequest(options, callbackFn) => {
// Create a standard node request via an OAuth tool
// Something that looks like this:
return https(options, callbackFn);
})
});
If your app has been approved as a 3rd-party app (one in which you can access other users’ data instead of limited of just your own team), you must use OAuth V2 to integrate with us. We’ll deliver your consumer key and secret to you manually. The other applicable OAuth settings are below:
Authorization
Authorization URL: https://app.mailshake.com/oauth/
Request Parameters
Include the following parameters as part of the query string of the authorization URL:
Parameter | Description |
---|---|
response_type=code |
Tells us that you are expecting to receive an authorization code |
client_id |
The client ID for your application |
redirect_uri |
The URI to send the user to after authorization is complete, must match a URI listed for your application |
scope |
One or more scope values (see below) indicating which parts of the user’s account you need access to |
state |
A random string generated by your application, which will be passed to the redirect_ui |
OAuth Scope
Specify the OAuth scope to tell customers what permissions you’re requesting. Specify the scopes in a comma-delimited string like so: campaign-read,campaign-write
Scope | Description |
---|---|
campaign-read | Grants read access to all operations. |
campaign-write | Grants write access to all operations. |
Response Parameters
Upon successful authorization, the user will be redirected to your redirect_uri
with the following query string parameters:
Parameter | Description |
---|---|
code |
The authorization code |
state |
The same state value you passed to the authorization URL |
Getting an Access Token
Access token URL: https://api.mailshake.com/2017-04-01/token
Request Parameters
POST
the following parameters to the access token URL:
Parameter | Description |
---|---|
grant_type=authorization_code |
Tells us that you are expecting to receive an authorization code |
client_id |
The client ID for your application |
client_secret |
The client secret for your application |
code |
The code you obtained from the authorization URL |
redirect_uri |
The same redirect URI passed to the authorization URL |
Response Properties
The response from the access token URL will be in JSON with the following properties:
Property | Description |
---|---|
access_token |
The token that can be used to be future requests |
refresh_token |
The token that can be used to obtain a new access token |
Refreshing an Access Token
Refresh token URL: https://api.mailshake.com/2017-04-01/token
Request Parameters
POST
the following parameters to the refresh token URL:
Parameter | Description |
---|---|
grant_type=refresh_token |
Tells us that you are refreshing a token |
client_id |
The client ID for your application |
client_secret |
The client secret for your application |
refresh_token |
The refresh token you obtained from the access token URL |
redirect_uri |
The same redirect URI passed to the authorization URL |
Response Properties
The response from the access token URL will be in JSON with the following properties:
Property | Description |
---|---|
access_token |
The new token that can be used to be future requests |
Test connection
mailshake.me()
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/me" \
-u "my-api-key:"
This endpoint returns a simple object with a User model attached.
{
"user": "[User model]"
}
You can hit our /me
endpoint to test that authentication is working. It will return information about the current user you are authenticating as.
Campaigns
List
mailshake.campaigns.list({
search: 'Venkman'
})
.then(result => {
result.results.forEach(campaign => {
console.log(JSON.stringify(campaign, null, 2));
});
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/list" \
-u "my-api-key:" \
-d search=Venkman
List all of a team’s campaigns.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
search | No | Filters what campaigns are returned. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
Get
mailshake.campaigns.get({
campaignID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/get" \
-u "my-api-key:" \
-d campaignID=1
This endpoint returns a Campaign model.
Retrieves a single campaign and its message sequence. A not_found
error will be returned if the campaign could not be found.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The ID of the campaign. |
Create
mailshake.campaigns.create({
title: 'My campaign'
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/create" \
-u "my-api-key:" \
-d title="My campaign"
This endpoint returns a Campaign model.
Creates a new campaign. This campaign cannot be sent until the user finishes the wizard in Mailshake’s user interface, but you can add recipients.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
title | [Month] [Day] Outreach |
No | A title to give to the campaign. |
senderID | No | The id of the sender to use for this campaign. |
Pause
mailshake.campaigns.pause({
campaignID: 1
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/pause" \
-u "my-api-key:" \
-d campaignID=1
This endpoint returns an empty response.
Immediately pauses all sending for a campaign. If a batch of emails for this campaign is currently being sent they will not be stopped.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The campaign to pause. |
Unpause
mailshake.campaigns.unpause({
campaignID: 1
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/unpause" \
-u "my-api-key:" \
-d campaignID=1
This endpoint returns an empty response.
Resumes sending for a campaign. This team’s sending calendar will reschedule itself to account for this campaign’s pending emails. In rare cases it may take up to 5 minutes for the calendar to show scheduled times for this campaign.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The campaign to unpause. |
Export
mailshake.campaigns.export({
campaignIDs: [1, 2, 3],
exportType: 'simple',
timezone: 'America/Indianapolis'
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/export" \
-u "my-api-key:" \
-H "Content-Type: application/json" \
-X POST -d '{"campaignIDs":[1, 2, 3], "exportType": "simple", "timezone": "America/Indianapolis"}'
This endpoint returns a CampaignExportRequest model.
Asynchronously starts an export of one or more campaigns to CSV format. All campaign data will be included in a single csv file you can download.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignIDs | Yes* | An array of campaign IDs to export. | |
exportType | Yes | The type of export to perform (see below). | |
timezone | UTC | No | The timezone that dates in the export should be based in. |
Export types
Type | Description |
---|---|
simple | Recipient-based export, listing each recipient (per campaign) on a single row. |
show-each-message |
Export based on sent messages, listing every sent email and related stats on a single row. |
unsubscribes | An export of all the unsubscribed email addresses for your team. |
ExportStatus
mailshake.campaigns.exportStatus({
statusID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/campaigns/export-status" \
-u "my-api-key:" \
-d statusID=1
This endpoint returns a CampaignExport model.
Exporting campaigns is an asynchronous process, so this endpoint lets you check on how things are going. If isFinished
is true, then the export has completed. The csvDownloadUrl
field provides the csv file you can download.
Recipients
Add
mailshake.recipients.add({
campaignID: 1,
addAsNewList: true,
listOfEmails: '"John Doe" <john@doe.com>, "Jane Doe" <jane@doe.com>'
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/add" \
-u "my-api-key:" \
-d campaignID=1 \
-d addAsNewList=true \
-d listOfEmails="\"John Doe\" <john@doe.com>, \"Jane Doe\" <jane@doe.com>"
Adds new recipients to a campaign. Each campaign can hold up to 5,000 recipients.
If you pass along a full name for your recipients, Mailshake will automatically prepare first
, last
, and name
(full name) as text replacements. If you only have a first name, use that value as their full name.
Any other fields you provide (like favorite_color
) can be used as text replacements within your campaign. If a text replacement isn’t found, that recipient’s emails will not be scheduled until you make corrections.
Here’s an example message:
Hi {{first}},
Thanks for adding your name to my email list on {{topic}}. I think you’re really going to like it!
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The campaign to add these recipients to. | |
addAsNewList | false | No | Pass true to keep these recipients grouped together. Otherwise they’ll be added to the last list you uploaded to your campaign. |
truncateExtraFields | false | No | Mailshake limits you to 30 recipient fields. A validation error will reject your request if you go over that limit unless you pass “true” for this parameter. In that case, we will simply ignore the extra fields. |
listOfEmails | Maybe | A comma or newline separated list of email addresses to add. You can include recipient names by using the format: "John Doe <john@doe.com>" |
|
addresses | Maybe | A structured list of recipient data that can include custom fields. | |
csvData | Maybe | A structured object representing a spreadsheet of comma-separated recipient data that can include custom columns as fields. |
Using addresses
mailshake.recipients.add({
campaignID: 1,
addAsNewList: true,
addresses: [
{
emailAddress: 'john@doe.com',
fullName: 'John Doe',
fields: {
favorite_color: 'Red'
}
}
]
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/add" \
-u "my-api-key:" \
-H "Content-Type: application/json" \
-X POST -d '{"campaignID":1,"addAsNewList":true,"addresses":[{"emailAddress":"john@doe.com","fullName":"John Doe","twitterID":"jdoe","instagramID":"jdoe","facebookUrl":"https://facebook.com/jdoe","linkedInUrl":"https://linkedin.com/in/jdoe","account":"J. Doe and Co.","phoneNumber":"5555555555","fields":{"favorite_color":"Red"}}]}'
This option lets you pass in an array of structured data. Each array item should be listed in this format;
Key | Required | Description |
---|---|---|
emailAddress | Yes | The recipient’s email address. |
fullName | No | The recipient’s full name, or first name if that’s all you have. |
twitterID | No | The recipient’s Twitter ID (just tne username, not the full URL). |
instagramID | No | The recipient’s Instagram ID (just tne username, not the full URL). |
facebookUrl | No | The recipient’s Facebook profile URL. |
linkedInUrl | No | The recipient’s LinkedIn profile URL. |
account | No | The account the recipient is associated with. |
phoneNumber | No | The recipient’s phone number |
fields | No | A simple JSON hash (not an array) of keys to values. Each field can be used as a text replacement. |
Using csvData
mailshake.recipients.add({
campaignID: 1,
addAsNewList: true,
csvData: {
csvRawData: 'email,name,favorite_color\njohn@doe.com,John Doe,Red',
emailColumnName: 'email',
fullNameColumnName: 'name'
}
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/add" \
-u "my-api-key:" \
-H "Content-Type: application/json" \
-X POST -d '{"campaignID":1,"addAsNewList":true,"csvData":{"csvRawData":"email,name,phone,fb,linkedin,ig,twitter,accountName,favorite_color\naug25-b@mailinator.com,John Doe,1231232233,https://www.facebook.com/jdoe,https://www.linkedin.com/jdoe,jdoeinsta,jdoetwitter,J. Doe and Co.,Red","emailColumnName":"email","fullNameColumnName":"name","phoneNumberColumnName":"phone","facebookUrlColumnName":"fb","linkedInUrlColumnName":"linkedin","instagramIDColumnName":"ig","twitterIDColumnName":"twitter","accountColumnName":"accountName"}}'
Pass in spreadsheet data in comma-separated-values format. You must have a column (of any name) that represents an email address, and optionally a column to represent a full name. All other columns will be translated into fields that can be used for text replacements.
Here is the format of the JSON object to be passed as the csvData
parameter:
Key | Required | Description |
---|---|---|
csvRawData | Maybe | Raw csv-formatted data. |
link | Maybe | A publicly accessible link that hosts csv-formatted data. |
emailColumnName | Yes | The name of the column that contains recipient email addresses. |
fullNameColumnName | No | The name of the column that contains recipient full names (or first names if that’s all you have). |
phoneNumberColumnName | No | The name of the column that contains recipient phone numbers |
facebookUrlColumnName | No | The name of the column that contains recipient Facebook profile URLs. |
linkedInUrlColumnName | No | The name of the column that contains recipient LinkedIn profile URLs. |
instagramIDColumnName | No | The name of the column that contains recipient Instagram IDs (just the username, not the full URL). |
twitterIDColumnName | No | The name of the column that contains the recipient Twitter IDs (just the username, not the full URL) |
accountColumnName | No | The name of the column that contains the recipient account names |
Response
This endpoint returns a AddRecipientsRequest model.
Adding recipients is an asynchronous process and may take a few minutes to fully process, but you’ll receive an immediate response to indicate how things are going. Email addresses that are on your unsubscribe list or ones that are already in your campaign will be ignored.
AddStatus
mailshake.recipients.addStatus({
statusID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/add-status" \
-u "my-api-key:" \
-d statusID=1
This endpoint returns a AddedRecipients model.
Adding recipients is an asynchronous process, so this endpoint lets you check on how things are going. If isFinished
is true, then the import has completed. The problems
field will let you determine the exact success or failure of the import.
List
mailshake.recipients.list({
campaignID: 1,
search: 'Egon'
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/list" \
-u "my-api-key:" \
-d campaignID=1 \
-d search=Egon
Lists all of the recipients in a campaign. You can use this endpoint to search recipients, filter by activity, or find recipients who have some of kind of problem (like a missing text replacement or an email that failed to send).
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The campaign to look in. | |
filter | No | Criteria to filter recipients with. | |
search | No | Filters what recipients are returned. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
Filter options
// Find recipients who have not opened any messages
mailshake.recipients.list({
campaignID: 1,
filter: {
action: 'opened',
negateAction: true
}
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/list" \
-u "my-api-key:" \
-H "Content-Type: application/json" \
-X POST -d '{"campaignID":"1","filter":{"action":"opened","negateAction":true}}'
Field | Default | Required | Description |
---|---|---|---|
action | Yes | The kind of recipient activity to look for. | |
negateAction | false | No | true to find recipients who have NOT taken the given action. |
campaignMessageID | No | If action is based on a message, you can limit it to only look at this message. |
Filter actions
Action | Description |
---|---|
opened | Recipients who have opened a message. |
clicked | Recipients who have clicked a link. |
replied | Recipients who have replied. |
wasSent | Recipients who were sent a message. |
bounced | Recipients who bounced. |
paused | Recipients who are paused. |
hasProblems | Recipients who have a missing text replacement or a failed sent email. |
Get
mailshake.recipients.get({
campaignID: 1,
emailAddress: 'john@doe.com'
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/get" \
-u "my-api-key:" \
-d campaignID=1 \
-d emailAddress=john@doe.com
This endpoint returns a Recipient model.
Gets a single recipient’s basic information. A not_found
error will be returned if the recipient could not be found.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
recipientID | Maybe | The ID of a recipient. | |
campaignID | Maybe | The campaign that this recipient belongs to. Required if emailAddress is specified. |
|
emailAddress | Maybe | The address of the recipient. |
Pause
mailshake.recipients.pause({
campaignID: 1,
emailAddress: 'john@doe.com'
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/pause" \
-u "my-api-key:" \
-d campaignID=1 \
-d emailAddress=john@doe.com
This endpoint returns a Recipient model.
Immediately pauses all sending for a single recipient. If any emails for recipient are currently being sent they will not be stopped.
A not_found
error will be returned if the recipient could not be found.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The campaign that this recipient belongs to. | |
emailAddress | Yes | The address of the recipient. |
Unpause
mailshake.recipients.unpause({
campaignID: 1,
emailAddress: 'john@doe.com'
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/unpause" \
-u "my-api-key:" \
-d campaignID=1 \
-d emailAddress=john@doe.com
This endpoint returns a Recipient model.
Resumes sending for a recipient. This team’s sending calendar will reschedule itself to account for this recipient’s pending emails. In rare cases it may take up to 5 minutes for the calendar to show updated scheduled times.
A not_found
error will be returned if the recipient could not be found.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | Yes | The campaign to unpause. | |
emailAddress | Yes | The address of the recipient. |
Unsubscribe
mailshake.recipients.unsubscribe({
emailAddresses: 'john@doe.com,jane@doe.com'
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/recipients/unsubscribe" \
-u "my-api-key:" \
-d emailAddresses=john@doe.com,jane@doe.com
This endpoint returns an empty response.
Adds a list of email addresses to your unsubscribe list.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
emailAddresses | Yes | A comma-separated list of email addresses to unsubscribe. |
Activity
These endpoints let you see what’s been going on with your campaigns.
Sent
mailshake.activity.sent({
campaignMessageType: 'initial'
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/sent" \
-u "my-api-key:" \
-d campaignMessageType=initial
This endpoint returns paginated SentMessage models.
Obtains the most recent emails you have sent. In most cases you’ll want to look at campaign-based emails, but this endpoint also lets you get one-off replies you’ve sent within Mailshake via Lead Catcher.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
messageType | any | No | If specified, you can filter to only one-off or campaign-message messages. one-off messages are replies you send manually within Mailshake, so in most cases you’ll want to omit this parameter or use campaign-message . |
campaignMessageType | any | No | Filter to a specific type of message within a campaign (see CampaignMessageTypes). |
campaignID | No | Restrict to a single campaign. | |
campaignMessageID | No | Restrict to a single message within a campaign. | |
recipientEmailAddress | No | Limit to specific recipients. If the value passed is not in the format of an email address, a fuzzy search will be done. Ergo, sending in firmxyz.com will match anyone on that domain. |
|
excludeBody | No | Excludes the email body from the response. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 25 | No | How many results to get at once, up to 25. |
Opens
mailshake.activity.opens()
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/opens" \
-u "my-api-key:"
Obtains the most recent emails opened.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | Restrict to a single campaign. | |
campaignMessageID | No | Restrict to a single message within a campaign. | |
excludeDuplicates | false | No | If true this will only not return data when recipients open the same email more than once. |
recipientEmailAddress | No | Limit to specific recipients. If the value passed is not in the format of an email address, a fuzzy search will be done. Ergo, sending in firmxyz.com will match anyone on that domain. |
|
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
since | No | Restrict results to events that occurred after since . Date is in UTC, and formatted like “2017-04-11 16:13:51” |
|
assignedToUserID | No | Restrict results to campaigns assigned to user |
Clicks
mailshake.activity.clicks()
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/clicks" \
-u "my-api-key:"
Obtains the most recent links clicked.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | Restrict to a single campaign. | |
excludeDuplicates | false | No | If true this will only not return data when recipients click the same link more than once. |
matchUrl | No | An exact matching of a specific link you’re tracking. | |
recipientEmailAddress | No | Limit to specific recipients. If the value passed is not in the format of an email address, a fuzzy search will be done. Ergo, sending in firmxyz.com will match anyone on that domain. |
|
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
since | No | Restrict results to events that occurred after since . Date is in UTC, and formatted like “2017-04-11 16:13:51” |
|
assignedToUserID | No | Restrict results to campaigns assigned to user |
Replies
mailshake.activity.replies({
replyType: 'reply'
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/replies" \
-u "my-api-key:" \
-d replyType=reply
Obtains the most recent replies to your sent emails. Pay special attention to replyType
because you can use this endpoint to look at bounces, out-of-office replies, etc.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
replyType | any | No | Filter to only reply , bounce , out-of-office , unsubscribe or delay-notification replies. |
campaignID | No | Restrict to a single campaign. | |
recipientEmailAddress | No | Limit to specific recipients. If the value passed is not in the format of an email address, a fuzzy search will be done. Ergo, sending in firmxyz.com will match anyone on that domain. |
|
nextToken | No | Fetches the next page from a previous request. | |
perPage | 25 | No | How many results to get at once, up to 25. |
Created Leads
mailshake.activity.createdLeads({
campaignID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/created-leads" \
-u "my-api-key:" \
-d campaignID=1
Obtains the most recently created leads. Usually leads are automatically created from the rules you’ve set up in Lead Catcher, but Mailshake users can also manually turn recipients into leads.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | Restrict to a single campaign. | |
recipientEmailAddress | No | Limit to specific recipients. If the value passed is not in the format of an email address, a fuzzy search will be done. Ergo, sending in firmxyz.com will match anyone on that domain. |
|
assignedToEmailAddress | No | Only get leads that are assigned to this person on your team. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
since | No | Restrict results to events that occurred after since . Date is in UTC, and formatted like “2017-04-11 16:13:51” |
|
assignedToUserID | No | Restrict results to campaigns assigned to user |
Assigned Leads
mailshake.activity.leadAssignments({
campaignID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/lead-assignments" \
-u "my-api-key:" \
-d campaignID=1
Obtains the most recently assigned leads. Leads are assigned by Mailshake users manually assigning them.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | Restrict to a single campaign. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
since | No | Restrict results to events that occurred after since . Date is in UTC, and formatted like “2017-04-11 16:13:51” |
|
assignedToUserID | No | Restrict results to campaigns assigned to user |
Lead Status Changes
mailshake.activity.leadStatusChanges({
campaignID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/activity/lead-status-changes" \
-u "my-api-key:" \
-d campaignID=1
Obtains the most recently updated leads. A lead can be closed, ignored, opened, or reopened. A reopened lead has open
as its status, it’s just that at one point that lead had been ignored or closed.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | Restrict to a single campaign. | |
recipientEmailAddress | No | Limit to specific recipients. If the value passed is not in the format of an email address, a fuzzy search will be done. Ergo, sending in firmxyz.com will match anyone on that domain. |
|
assignedToEmailAddress | No | Only get leads that are assigned to this person on your team. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
since | No | Restrict results to events that occurred after since . Date is in UTC, and formatted like “2017-04-11 16:13:51” |
|
assignedToUserID | No | Restrict results to campaigns assigned to user |
Leads
A lead in Mailshake is a recipient who may be interested in whatever you’re pitching in your campaigns. Lead Catcher will automatically find leads based on criteria you set up, but you can also create and manage leads via the API.
List
mailshake.leads.list({
campaignID: 1,
status: 'open'
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/leads/list" \
-u "my-api-key:" \
-d campaignID=1 \
-d status=open
Lists your leads. You can use this endpoint to search leads, filter by status, or find leads assigned to one of your teammates.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | Filter leads to the ones from this campaign. | |
status | No | Filter to leads in a particular status. | |
assignedToEmailAddress | No | Leads assigned to this teammate. | |
search | No | Filters what leads are returned. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
Get
mailshake.leads.get({
campaignID: 1,
emailAddress: 'john@doe.com'
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/leads/get" \
-u "my-api-key:" \
-d campaignID=1 \
-d emailAddress=john@doe.com
This endpoint returns a Lead model.
Gets a single lead. A not_found
error will be returned if the lead could not be found.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
leadID | Maybe | The ID of a lead. | |
recipientID | Maybe | The ID of the recipient that this lead is for. | |
campaignID | Maybe | The campaign that this recipient belongs to. Required if emailAddress is specified. |
|
emailAddress | Maybe | The address of the recipient. |
Create
mailshake.leads.create({
recipientIDs: [1, 2, 3]
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
// Or
mailshake.leads.create({
campaignID: 1,
emailAddresses: [
'a@johndoe.com',
'c@johndoe.com',
'd@johndoe.com'
]
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/leads/create" \
-u "my-api-key:" \
-d recipientIDs=1
-d recipientIDs=2
-d recipientIDs=3
Or
curl "https://api.mailshake.com/2017-04-01/leads/create" \
-u "my-api-key:" \
-d campaignID=1
-d emailAddresses=a@johndoe.com
-d emailAddresses=b@johndoe.com
-d emailAddresses=c@johndoe.com
This endpoint returns CreatedLeads model.
Creates one or more leads from recipients of a campaign. You can either pass in the IDs of recipients or if it’s easier, you can pass their email addresses instead. If a recipient was already a lead and was won, this will reopen them as a lead.
Parameters
You can specify recipientIDs
or emailAddresses
or both.
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | The ID of the campaign from which to create a lead. | |
emailAddresses | No | A list of email addresses to find recipients from for creating leads. This list will be added to the recipientIDs parameter if both are passed. |
|
recipientIDs | No | A list of recipient IDs to create leads from. This list will be added to the recipientIDs parameter if both are passed. |
Close
mailshake.leads.close({
leadID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/leads/close" \
-u "my-api-key:" \
-d leadID=1
This endpoint returns an empty response.
Marks a lead as “Won” (or “Lost” if you send in that status). The alternative to these states is “Ignored” which means that the lead wasn’t worth pursuing.
Parameters
Only one identifier is required – just use what’s most convenient to you.
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | ||
emailAddress | No | The email address of a recipient in this campaign. | |
recipientID | No | The ID of the recipient that this lead refers to. | |
leadID | No | The ID of the lead. | |
status | “closed” | No | Can be set to “closed” or “lost”. |
Ignore
mailshake.leads.ignore({
leadID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/leads/ignore" \
-u "my-api-key:" \
-d leadID=1
This endpoint returns a LeadStatus model.
Marks a lead as “ignored” when means the lead wasn’t worth pursuing. Use Close and pass lost
as the status
to indicate you worked on this lead but it didn’t pan out.
Parameters
Only one identifier is required – just use what’s most convenient to you.
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | ||
emailAddress | No | The email address of a recipient in this campaign. | |
recipientID | No | The ID of the recipient that this lead refers to. | |
leadID | No | The ID of the lead. |
Reopen
mailshake.leads.reopen({
leadID: 1
})
.then(result => {
console.log(JSON.stringify(result, null, 2));
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/leads/reopen" \
-u "my-api-key:" \
-d leadID=1
This endpoint returns a LeadStatus model.
Takes a closed or ignored lead and makes it open again and available for review.
Parameters
Only one identifier is required – just use what’s most convenient to you.
Parameter | Default | Required | Description |
---|---|---|---|
campaignID | No | ||
emailAddress | No | The email address of a recipient in this campaign. | |
recipientID | No | The ID of the recipient that this lead refers to. | |
leadID | No | The ID of the lead. |
Team
List Members
mailshake.team.listMembers()
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/team/list-members" \
-u "my-api-key:"
Lists the users belonging to this team.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
search | No | Filters the returned users. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
Senders
List
mailshake.senders.list({
search: '@gmail.com'
})
.then(result => {
result.results.forEach(campaign => {
console.log(JSON.stringify(campaign, null, 2));
});
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/senders/list" \
-u "my-api-key:" \
-d search="@gmail.com"
List all of a team’s senders.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
search | No | Filters what senders are returned. | |
nextToken | No | Fetches the next page from a previous request. | |
perPage | 100 | No | How many results to get at once, up to 100. |
Pushes
The type of request we’ll send to your servers:
{
"resource_url": "https://api.mailshake.com/2017-04-01/..."
}
let express = require('express');
let bodyParser = require('body-parser');
let PushHandler = require('mailshake-node').PushHandler;
// Start your web server
let app = express();
app.use(bodyParser.json({}));
// Hook up the Mailshake push handler
let handler = new PushHandler(mailshake, {
baseUrl: 'https://yourwebsite.com',
rootPath: 'path/to/your/handler',
secret: 'my-secret'
});
handler.on('push', push => {
console.log(JSON.stringify(push, null, 2));
});
handler.on('pushError', err => {
console.error(`${err.code}: ${err.stack}`);
});
handler.hookExpress(app);
// Start your server
app.listen(80);
// Verify that it's hooked up properly by visiting your site, something like:
// https://yourwebsite.com/path/to/your/handler/my-secret/some-unique-id
One of the cooler things you can do with the Mailshake API is subscribe to pushes. Whenever an action occurs that you’ve subscribed to, Mailshake will make a HTTP request to your servers so you can react in real time.
Your servers need to accept POST
requests from us using the application/json
content type. We’ll include a simple object with resource_url
as its only key. Send an HTTP request to this url and be sure to include the same authentication as with our other API operations.
Your server should respond to us with a 200
status indicating that you’ve handled the request (otherwise we’ll wait and try a few more times in case your server is under maintenance).
Create
handler.subscribe('Clicked')
.then(targetUrl => {
// Save your targetUrl somewhere so you can unsubscribe later
})
.catch(err => {
console.error(`${err.code}: ${err.stack}`);
});
curl "https://api.mailshake.com/2017-04-01/push/create" \
-u "my-api-key:" \
-d event=Clicked \
-d targetUrl=https://mysite.com/some-secret/some-unique-id
Starts a subscription for a type of push.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
targetUrl | Yes | The publicly accessible url to your servers that we should send a POST request to. We highly recommend using https and including a secret key so that other actors can’t send requests as if they were Mailshake. |
|
event | Yes | The type of event you’re subscribing to. | |
filter | No | If you only want subscriptions when certain criteria are met, specify them as a JSON object. |
Filters
You can apply a filter to some events so that you only get pushes that meet certain criteria.
Filter fields:
Field | Description |
---|---|
campaignID | To only get events for a specific campaign. |
campaignMessageID | For events based on message, only get events for a specific message. |
excludeDuplicates | true if you don’t want to get pushes for duplicate opens or clicks. |
matchUrl | For clicks you can only be notified when this exact url is clicked. |
messageType | For sent messages you can be notified only when certain types of messages are sent. |
Events
Event | Description |
---|---|
Clicked | Someone clicked a link. |
Opened | Someone opened an email. |
Replied | Someone replied to one of your emails. |
MessageSent | Mailshake sent an email on your behalf. |
LeadCreated | A lead was created. |
LeadStatusChanged | A lead’s status was changed. |
Delete
let targetUrl; // Look this up from when you subscribed
handler.unsubscribe(targetUrl)
.then(() => {
// Done
})
.catch(err => {
console.error(`${err.code}: ${err.message}`);
});
curl "https://api.mailshake.com/2017-04-01/push/delete" \
-u "my-api-key:" \
-d targetUrl=https://mysite.com/some-secret/some-unique-id
Unsubscribes a push you previously created. Since all subscribed pushes require a unique targetUrl
, that’s all you need to send in to delete your push.
Parameters
Parameter | Default | Required | Description |
---|---|---|---|
targetUrl | Yes | The unique target url of a push. |
Models
Most models will have these common fields:
Field | Type | Description |
---|---|---|
object | string | The type of model this data represents. |
id | integer | The unique ID of this data. |
User
{
"object": "user",
"id": 1,
"teamID": 1,
"teamName": "My Team",
"isTeamAdmin": true,
"isDisabled": false,
"emailAddress": "me@example.com",
"fullName": "Jane Doe",
"first": "Jane",
"last": "Doe",
"teamBlockedDate": null
}
A Mailshake user and the team that they are on.
Campaign
{
"object": "campaign",
"id": 1,
"title": "My campaign",
"created": "2017-08-19T02:31:22.218Z",
"isArchived": false,
"isPaused": false,
"messages": [
{
"object": "message",
"id": 1,
"type": "initial",
"subject": "My subject",
"replyToID": null,
"isPaused": false
},
{
"object": "message",
"id": 2,
"type": "follow-up",
"subject": "null",
"replyToID": 1,
"isPaused": false
},
{
"object": "message",
"id": 3,
"type": "follow-up",
"subject": "null",
"replyToID": 1,
"isPaused": false
},
{
"object": "message",
"id": 4,
"type": "drip",
"subject": "Here's a drip email",
"replyToID": null,
"isPaused": false
},
{
"object": "message",
"id": 5,
"type": "on-click",
"subject": "You clicked my link",
"replyToID": null,
"isPaused": false
}
],
"sender": {
"object": "sender",
"id": "abc",
"emailAddress": "mysender@company.com",
"fromName": "Firm ABC",
"created": "2017-08-19T02:31:22.218Z"
},
"url": "https://mailshake.com/app/#/..."
}
A Mailshake campaign is the container for a sequence of messages and the recipients to whom they’ll be sent.
Notable fields:
Field | Description |
---|---|
isArchived | Will be set to true if this campaign is archived. Archived campaigns must be unarchived via the web interface before they can be fully interacted with. |
Message
{
"object": "message",
"id": 423,
"type": "initial",
"subject": "My subject",
"replyToID": null,
"isPaused": false
}
{
"object": "message",
"id": 424,
"type": "follow-up",
"subject": null,
"replyToID": 423,
"isPaused": false
}
A message in your campaign such as a follow-up or a drip message.
Notable fields:
Field | Description |
---|---|
replyToID | Only for follow-up messages, this is the message that indicates the root of the reply chain. If 4 messages will be sent in one reply chain, they’ll all have the same replyToID value. |
MessageTypes
Type | Description |
---|---|
initial | The first email in a campaign. |
follow-up |
Replies to the initial message sent only if the recipient doesn’t reply. |
drip | Secondary emails that are not stopped by a recipient’s reply. |
on-click |
Email fired whenever the recipient clicks a specific link in any of the emails. |
Sender
{
"object": "sender",
"id": "abc",
"emailAddress": "mysender@company.com",
"fromName": "Firm ABC",
"created": "2017-08-19T02:31:22.218Z"
}
A connected email address responsible for sending emails for their associated campaigns.
CreatedLeads
{
"leads": [
{
"recipientID": 1,
"leadID": 1
}
],
"emailsNotFound": [
"me@example.com"
],
"invalidEmails": [
"invalidexample.com"
],
"recipientIDsNotFound": [2, 3],
"isEmpty": false
}
The results of an attempt to create leads.
Notable fields:
Key | Description |
---|---|
emailsNotFound | A list of email addresses that were not turned into leads because they did not match any recipients. |
invalidEmails | A list of email addresses that were not turned into leads because they did not pass validation. |
recipientIDsNotFound | A list of recipient IDs that were not found. |
isEmpty | true if no email addresses or recipient IDs were actually turned into leads. |
LeadStatus
{
"status": "ignored",
"leadID": 1
}
The result of a status change of a lead. See Lead Statuses.
SentMessage
{
"object": "sent-message",
"id": 1,
"actionDate": "2017-04-06T17:45:07.188Z",
"recipient": {
"object": "recipient",
"id": 399,
"emailAddress": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe",
"created": "2017-04-06T17:45:07.188Z",
"fields": {
"favorite_color": "Red"
}
},
"campaign": {
"object": "campaign",
"id": 248,
"title": "My Campaign",
"created": "2017-04-06T17:45:07.188Z"
},
"type": "campaign-message",
"message": {
"object": "message",
"id": 423,
"type": "initial",
"subject": "My subject"
},
"from": {
"object": "email-address",
"address": "sally@doe.com",
"fullName": "Sally Doe",
"first": "Sally",
"last": "Doe"
},
"to": [
{
"object": "email-address",
"address": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe"
}
],
"subject": "This is my subject",
"externalID": "...",
"externalRawMessageID": "...",
"externalConversationID": "...",
"rawBody": "...",
"body": "...",
"plainTextBody": "..."
}
An email that was sent via Mailshake. Most messages have a messageType
of campaign
which means they were sent as part of a campaign sequence. Messages with one-off
were manual replies sent via Lead Catcher.
Notable fields:
Field | Description |
---|---|
message | For campaign sent messages, this is the message inside your campaign’s sequence. one-off sent messages will omit this field. |
externalID | The ID of the email on the mail account’s platform. |
externalRawMessageID | The actual ID of the email message from the emails’ headers. |
externalConversationID | The ID of the email thread on the mail account’s platform. |
rawBody | The raw HTML. |
body | This is is the HTML with tracking and replies stripped out. |
plainTextBody | A display-friendly version of the email body. |
Message Types
Type | Description |
---|---|
one-off | An email sent as a manual reply via Lead Catcher. |
campaign | An email sent as part of a campaign sequence. |
Open
{
"object": "open",
"id": 1,
"actionDate": "2017-04-06T17:40:23.194Z",
"isDuplicate": false,
"contactID": 1,
"recipient": {
"object": "recipient",
"id": 1,
"emailAddress": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe",
"created": "2017-04-06T17:45:07.188Z",
"fields": {
"favorite_color": "Red"
}
},
"campaign": {
"object": "campaign",
"id": 1,
"title": "My Campaign",
"created": "2017-04-06T17:40:23.194Z"
},
"parent": {
"object": "sent-message",
"id": 1,
"type": "campaign-message",
"message": {
"object": "message",
"id": 1,
"type": "initial",
"subject": "My subject"
}
}
}
The result of a recipient opening one of your sent emails.
Notable fields:
Field | Description |
---|---|
parent | An abbreviated version of the sent message that was opened. In most cases this is an email in your campaign sequence, but it could be a one-off email sent via Lead Catcher. |
isDuplicate | true if this recipient is opening the email for the second or nth time. |
Click
{
"object": "click",
"id": 1,
"link": "http://google.com",
"actionDate": "2017-04-06T17:38:53.369Z",
"isDuplicate": false,
"contactID": 1,
"recipient": {
"object": "recipient",
"id": 1,
"emailAddress": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe",
"created": "2017-04-06T17:45:07.188Z",
"fields": {
"favorite_color": "Red"
}
},
"campaign": {
"object": "campaign",
"id": 1,
"title": "My Campaign",
"created": "2017-04-06T17:40:23.194Z"
},
"parent": {
"object": "sent-message",
"id": 1,
"type": "campaign-message",
"message": {
"object": "message",
"id": 1,
"type": "initial",
"subject": "My subject"
}
}
}
The result of a recipient clicking a link in one of your sent emails.
Notable fields:
Field | Description |
---|---|
link | The full url that was clicked. |
parent | An abbreviated version of the sent message that was opened. In most cases this is an email in your campaign sequence, but it could be a one-off email sent via Lead Catcher. |
isDuplicate | true if this recipient is clicking the link for the second or nth time. |
Reply
{
"object": "reply",
"id": 1,
"actionDate": "2017-04-06T17:42:54.475Z",
"recipient": {
"object": "recipient",
"id": 1,
"emailAddress": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe",
"created": "2017-04-06T17:45:07.188Z",
"fields": {
"favorite_color": "Red"
}
},
"campaign": {
"object": "campaign",
"id": 1,
"title": "My Campaign",
"created": "2017-04-06T17:40:23.194Z"
},
"type": "out-of-office",
"parent": {
"object": "sent-message",
"id": 1,
"type": "campaign-message",
"message": {
"object": "message",
"id": 1,
"type": "initial",
"subject": "My subject"
}
},
"subject": "Re: This is my subject",
"externalID": "...",
"externalRawMessageID": "...",
"externalConversationID": "...",
"rawBody": "...",
"body": "...",
"plainTextBody": "...",
"contactID": 1,
}
Represents any kind of reply received from a recipient to one of your sent emails.
Notable fields:
Field | Description |
---|---|
type | The type of reply. |
parent | An abbreviated version of the sent message that was opened. In most cases this is an email in your campaign sequence, but it could be a one-off email sent via Lead Catcher. |
externalID | The ID of the email on the mail account’s platform. |
externalRawMessageID | The actual ID of the email message from the emails’ headers. |
externalConversationID | The ID of the email thread on the mail account’s platform. |
rawBody | The raw HTML. |
body | This is is the HTML with tracking and replies stripped out. |
plainTextBody | A display-friendly version of the email body. |
ReplyType
Type | Description |
---|---|
reply | A normal reply. |
bounce | Information about why your email bounced. |
out-of-office | An “I’m out of the office” reply. Follow-ups will still be sent to these folks unless you pause them. |
unsubscribe | The user was unsubscribed because their reply requested us to do so. |
delay-notification | Your mail account indicated that your original message is still trying to be sent. |
Lead
{
"object": "lead",
"id": 1,
"created": "2017-04-11T16:13:51.956Z",
"openedDate": "2017-04-11T16:13:51.955Z",
"lastStatusChangeDate": null,
"contactID": 1,
"assignedOnDate": "2017-04-11T16:13:51.955Z",
"recipient": {
"object": "recipient",
"id": 1,
"emailAddress": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe",
"created": "2017-04-06T17:45:07.188Z",
"fields": {
"favorite_color": "Red"
}
},
"campaign": {
"object": "campaign",
"id": 1,
"title": "My Campaign",
"created": "2017-04-11T16:13:51.956Z",
},
"status": "open",
"assignedTo": {
"object": "user",
"id": 1,
"emailAddress": "lucy@yourteam.com",
"fullName": "Lucy Doe",
"first": "Lucy",
"last": "Doe"
}
}
A recipient that was (at least at one time) a prospect. Leads are created in the open
status and can be set closed
, lost
, or ignored
.
Notable fields:
Field | Description |
---|---|
status | The current status of this lead. |
Lead Statuses
Status | Description |
---|---|
open | The lead is available for review. |
ignored | The lead wasn’t worth pursuing. |
closed | The lead turned into a successful interaction. |
lost | The lead didn’t pan out. |
AddRecipientsRequest
{
"invalidEmails": ["oneexample.com", "twoexample.com"],
"isEmpty": false,
"checkStatusID": 1
}
Notable fields:
Key | Description |
---|---|
invalidEmails | A list of email addresses that were not imported because they did not pass validation. |
isEmpty | true if no email addresses were actually imported. |
checkStatusID | An ID you can use to monitor the import. |
AddedRecipients
{
"isFinished": true,
"problems": {
"unsubscribedEmails": ["one@example.com"],
"alreadyInCampaignEmails": ["two@example.com"],
"passedAccountLimitEmails": ["three@example.com"],
"hasProblems": true
}
}
Describes the progress in adding a batch of recipients.
Notable fields for problems
:
Key | Description |
---|---|
unsubscribedEmails | A list of email addresses that are on your unsubscribe list. |
alreadyInCampaignEmails | A list of email addresses that were already part of this campaign. |
passedAccountLimitEmails | A list of email addresses that could not be imported because your campaign exceeded the number of allowed recipients it can hold. |
Recipient
{
"object": "recipient",
"id": 1,
"emailAddress": "john@doe.com",
"fullName": "John Doe",
"first": "John",
"last": "Doe",
"created": "2017-04-06T17:45:07.188Z",
"isPaused": false,
"contactID": 1,
"fields": {
"favorite_color": "Red"
}
}
Notable fields:
Key | Description |
---|---|
fields | A simple JSON hash (not an array) of keys to values. Each field can be used as a text replacement. |
CampaignExportRequest
{
"isEmpty": false,
"checkStatusID": 1
}
Notable fields:
Key | Description |
---|---|
isEmpty | true if no campaigns were actually exported. |
checkStatusID | An ID you can use to monitor the export. |
CampaignExport
{
"isFinished": true,
"csvDownloadUrl": "https://.../something-unique.csv"
}
Notable fields:
Key | Description |
---|---|
isFinished | true when the export is done, false to wait a bit and check again. |
csvDownloadUrl | When finished, this is the url of the CSV file you can download containing your export. |
Errors
A raw error response looks like this:
{
"code": "invalid_api_key",
"error": "Invalid api key",
"time": "2017-08-21T13:23:11.814Z"
}
Mailshake uses the following error codes:
General errors
Code | Description |
---|---|
invalid_api_key | The key you provided us was either missing or invalid. |
missing_team_admin | Your team doesn’t have an active team administrator (check that your billing is up to date) |
missing_dependent_data | The object on which you’re acting wasn’t found or you don’t have permission. An example would be trying to pause a campaign with an ID that doesn’t reference a campaign. |
missing_parameter | Your request is missing a required parameter. |
invalid_parameter | One of your request’s parameter is in an unsupported format. |
not_authorized | Your credentials don’t allow you to execute this request. |
not_found | A more semantically permissive version of missing_dependent_data . We’re splitting hairs a bit here, but generally speaking when this code is returned your application might just want to note the problem and carry on. Whereas if you encounter the missing_dependent_data error it’s more of a show-stopper. |
exceeds_monthly_recipients | You can’t add this many recipients because you will pass your monthly quota allowance. You can contact us to request an increase. |
user_not_admin | The user on file for your application must be an administrator of your team. |
user_is_disabled | The user on file for your application must have an active account (check your billing) |
missing_subscription | Your team must have an active and paid subscription to Mailshake. |
team_blocked | Your team has been blocked while our compliance team runs a review of your usage of our platform. |
limit_reached | Your application has exceeded it’s quota and must wait to make more requests, or you can contact us to request an increased quota. The message will indicate the next time you can try a request: Please wait and try again after: 2017-08-21T15:16:15.207Z |
internal_error | Something general went wrong with Mailshake. This may be a temporary issue or we may have a bug (if so notify us). |
unspecified_error | Your request was probably rejected by some kind of validation. |
Specific to OAuth2
Code | Description |
---|---|
unauthorized_request | Your request is missing authentication. |
invalid_client | Your client_id or client_secret parameter is wrong. |
invalid_grant | Your request for authorization is malformed. |
app_not_approved | Your application is not allowed to be used by other teams. |
invalid_token | Your token has expired. Try using your refresh token to get another access token. |
missing_scope | Your authentication request did not specify any scopes. |
Limits
Based on your plan with us, your API key will have a few limitations on your usage. You can contact us to request an increase.
New recipients / month
Each Mailshake campaign can hold up to 5,000 recipients. However, adding recipients through the Mailshake API is limited to a certain number per month. This isn’t a per-campaign limit, it’s accumulative across all campaigns. If your app attempts to exceed this limit, you’ll get the exceeds_monthly_recipients
error back.
The monthly window is aligned with the calendar year. Ergo on the 1st of each month your recipient limit will reset.
Rate limits
As an example of quota units work, check this out:
// Quota units: 400
mailshake.campaigns.pause(1)
// Quota units: 395
mailshake.campaigns.pause(2)
mailshake.campaigns.pause(3)
// Quota units: 385
mailshake.recipients.add([ /* 32 recipients */ ])
// This ^^ costs 20 + 32, or 52
// Quota units: 343
// ...an hour passes by ...
// Quota units: 400
Each call to our API costs a varying number of “quota units,” and you’re allowed X quota units per hour to limit how frequently you can make requests. If you hit a limit, you’ll get the error code limit_reached
. The error message will indicate when you can try again (see our error codes).
Units | Operation |
---|---|
10 | Campaigns > List |
5 | Campaigns > Pause |
25 | Campaigns > Unpause |
20 + N |
Campaigns > Export |
25 | Leads > Create |
5 | Leads > Close |
5 | Leads > Ignore |
5 | Leads > Reopen |
20 + N |
Recipients > Add |
10 | Recipients > List |
5 | Recipients > Pause |
10 | Recipients > Unpause |
2 | Recipients > Unsubscribe |
100 | Push > Create |
3 | Activity > Clicks |
2 | Activity > Opens |
1 | Activity > Sent |
10 | Activity > Replies |
5 | Activity > CreatedLeads |
5 | Activity > LeadStatusChanges |