Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jeremy Ruston, Copyright © 2007-2011 UnaMesa Association
Our API provides access to various useful back-end functions in Smartmessages, in particular getting a list of mailing lists, subscribing and unsubscribing, and getting info on recipients (including mailing history).
Our API may be overkill for some tasks, particularly for simple synchronisation purposes - our [[Callback|Callbacks]] system may be all you need.
!!What's an API?
An API is an [[Application Programming Interface|http://en.wikipedia.org/wiki/Api]], and presents an access point through which you can write code to talk to Smartmessages behind-the-scenes functions without all that visual HTML stuff to get in the way. This might help you to:
* Build your own subscribe/unsubscribe pages
* Integrate with your CRM system
* Anything else you might want to do!
!!Implementation
The API is an [[RPC|http://en.wikipedia.org/wiki/Remote_Procedure_Call]]-style interface, providing responses suitable for direct consumption by ~JavaScript ([[JSON|http://en.wikipedia.org/wiki/Json]]) and PHP (serialised arrays), XML (everything else), plus simple HTML output to help you debug your code.
The initial focus of our API has been to make basic information available, and provide you with sufficient resources for you to build your own subscribe and unsubscribe pages. If you don't want to go that far, you can [[automate our subscribe and unsubscribe pages|Subscribing]] anyway. There are plenty more functions yet to come!
''Note that all string data, parameters and responses must use the ~UTF-8 character set''.
We reserve the right to withdraw API access, or to place restrictions on the quantity and/or rate of connections and requests.
!!How To Use The API
Firstly you'll need to get API access enabled for your login ID, or we can create a new login for you that has appropriate permissions. Next, go to your account tab inside smartmessages and copy the API key shown below your account name. If you have access to multiple accounts, they will have different API keys. Then you need to start writing your scripts (using PHP, Java, ASP, Ruby, etc) that call the functions we provide.
!!!Give me the code!
We host our client libraries and example code here and on [[GitHub|https://github.com/Synchro/SmartmessagesClients]]. If you make some worthwhile changes, please fork our code on there and send us a pull request. If you want to suggest an improvement or report a bug please [[create an issue|https://github.com/Synchro/SmartmessagesClients/issues]].
The quickest way to get started is with PHP using our [[API wrapper class|APIExamples]], which implements all features and is ready to go. If you're using another language, you might like to use that code as a basis.
The initial access point is https://www.smartmessages.net/api/, but when you log in, you will be provided with an endpoint URL that you should direct subsequent calls to. This allows us future flexibility in how we support the API.
Though we provide JSON output, it's not a good idea to implement access to this API using client-side ~JavaScript, because it's not possible to do so without exposing your login credentials and API key, which would compromise the security of your data.
!!Errors and Feedback
Any function that fails for some reason will return a status value of false, usually accompanied by an error code, and a plain text error message. Some responses contain an additional message field for additional information about the function you just called.
!!!Defined error codes
* ~ERR_INCORRECT_ID_PASS: 1
* ~ERR_UNKNOWN_MISSING_METHOD: 2
* ~ERR_INCORRECT_METHOD_PARAMS: 3
* ~ERR_NO_PERMISSION: 4
* ~ERR_INVALID_EMAIL: 5
* ~ERR_USER_NOT_FOUND: 6
* ~ERR_ALREADY_SUBSCRIBED: 7
* ~ERR_SESSION_EXPIRED: 8
* ~ERR_MISSING_API_KEY: 9
* ~ERR_LOGIN_FAILED_SUSPENDED: 10
* ~ERR_LOGIN_STILL_SUSPENDED: 11
* ~ERR_INVALID_LIST_ID: 12
* ~ERR_SUBSCRIPTION_NOT_FOUND: 13
* ~ERR_INVALID_CUSTOMER_ID: 14
* ~ERR_LIST_UPLOAD_FAILED: 15
* ~ERR_LIST_NOT_YOURS: 16
* ~ERR_ACCOUNT_NOT_FOUND: 18
* ~ERR_NOT_YET_IMPLEMENTED: 19
* ~ERR_INVALID_URL: 20
* ~ERR_UNKNOWN_UPLOAD: 21
* ~ERR_INVALID_NAME: 22
* ~ERR_INVALID_CAMPAIGN_ID: 23
* ~ERR_CAMPAIGN_NOT_YOURS: 24
* ~ERR_TEMPLATE_NOT_FOUND: 25
* ~ERR_INCORRECT_API_KEY: 26
* ~ERR_TEMPLATE_NOT_YOURS: 27
* ~ERR_BAD_URL: 28
* ~ERR_DELETED: 29
* ~ERR_INVALID_MAILSHOT_ID: 30
* ~ERR_NEEDS_UPGRADE: 31
* ~ERR_INTERNAL: 99
Generally you should never receive an error 99 - please report it to us if you do.
!!Output formats
People like to work in different ways, so we provide output in multiple formats which you can choose as best fits your application by specifying which one you want when you log in:
*''php'': This is the result of serialising PHP arrays. If you're working in PHP (as we are), this is by far the easiest format to deal with as all you need to do is [[unserialize($response)|http://www.php.net/manual/en/function.unserialize.php]] to have all the response data in a ready-to use format. This is the default format, and is also what our PHP client class uses.
*''json'': [[JavaScript Object Notation|http://en.wikipedia.org/wiki/Json]] is an increasingly popular markup for web applications due to its compact syntax, ease of parsing and cross-language support.
*''xml'': XML is very popular and flexible, so we provide simple XML output too, though it's a bit more verbose and slower than json or php, so use them if you can.
*''html'': This outputs a simple full HTML page with a debug dump of the response on. This is only of use for debugging.
*''htmlfragment'': Outputs the same data as html format, but without html, head and body tags, useful for debugging if you're making several API calls.
!!Functions
Exact parameter names and response labels are given in italic text. Mandatory parameters are bold. All functions except login require an //''accesskey''// parameter which you will receive in the response to a successful login call, and this parameter is not shown on parameter lists.
!!Connection functions
!!!login
!!!!Parameters:
*//''username''//: the email address that you use to log into smartmessages
*//''password''//: your password
*//''apikey''//: the key you copied from your account tab.
*//outputformat//: an optional parameter to set the format to one of xml, json, php (the default), html or htmlfragment
!!!!Response
*//accesskey// => string 'abc123'
*//endpoint// => string 'https://www.smartmessages.net/api/'
*//status// => boolean true
*//accountname// => string 'Your account name'
*//username// => string 'Your login email address'
*//expires// => integer timestamp
This is the first function you need to call. You should check for the presence of the 'status' value and check that it is not false. You will also be provided with an access key which you will need for all subsequent function calls, and you should use the endpoint value to form the basis of your access ~URLs. The timestamp indicates when your session will expire, though it is extended every time you make a function call.
You need to provide your id and password as well as the API key because the API key is common to all users in an account, but permissions are defined per user.
So that you don't need to expose your own login credentials in source code you may provide to your own customers, we can create additional accounts that have ~API-only access that are unique for your application. Feel free to use your own ID while developing, and request a new separate ID from us when you're ready to deploy.
!!!ping
!!!!Parameters:
*None
!!!!Response:
*//status// => boolean
Does nothing except refresh your access key. Use this if you want to keep your access key valid for an extended period without actually doing anything.
!!!logout
!!!!Parameters:
*None
!!!!Response:
*//status// => boolean
Call this when you've finished using the API. After this call, your access key will no longer work, and you'll have to request a new one using login. If you don't call logout, your key will eventually expire anyway, but it helps us free up resources and thus keep smartmessages performance high for everyone.
!!Template functions
Note that all imported templates are subject to content filtering for security purposes. Generally this won't make any visible difference, but tags such as scripts and iframes will be removed, along with script-related attributes.
!!!gettemplates
!!!!Parameters:
*//includeglobal//: Boolean; whether to include the standard smartmessages-supplied templates. Defaults to false.
*//includeinherited//: Boolean; whether to include templates inherited from parent accounts. Defauts to true.
!!!!Response
*//status// => boolean
*//templates// => array
Returns an array of all templates that you have access to.
!!!addtemplate
!!!!Parameters
*//''name''//: The name of the new template, max 100 characters.
*//''html''//: The HTML part of the template.
*//''plain''//: The plain-text half of the template.
*//''subject''//: The default subject line, max 100 characters.
*//description//: A description of the new template, max 255 characters.
*//generateplain// Boolean; whether to create a plain text version from the html version. Defauts to false. The ''plain'' parameter is ignored if this is set.
*//language// The ~ISO-639-1 language that this template is in, defaults to 'en' for English.
*//importimages// Boolean; Whether to import referenced images and rewrite URLs to point at the new locations on our servers.
!!!!Response
*//status// => boolean
*//templateid// => integer
*//importcount// => integer (The number of images imported, if any)
Creates a new template. This should use an HTTP POST method in order to handle the large data size of templates. If images are set to import, they are placed in a folder based on the name of the template; any files with the same names will be overwritten. Images that fail to import are skipped and not reported, so it's a good idea to check the template afterwards. Image URLs with query strings are skipped (e.g. for third party tracking services).
!!!addtemplatefromurl
!!!!Parameters
*//''name''//: The name of the new template, max 100 characters.
*//''url''//: A URL of the web page you want to import.
*//''subject''//: The default subject line, max 100 characters.
*//description//: A description of the new template, max 255 characters.
*//importcount// Boolean; Whether to import referenced images and rewrite URLs to point at the new locations on our servers.
!!!!Response
*//status// => boolean
*//templateid// => integer
*//importedimages// => integer (The number of images imported, if any)
Creates a new template from a URL. This is useful if you develop or generate your templates on an external system. A plain-text version is created automatically. If images are set to import, they are placed in a folder based on the name of the template; any files with the same names will be overwritten. Images that fail to import are skipped and not reported, so it's a good idea to check the template afterwards. Image URLs with query strings are skipped (e.g. for third party tracking services).
If the URL fails to respond or returns an error, you'll get an {{{ERR_BAD_URL}}} error code. If you supply a malformed URL or one which doesn't use HTTP or HTTPS, you'll get an {{{ERR_INVALID_URL}}} error code.
!!!updatetemplate
!!!!Parameters
*//''templateid''// integer
*//''name''//: The new name of the template, max 100 characters.
*//''html''//: The HTML part of the template.
*//''plain''//: The plain-text half of the template.
*//''subject''//: The default subject line, max 100 characters.
*//description//: A description of the new template, max 255 characters.
*//generateplain// Boolean; whether to create a plain text version from the html version. Defauts to false. The ''plain'' parameter is ignored if this is set.
*//language// The ~ISO-639-1 language that this template is in, defaults to 'en' for English.
*//importimages// Boolean; Whether to import referenced images and rewrite URLs to point at the new locations on our servers.
!!!!Response
*//status// => boolean
*//templateid// => integer
*//importcount// => integer (The number of images imported, if any)
Creates a new template. Note you can only update templates that are in your account - not global or inherited ones. This should use an HTTP POST method in order to handle the large data size of templates. If images are set to import, they are placed in a folder based on the name of the template; any files with the same names will be overwritten. Images that fail to import are skipped and not reported, so it's a good idea to check the template afterwards. Image URLs with query strings are skipped (e.g. for third party tracking services).
!!!deletetemplate
!!!!Parameters
*//''templateid''// integer
!!!!Response
*//status// => boolean
Deletes a template. Note you can only delete templates that are in your account - not global or inherited ones.
''Warning: also deletes any mailshots that used this template''.
!!Campaign functions
Campaigns are groups of mailshots organised into folders - don't get them confused with mailshots.
!!!getcampaigns
!!!!Parameters:
*None
!!!!Response
*//status// => boolean
*//campaigns// => array
Returns a list of all campaign ids and names in your account.
!!!addcampaign
!!!!Parameters
*//''name''//: The name of the new campaign, max 100 characters.
!!!!Response
*//status// => boolean
*//campaignid// => integer
Creates a new campaign folder into which you can put new mailshots, or move existing mailshots.
!!!updatecampaign
!!!!Parameters
*//''campaignid''// => integer
*//''name''//: The new name for the campaign, max 100 characters.
!!!!Response
*//status// => boolean
Changes the name of an existing campaign. Note that campaign names don't have to be unique, though it's in your own interests to make them so!
!!!deletecampaign
!!!!Parameters
*//''campaignid''// => integer
!!!!Response
*//status// => boolean
Deletes a campaign.
''Warning: also deletes any mailshots contained in the campaign''.
!!!getcampaignmailshots
!!!!Parameters
*//''campaignid''// => integer
!!!!Response
*//status// => boolean
*//mailshots// => array
Returns an array of useful information about all the mailshots contained by a campaign.
!!Mailshot functions
See also getcampaignmailshots
!!!getmailshot
!!!!Parameters:
*//''mailshotid''//: The integer id of the mailshot you want
!!!!Response:
*//status// => boolean
*//mailshot// => array
**//name// => string
**//subject// => string
**//mailinglistid// => integer The id of the mailing list this mailshot is to
**//templateid// => integer The id of the template being sent
**//campaignid// => integer The id of the campaign this mailshot is stored in
**//status// => string 'unsent', 'complete', 'sending', 'error' etc
**//date_sent// => an ~ISO-8601 date in '~YYYY-MM-DD HH:MM:SS', or a zero date if it's unsent, of the time the mailshot was sent
**//from_address// => string The email address the message is sent from
**//from_name// => string The name the message is sent from
**//replyto// => string The email address replies go to, if different from the from address
**//date_completed// => an ~ISO-8601 date in '~YYYY-MM-DD HH:MM:SS', or a zero date if it's unsent or incomplete, of the time the mailshot completed
**//thumbnail_url// => string A URL of an image of the mailshot as sent (scaled to fit in a 320x320 pixel box)
**//preview_url// => string A URL of a web version of the mailshot as sent
**//recordcount// => integer number of subscribers in the uploaded list (not populated until upload complete)
**//messagecount// => integer Number of messages in this mailshot
**//opencount// => integer number of openings recorded
**//clickcount// => integer number of clicks recorded
Gets information about an existing mailshot.
!!!sendmailshot
!!!!Parameters
*//''templateid''//: The id of the template to send.
*//''listid''//: The id of the mailing list to send to.
*//title//: The title of the new mailshot, max 100 characters. If omitted, will generate a name based on the date.
*//''campaignid''//: The id of the campaign to put the mailshot in.
*//subject//: The subject template to use (allowed syntax is the same as for templates). Will use the template's default subject if omitted.
*//''fromaddr''//: The email address to use as the from address (should be in a domain you have set up DKIM and SPF for).
*//fromname//: The name to use as the sender.
*//replyto//: An address that replies should go to, if different to the from address.
*//when//: When to send the mailshot. Either the word 'now' (the default), or an ~ISO-8601 date in '~YYYY-MM-DD HH:MM:SS' format.
*//elements//: For future expansion, ignore for now.
!!!!Response
*//status// => boolean
*//mailshotid// => integer
Creates a new mailshot and schedules it for sending.
!!!getmailshotclicks
!!!!Parameters:
*//''mailshotid''//: The integer id of the mailshot you want
*//ascsv//: Boolean. Whether to return the result in CSV format
!!!!Response:
*//status// => boolean
*//clicks// => array
**//url// => string The link they clicked on
**//email// => string The address of the recipient that clicked
**//timestamp// => an ~ISO-8601 UTC date in '~YYYY-MM-DD HH:MM:SS'
Gets a list of clicks relating to a particular mailshot. If the {{{ascsv}}} parameter is supplied and true, results will be provided in CSV format, which is smaller, faster and easier to handle (just save it directly to a file) than other formats.
!!!getmailshotopens
!!!!Parameters:
*//''mailshotid''//: The integer id of the mailshot you want
*//ascsv//: Boolean. Whether to return the result in CSV format
!!!!Response:
*//status// => boolean
*//opens// => array
**//email// => string The address of the recipient that clicked
**//timestamp// => an ~ISO-8601 UTC date in '~YYYY-MM-DD HH:MM:SS'
Gets a list of opens relating to a particular mailshot. If the {{{ascsv}}} parameter is supplied and true, results will be provided in CSV format, which is smaller, faster and easier to handle (just save it directly to a file) than other formats.
//Note that we record a single extra open event for recipients that click if they do not have an open logged already. This allows us to avoid the nonsensical situation where someone has clicked by not opened, which could lead to unique clicks being larger than unique opens.//
!!!getmailshotunsubs
!!!!Parameters:
*//''mailshotid''//: The integer id of the mailshot you want
*//ascsv//: Boolean. Whether to return the result in CSV format
!!!!Response:
*//status// => boolean
*//unsubs// => array
**//email// => string The address of the recipient that clicked
Gets a list of unsubscribes relating to a particular mailshot. If the {{{ascsv}}} parameter is supplied and true, results will be provided in CSV format, which is smaller, faster and easier to handle (just save it directly to a file) than other formats.
!!!getmailshotbounces
!!!!Parameters:
*//''mailshotid''//: The integer id of the mailshot you want
*//''ascsv''//: Boolean. Whether to return the result in CSV format
!!!!Response:
*//status// => boolean
*//bounces// => array
**//email// => string The address of the recipient that clicked
**//code// => integer The bounce reason code
**//type// => string Hard, soft or other
**//reason// => string Text description of the bounce, for example 'Soft bounce - Mailbox full'
**//timestamp// => an ~ISO-8601 UTC date in '~YYYY-MM-DD HH:MM:SS'
Gets a list of bounces relating to a particular mailshot. This is the same information as is downloadable from the contacts page, except that this is for a single mailshot, not a whole list (which might cover multiple mailshots). If the {{{ascsv}}} parameter is supplied and true, results will be provided in CSV format, which is smaller, faster and easier to handle (just save it directly to a file) than other formats.
!!List functions
!!!addlist
!!!!Parameters
*//''name''//: The name of the new list, max 100 characters.
*//description//: A description of the new list, max 255 characters.
*//visible//: Boolean. Whether the new list should be publicly visible, defaults to true.
!!!!Response
*//status// => boolean
*//listidid// => integer
Creates a new mailing list, ready for you to upload subscribers into. List names do not have to be unique.
!!!updatelist
!!!!Parameters
*//''listid''// => integer
*//''name''//: The new name for the list, max 100 characters.
*//''description''//: The new description for the list, max 255 characters.
*//''visible''//: Boolean. Whether the list should be publicly visible
!!!!Response
*//status// => boolean
Changes the properties of an existing list. Note that all parameters are mandatory.
!!!deletelist
!!!!Parameters
*//''listidid''// => integer
!!!!Response
*//status// => boolean
Deletes a list.
''Warning: also deletes any mailshots that have used this list''.
!!!getlists
!!!!Parameters:
*//showall//: (optional) Whether to include all mailing lists, or just those set to 'visible' (the default). 1 for true, 0 for false.
!!!!Response (example data):
*//status// => boolean
*//mailinglists// => array
**341897 => array
***//id// => int 341897
***//name// => string 'Announcements'
***//description// => string 'Occasional information releases'
***//visible// => boolean true
**341886 = array
***//id// => int 341886
***//name// => string 'Newsletter'
***//description// => string 'Regular monthly news, hints & tips'
***//visible// => boolean true
This function returns a list of all the mailing lists that are available in your account that are set to be visible (editable on each mailing list page). The response contains the list ID, its name, and a description of each list, and the array of lists is also indexed by the list ID. Normally the list returned only includes lists that are set to visible, i.e. those that appear on the default landing page; If you add the 'showall' boolean parameter ad set it to 1, the response will include all lists, not just those that are marked as visible. The response includes a 'visible' property for each list which you can use to decide whether to display the list in your own pages.
!!!getlist
!!!!Parameters:
*//''listid''//: The integer id of the list you want
*//ascsv//: Boolean. Whether to return the result in CSV format
!!!!Response:
*//status// => boolean
*//list// => array
** Each entry is a large structure similar to what is returned by //''getuserinfo''//.
Gets a complete list of recipients on a mailing list. If the {{{ascsv}}} parameter is supplied and true, results will be provided in CSV format, which is smaller, faster and easier to handle (just save it directly to a file) than other formats.
''We //strongly// recommend that you use the {{{ascsv}}} option with this function as the response can be extremely large in PHP, JSON or XML formats, extending to hundreds of megabytes for large lists, taking a correspondingly long time to download, and possibly causing memory problems in client code. For this reason, this function defaults to CSV format.''
!!!getlistunsubs
!!!!Parameters
*//''listid''//: The id of the list you want to get unsubscibes from (obtained using the getlists function)
!!!!Response (example data):
*//status// => boolean
*//unsubscribes// => array
**string 'user1@example.com'
**string 'user2@example.com'
**string 'user3@example.com'
Provides a list of all addresses that have unsubscribed from the given list. Note that unsubscribes are retained even if the list is empty, and used to suppress unsubscribed addresses from subsequent uploads. We suggest you occasionally download unsubscribes in order to update your own copies of mailing lists (or make use of our callbacks in order to retain real-time sync).
!!List upload functions
!!!uploadlist
!!!!Parameters:
*//''listid''//: The integer id of the list you want to upload into (obtained using the getlists function)
*//''source''//: A short string describing where this list came from, for example if it's a bought list, the supplier name and order reference. This is used to retain audit trails of your subscribers.
*//definitive//: A boolean value indicating whether this upload is to be considered definitive data that should overwrite any existing data you have for each recipient for the included fields. See MailingLists for more info on the behaviour behind this. Defaults to false.
*//replace//: A boolean value indicating whether anyone already on this list that is not in this new upload should be removed. The default behaviour is to add to the list (i.e. false).
*//firstlinefields//: A boolean value indicating whether the first line of the file contains field names (note that these must match allowed field names). Defaults to false.
*//''file''//: The list file
!!!!Response:
*//status// => boolean
*//uploadid// => integer
This is a complex function used for uploading entire mailing lists in one go. It differs from the rest of our ~APIs in that it requires that you use an HTTP POST containing a multipart/form-data MIME body. Our reference PHP implementation does this for you and may provide a good basis for you to write code in other languages.
Currently the list file must be in standard [[RFC4180|http://www.rfc-editor.org/rfc/rfc4180.txt]] CSV format - comma delimited, optional double-quote delimiter escaping, double quotes escaped by themselves. This format can be exported from programs like Excel, Filemaker, Access, ~QuickBooks and numerous CRM packages.
The uploading process is asynchronous - this function will return when the physical file transfer is complete, but the actual import of the data takes longer, so you need to monitor its progress and do not send mailshots to the new list until it is complete. This function returns an integer identifier that can be used to query the status of an upload using the getuploadinfo call. You may only submit a single list file per request, and it can be compressed as a zip archive (in which case only the first file inside the archive will be used). There is a limit of 50Mb (uncompressed size).
In the absence of field names in the upload, the importer will fall back to whatever the field list is set to for the account, and if that is not set, to our default field order. The account field order can be set on the settings page, or though the API call 'setimportfields'.
!!!getuploads
!!!!Parameters:
*//''listid''//: The integer id of the list you want upload info for (obtained using the getlists function)
!!!!Response:
*//status// => boolean
*//uploads// => array
**0 => array
***//id// => integer the upload ID
***//status// => string 'pending', 'in progress', 'cancelled', 'complete', 'error' etc
***//progress// => integer (percentage)
***//cancelled// => boolean false (whether the upload has been cancelled)
***//filename// => string 'mylist.csv'
***//recordcount// => integer number of subscribers in the uploaded list (not populated until upload complete)
***//badcount// => integer number of invalid addresses in the uploaded list (not populated until upload complete)
***//groupcount// => integer number of addresses that look like group addresses in the uploaded list (not populated until upload complete)
***//uploaddate// => date and time (~ISO-8601 format) the upload completed (not populated until upload complete)
This function gets a history of all uploads, including recent ones. Typically you should call this function when monitoring an upload's progress in preference to the getuploadinfo function as it's faster and returns less information that's not relevant to the upload progress (even though it returns info on more than one upload).
!!!getuploadinfo
!!!!Parameters:
*//''listid''//: The integer id of the list you want upload info for (obtained using the getlists function)
*//''uploadid''//: The integer id of the particular upload you want info for (obtained using the getuploads or uploadlist functions)
!!!!Response:
*//status// => boolean
*//upload// => array
**//id// => integer The upload ID
**//status// => string 'pending', 'in progress', 'cancelled', 'complete', 'error' etc
**//progress// => integer (percentage)
**//cancelled// => boolean false (whether the upload has been cancelled)
**//filename// => string 'mylist.txt'
**//recordcount// => integer number of subscribers in the uploaded list (not populated until upload complete)
**//badcount// => integer number of invalid addresses in the uploaded list (not populated until upload complete)
**//groupcount// => integer number of addresses that look like group addresses in the uploaded list (not populated until upload complete)
**//uploaddate// => date and time (~ISO-8601 format) the upload completed (not populated until upload complete)
**//badaddresses// => array The array of invalid email addresses, if any.
*** 0 => 'invalid@example.'
**//groupaddresses// => array The array of group email addresses, if any.
*** 0 => 'sales@example.com'
*** 1 => 'customerservices@example.com'
**//definitive// => boolean true (whether the upload was uploaded in definitive mode - see the uploadlist function)
**//replace// => boolean true (whether the upload was uploaded in replace mode - see the uploadlist function)
**//firstlinefields// => boolean true (whether the upload had field names in its first line - see the uploadlist function)
**//contactcount// => integer number of entirely new contacts in this list (not populated until upload complete)
**//subcount// => integer number of new subscriptions in this list (not populated until upload complete)
**//suppressunsubcount// => integer number of unsubscribes suppressed from this upload (not populated until upload complete)
**//suppressspamcount// => integer number of spam reporters suppressed from this upload (not populated until upload complete)
**//suppressbouncecount// => integer number of known bouncing addresses suppressed from this upload (not populated until upload complete)
**//uploadbyid//: integer The ID of the user that did the upload - can be fed into the getuserinfo function
**//source//: A short string describing where this list came from, see uploadlist for details
!!!cancelupload
!!!!Parameters:
*//''listid''//: The integer id of the list the upload is in (obtained using the getlists function)
*//''uploadid''//: The integer id of the particular upload you want to cancel (obtained using the getuploads or uploadlist functions)
!!!!Response:
*//status// => boolean
If an upload has 'pending' or 'in progress' status, it can be cancelled, and none of the addresses on it will appear on the list and none of the field data changed by it will be applied.
!!Subscriber functions
!!!subscribe
!!!!Parameters
*//''address''//: The email address you want to subscribe
*//''listid''//: The id of the list you want to subscribe to (obtained using the getlists function)
*//name//: Optionally, the name you want to give to this subscriber. Goes into the 'Dear' field within smartmessages, usually used when addressing the recipient, e.g. 'John', 'Dr Who', 'Mr President', 'Gnasher' etc.
*//title//: Optionally, the title name you want to give to this subscriber.
*//firstname//: Optionally, the subscriber's first name.
*//lastname//: Optionally, the subscriber's last name (surname).
*//companyname//: Optionally, the subscriber's company name.
!!!!Response:
*//status// => boolean
*//msg// => string optional message
Doesn't need much explanation! If your subscription policy is confirmed or double opt-in (which it is by default), your recipients will receive a confirmation message which they may need to respond to before they receive messages from this list. If you subscribe someone who is already on the list, you will not get an error, but you will receive an additional //msg// property in the response saying so. The optional naming fields provide an easy way to add someone to a list and record their name in one go without having to call setuserinfo later, so it's ideal as something to work behind a signup form on your web site.
!!!unsubscribe
!!!!Parameters
*//''address''//: The email address you want to unsubscribe
*//''listid''//: The id of the list you want to unsubscribe from (obtained using the getlists or getuserinfo functions)
!!!!Response
*//status// => boolean
If you try to unsubscribe someone from a list that is not on it, you will receive an error response. Unsubscribes take effect immediately.
!!!deletesubscription
!!!!Parameters
*//''address''//: The email address you want to delete from a list
*//''listid''//: The id of the list you want to delete the subscriber from (obtained using the getlists or getuserinfo functions)
!!!!Response
*//status// => boolean
This works just like the unsubscribe call but without the associated semantics, so there will be no unsubscribe notification, no global unsubscribe action, no addition to suppressions etc, the subscriber is simply removed from the specified list with no other side-effects.
If you try to delete someone from a list that is not on it, you will receive an error response. Deletes take effect immediately.
!!!getuserinfo
!!!!Parameters
*//''address''//: The email address you want to get info on.
!!!!Response (example data):
*//status// => boolean true
*//userinfo// => array
**//email// => string 'user@example.com'
**//ownerdata// => string 'abc123'
**//title// => string 'Mr'
**//initials// => string 'FJ'
**//jobtitle// => string 'Marketing Director'
**//firstname// => string 'Finbar'
**//lastname// => string 'Jones'
**//dear// => string 'Mr Jones'
**//companyname// => string 'Example Marketing'
**//address1// => string 'Example House'
**//address2// => string 'Noodle Road'
**//address3// => string
**//county// => string
**//posttown// => string 'London'
**//postcode// => string '~SE1 5OK'
**//country// => string 'GB'
**//phone// => string '020 8123456'
**//fax// => string
**//mobile// => string
**//dob// => string '1971-05-27'
**//sex// => string 'm'
**//url// => string 'http://www.example.com/'
**//timezone// => string 'Europe/London'
**//custom1// => string 'custom value 1'
**//custom2// => string 'custom value 2'
**...
**//custom31// => string 'custom value 31'
**//custom32// => string 'custom value 32'
**//subscriptions// => array
***0 => array
****//listid// => int 341897
****//name// => string 'Announcements'
****//description// => string 'Occasional information releases'
****//visible// => boolean true
***1 => array
****//listid// => int 341886
****//name// => string 'Newsletter'
****//description// => string 'Regular monthly news, hints & tips'
****//visible// => boolean true
***2 => array
****//listid// => int 341889
****//name// => string 'Test list'
****//description// => string
****//visible// => boolean false
**//mailshots// => array
***0 => array
****//mailshotid// => int 33988
****//name// => string 'First test'
****//date_sent// => string '2007-07-06 14:20:00'
****//listid// => int 341889
****//previewurl// => string 'http://www.smartmessages.net/web/3939_33988_'
***1 => array
****//mailshotid// => int 33989
****//name// => string 'Example July Newsletter'
****//date_sent// => string '2007-07-06 15:20:24'
****//listid// => int 341886
****//previewurl// => string 'http://www.smartmessages.net/web/3939_33989_'
This returns lots of useful information about the recipient. You can see all their personal data, their current subscriptions (including those to invisible lists), and a list of all the messages they have been sent (along with preview ~URLs so you can see a message like the one they were sent). The list of personal data fields is subject to change, but we'll try to only add fields rather than take them away.
!!!setuserinfo
!!!!Parameters
*//''address''//: The email address you want to get info on.
*//''userinfo''//: An array of properties to set
!!!!Response:
*//status// => boolean
Provides a counterpart to the getuserinfo function for setting personal data, perhaps to sync with your CRM system, or provide storage back-end for a custom form. A user's properties have the same names as those returned from getuserinfo, but you need to wrap them in an HTML array style query string - there is a function in the [[example code|APIExamples]] to do that for you. Note that you CANNOT change a user's email address. If you need to change an email address, unsubscribe the current address and re-subscribe under the new address.
Some fields have input constraints:
*Only ~UTF-8 encoding is supported
*Most address fields are limited to 100 characters
*Phone numbers are limited to 20 characters
*Dates must be in ~ISO-8601 ~YYYY-MM-DD format
*Country requires a 2-character ~ISO-3166 code (Notably the UK is 'GB' in this scheme)
*Language requires a 2-character ~ISO-639-1 code
*Sex may be 'm', 'f', or 'unknown' (the default)
*Only custom fields are allowed to contain line breaks
*Custom fields are limited to 255 characters
Fields that are not set are not changed, fields that are set but empty will be cleared.
The meaning of custom fields is entirely up to you, and you are limited to 32 custom fields.
!!Account-level functions
!!!getspamreporters
!!!!Parameters
*None
!!!!Response (example data):
*//status// => boolean
*//spamreporters// => array
**string 'user1@example.com'
**string 'user2@example.com'
**string 'user3@example.com'
Provides a list of addresses that have reported emails from you as spam, at ~ISPs that we receive such reports from. Note that these are treated account-wide - users are unsubscribed from all your lists and prevented form being added to any list in future - it's just not worth trying to retain such recipients.
!!!getcallbackurl
!!!!Parameters:
*None
!!!!Response:
*//status// => boolean
*//url// => text
Retrieve the callback URL for your account.
!!!setcallbackurl
!!!!Parameters:
*//''url''//: text ~'http://www.example.com/callback.php' (put your callback URL here)
!!!!Response:
*//status// => boolean
Set the callback URL for your account. This must be a complete, valid URL using either HTTP or HTTPS protocol. Setting the URL to an empty string will disable callbacks; If callbacks have been disabled on your account (e.g. if your callback handler breaks), setting the URL to a valid, non-empty value (including to its existing value) will re-enable callbacks. If the URL you provide is invalid, the existing value will not be changed.
!!!getfieldorder
!!!!Parameters:
*None
!!!!Response (default fields shown)
*//status// => boolean
*//fields// => array
**0 => emailaddress
**1 => ownerdata
**2 => title
**3 => initials
**4 => jobtitle
**5 => firstname
**6 => lastname
**7 => dear
**8 => companyname
**9 => address1
**10 => address2
**11 => address3
**12 => posttown
**13 => county
**14 => postcode
**15 => country
**16 => phone
**17 => fax
**18 => mobile
**19 => dob
**20 => sex
**21 => custom1
**...
**32 => custom32
**33 => preferred_format
**34 => preferred_format_sms
**35 => preferred_language
**36 => url
**37 => crm_id
This function retrieves the current default import field order for your account. If you have not set a field import order, then you will get back the default order (as shown). The same functionality is available on the account settings page.
!!!setfieldorder
!!!!Parameters:
*//fields// => array (example shown)
**0 => emailaddress
**1 => firstname
**2 => lastname
!!!!Response
*//status// => boolean
Using this function you can set the default import order for subsequent uploads. This is particularly useful if you frequently upload lists with new subscriber data as you can set up smartmessages to match the export format of whatever database or spreadsheet you're using. You simply provide an array of the fields you want in the order you want. The fields you specify must be a subset of our default set (see the getfieldorder function for what they are); Fields cannot appear more than once; If the list you provide contains field names we don't know, the whole request will be ignored and will be rejected with a false status value. The field list ''must'' contain 'emailaddress' - a list is not any use without it! The same functionality is available on the account settings page.
!!Utility functions
!!!validateaddress
!!!!Parameters:
*//''address''//: string email address
!!!!Response:
*//status// => boolean
*//valid// => boolean
A utility function to assess whether an email address is valid. This is the same check as we use internally to spot bad addresses during uploads. Just submit an address and check the value of the 'valid' response property. [[Read more|Email Address Validation]] about address validation.
We provide a client wrapper class and sample code for our [[API]] in PHP which you can find [[on GitHub|https://github.com/Synchro/SmartmessagesClients]]. You will need to substitute your own login ID, password and API key in the example script. There's also a .NET library on there. If you write a wrapper for other languages, we'd love to make it available to other users.
If you have any ideas or improvements you'd like to see in our API, please [[create an issue|https://github.com/Synchro/SmartmessagesClients/issues]] on ~GitHub, or create a fork and submit a pull request.
!No sending limit
For most self-service accounts, there is no limit on the number of messages you can send to your recipients, and it won't cost you any more to email them more often.
!Contact limits
Where we do impose limits is on the number of contacts that you can hold in your account at any one time. This may be a larger number than are on your current lists because we need to hold data on addresses that have bounced, reported spam, unsubscribed or are on suppression lists. If you go over your account limit, we will not prevent you from uploading more, nor block subscriptions to your lists, but you will not be able to send messages to your lists until you have increased your subscription level to cover your contacts - see [[Payment]] for how to do that.
!!Removing unused contacts
If you're really concerned about the number on non-subscribed contacts you have (for example if you uploaded a new 10k list and then deleted it, you would be left with 10k non-subscribed contacts eating up your allowance), we can delete them for you, though we generally won't delete those that have asked to be unsubscribed or suppressed (as opposed to those you have deleted from lists) as it would make it impossible to fulfil our data protection obligations. If after this you're still over quota, you will have to [[upgrade your subscription|Payment]].
Under the ''Settings'' tab you can:
Change your password & manage your time zone
[IMG[images/personal.gif]]
Edit your company details:
[IMG[images/account.gif]]
Attach in Google Analytics:
[IMG[images/google.gif]]
Set up your domain authentication to get better delivery:
[IMG[images/domain.gif]]
Set up your field order so that uploads are in your own format:
[IMG[images/fieldorder.gif]]
Smartmessages does not have auto-responders as they are more appropriately handled by CRM systems. You can achieve some of the functionality that autoresponders are commonly used for using [[Continuous mailshots]].
There are several ways that you can automate interactions with smartmessages:
*Calling our standard [[subscribe|Subscribing]] and [[unsubscribe|Unsubscribes]] forms
*Using our [[API]]
*Using [[callbacks|Callbacks]]
*Using [[Continuous mailshots]]
Please read those sections for more details
A bounce or bounceback is a message received from a mail server describing a failed delivery attempt. A typical example would be if you sent jack@example.com an email, but there's nobody called jack at example.com, so the mail server at example.com sends a message back to a special address we put in each message describing why the message couldn't be delivered. We handle all the bounces that your mailings generate, analyse them, take necessary action, and provide reports on them.
Bounces are a thorny problem for any mailing list system. Mail servers are unpredictable and very often badly configured, and you're likely to interact with several thousand different ones on a reasonable size mailing.
People often talk of 'hard' and 'soft' bounces without really knowing what they mean. In fact, hard bounces are the only ones of any real interest, and even then they are not necessarily 'hard' at all - it's all much less black and white that you might have been led to believe. Generally a hard bounce is one that is negative and likely to be permanently true, for example a reply which says 'this user does not exist'. A soft bounce is pretty much anything else, even those that describe themselves as 'permanent', 'fatal' or other such strong terms - for example a user's mailbox being full is classed as a permanent error, even though they could empty it at any moment.
When we receive a bounce message, we try to interpret what it says as accurately as possible, but it's not easy. Though there are some common standards for such messages, adherence to the rules is pretty bad. Our mail servers use a bounce processing system based on [[BoogieTools BounceStudio|http://www.boogietools.com/]]. We return the same codes we receive from their handling of bounces in the downloadable bounce reports we provide. Here's an overview of the codes:
|!Bounce Type|!Description|
|0|NON BOUNCE|
|10|HARD BOUNCE|
|20|SOFT BOUNCE - General|
|21|SOFT BOUNCE - Dns Failure|
|22|SOFT BOUNCE - Mailbox Full|
|23|SOFT BOUNCE - Message Size Too Large|
|30|BOUNCE WITH NO EMAIL ADDRESS|
|40|GENERAL BOUNCE|
|50|MAIL BLOCK - General|
|51|MAIL BLOCK - Known Spammer|
|52|MAIL BLOCK - Spam Detected|
|53|MAIL BLOCK - Attachment Detected|
|54|MAIL BLOCK - Relay Denied|
|60|AUTO REPLY|
|70|TRANSIENT BOUNCE|
|80|SUBSCRIBE REQUEST|
|90|UNSUBSCRIBE REQUEST|
|100|CHALLENGE RESPONSE|
Most of these broad categories are easy enough to understand. A non-bounce is a message that's ended up at our bounce return address that isn't a bounce, for example if a recipient sent a reply to the bounce address instead of from or replyto addresses because their mail program was misconfigured, or because a spam generator guessed lucky and created a legitimate bounce address for a spam message. For full details on what these codes mean, [[read their documentation|http://www.boogietools.com/Products/Linux/BounceStudioAPI/help/files/BounceTypes.html]].
Bounces can take time - so just because your mailshot has all been sent and you have a nice low bounce count, don't be surprised when you find that it increases later on. We've had some mail servers send us bounces 6 months after sending!
We use [[VERP|http://en.wikipedia.org/wiki/Variable_envelope_return_path]] addressing for our return path (where bounces go), so we can cope with some server misconfigurations - for example, Microsoft Exchange sometimes sends bounces that don't actually say who the original message was sent to (helpful, huh?!), but because of our addressing system, we can still figure out who it was.
Sometimes we get a bounce that says 'Uh, something didn't work' with no indication of what or why it failed, and though we try, there's not a lot we can do about such messages - it may be that the user doesn't exist, or their mailbox is full, or their mail server is just rubbish (it happens!). Sometimes mail servers just go nuts - we once had a major ISP's mail server send us 30,000 bounce messages in reply to a single message! There's only so much we can do - and all mailing list providers are in the same boat here.
!!!Greylisting
Some mail servers use [[greylisting|http://en.wikipedia.org/wiki/Greylisting]] in an often successful attempt to fool the millions of zombie ~PCs responsible for most spam into not sending them messages. This involves initially rejecting an incoming message, but allowing it to be received later on. It relies on the idea that the minimal mail senders in such programs will never get around to trying again and don't care if some of their millions of messages are not delivered. Because we run well-behaved mail servers, we have full support for this system - but be aware that it can mean that messages take longer to be delivered.
!!Automatic unsubscribe of bounces
We automatically unsubscribe consistently bouncing addresses after approximately three bounces. It's approximate because we adapt according to the historical behaviour of each address, for example if a hard bounce happens in a sequence of otherwise successful deliveries to an address, it will be ignored and future deiveries won't be affected. There is a major exception to this: Yahoo! domains (and other domains they handle, such as btinternet.com) are unsubscribed after a single hard bounce; it's a requirement of their terms of service for deliveries, and repeatedly attempting deliveries to their domains just results in delivery penalties.
Because these bounces are not dependent on content or source, we maintain them globally, so that if an address is bouncing for one of our other customers, it will be suppressed from your lists when you upload them, which helps improve your deliverability by not sending to known-bad addresses - //even on your very first send//!
We auto-unsubscribe bounces that are reported as 'unknown user', 'unknown domain', 'mailbox full' or 'bad address syntax'. It's useful to remove 'mailbox full' addresses as these days mailboxes are rarely full for real users, so they are likely to be defunct accounts that are accumulating junk, especially if they give that error consistently over several delivery attempts.
It's possible to add branding options that we will use when presenting customer-facing pages such as subscribe and unsubscribe, and also some of our standard email templates. This takes the form of three editable boxes accessed via the branding tab, which you'll find under the account tab. The boxes allow you to supply HTML snippets for a header and footer that will be placed before and after the main page content, and a CSS style sheet that will allow you to override [[our standard style sheet|https://www.smartmessages.net/css/smartmessages.css]], so you can change fonts, set page width, set backgrounds etc. Here are simple examples of each:
!!Header
{{{
<div id="header">
<a href="http://www.smartmessages.net><img src="https://www.smartmessages.net/content/smartmessages/images/smheader.jpg" width="690" height="105" border="0" alt="Smartmessages header" /></a>
</div>
}}}
!!Footer
{{{
<div id="footer">Powered by Smartmessages</div>
}}}
!!Style Sheet
{{{
#mainbody {width: 670px;}
#footer {width: 682px;}
}}}
Note that if you refer to images that are not served over HTTPS (SSL), it's quite likely your users will receive security notices in Internet Explorer.
We use your branding on the following pages:
* Your landing page.
* Subscribe.
* Unsubscribe.
!Notification messages
We are often asked about branding of notifications that are sent when users subscribe and unsubscribe. These messages serve a specific technical role, and they absolutely have to remain working and contain some speciic text in order for us to conform to the necessary legal requirements - allowing them to be edited is extremely likely to compromise that role and thus make both you and us liable for the consequences, so we don't allow it. What we do do however is make use of your logo that you can upload in the settings area - read on for more. If you set a logo, our own branding is limited to a mention in the legal notices and a small footer - your own account name gets top-billing in the message alongside your logo, so there's little chance that your recipients will be confused by it.
!Account logo
We can make use of your own logo in numerous places around our web site and in some email messages. Create a logo image with the following criteria:
* Must fit into 256 pixels wide by 128 pixels tall.
* Images bigger than this will be scaled down to fit.
* Images smaller than this will ''not'' be scaled up.
* Be in PNG format (other formats will be converted into PNG, so you can upload other formats if you like).
* PNG transparency will be preserved (and is highly recommended).
* Must be readable on a light background - it's no use having white text on a transparent background as it will be invisible if we display it on a white page.
Here's an example; our own logo:
[IMG[http://www.smartmessages.net/logo/2]]
Your logo will be used in the following locations:
* In the header of all notification messages sent to your subscribers
* On the home page.
* On the home page of accounts that you refer to us.
* In any email templates that you place the {{{[[logo]]}}} tag in.
We may add to this list in future.
While we try to support as many different web browsers as possible, some make it very difficult, in particular Internet Explorer 6. If you use ~IE6, you may find our site slow or not particularly pretty in some places. Some browsers are over 80 times faster than ~IE6, so we highly recommend that you upgrade to a browser less than 7 years old, such as [[Internet Explorer 7|http://www.microsoft.com/windows/downloads/ie/getitnow.mspx]], [[IE8 beta |http://www.microsoft.com/windows/products/winfamily/ie/ie8/default.mspx]], [[FireFox 2.0 |http://www.mozilla-europe.org/en/products/firefox/]] or [[3.0|http://www.mozilla.com/en-US/firefox/all-beta.html]], or, for ultimate speed, standards compliance and quality, [[Opera|http://www.opera.com/]] or [[Safari|http://www.apple.com/safari/]].
As a simpler alternative to using our API, you may be well-served by our callback system. Instead of writing scripts to interrogate our system, we can tell you when something happens in your account, without you having to keep asking (polling). Callbacks are also known as web hooks, and you can [[read more about them here|http://wiki.webhooks.org/]].
On your account settings page, you can enable callbacks, provide us with the URL of a script on your site that should receive these callbacks, and select the events that you'd like to receive callbacks for. It's an efficient mechanism because there is only traffic generated when something happens, but at very busy times you may have a lot of requests coming your way!
Callbacks are ideal for posting events into CRM systems. For example when someone clicks on a link, it could put them into a follow-up contact queue for sales staff in your CRM.
!URL pattern
The callback URL should look like this:
{{{
http://www.example.com/callback.php
}}}
We also support https, username and password, e.g.
{{{
https://user:pass@www.example.com/callback.php
}}}
If you don't use password protection, you can improve the security of your script by giving it a less guessable name, for example:
{{{
http://www.example.com/callback924a8ceeac17f54d3be3f8cdf1c04eb2.php
}}}
The only real restrictions are that your URL should be valid and should not contain a query string (starting with ?) or anchor location/fragment part (starting with #), and not be in a private or inaccesssible network (e.g. {{{192.168.*}}} or {{{localhost}}}). Of course you don't have to implement your handler in PHP - you can use any language you like - and it's generally very easy to implement handlers for the straightforward requests we use.
!Security
It's in your interests to validate that the request you get looks like the documentation below describes before acting upon it so as to avoid security issues with malicious users attempting to inject bad data via your callback. For example, force mailshot ~IDs to be integers, ignore any text in those fields. Our sample script applies some simple validation. You can also firewall by our source ~IPs (though be aware that they are subject to change) which will be in the networks {{{93.93.128.0/24}}} and {{{93.93.135.0/24}}}.
!Events
These are the callback events that we can send you:
* subscribe
* unsubscribe
* open
* click
* bounce
* spamreport
* mailshotstarted
* mailshotcomplete
* listuploadcomplete
* listuploadfailed
Below you'll find descriptions of parameter names and values that will be supplied with each of these requests.
When you receive a request from us (which will be in the form of a normal [[HTTP POST|http://en.wikipedia.org/wiki/HTTP_POST]]), all we want in return is a normal {{{HTTP/1.0 200 OK}}} success response (which is the default in languages like PHP, so you don't have to do anything special). We will also accept {{{201 Created}}} or {{{202 Accepted}}} responses. We give you 15 seconds to handle the request, but if your callback URL fails to respond, returns an error or a success code we're not expecting, we will retry the callback after 30 seconds, 10 minutes, 1 hour, and 1 day, after which we'll disable your callback and email all your account admins saying we've done so. We don't log bad responses and we ignore response bodies. ''It's up to you to make sure your system is available and working!'' When you've fixed your callback you can re-enable it on your account settings page. While callbacks are disabled, we don't post any new events.
You can get and set your callback URL on your account settings page or via our {{{getcallbackurl}}} / {{{setcallbackurl}}} API functions.
!!Performance considerations
None of the CRM systems we've encountered are anywhere near fast enough to handle events at the full rate at which we can send events to you (hundreds per second); for example we found that salesforce.com takes up to two seconds to handle a single event. Some of our bigger customers receive peaks of 150 message openings per second alone, so please bear in mind these performance constraints when you are writing your script and selecting the events you want to receive as they will be coming thick and fast. A good way of dealing with 'peaky' traffic is to place the incoming callback requests into a queueing system (for example [[ZeroMQ|http://www.zeromq.org/]], [[beanstalkd|http://kr.github.com/beanstalkd/]] or [[Amazon's SQS|http://aws.amazon.com/sqs/]]) so you can process them at leisure rather than trying to handle them immediately; We use beanstalkd for our outbound queue.
We don't provide a 'send' callback event, but you can get a list of people that are on a mailing list used for a particular mailshot through our web interface or via our API; a more efficient batch operation instead of receiving send events.
!!Open
*//event//: The word 'open'.
*//address//: The email address that opened a message.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//mailshotid//: The Smartmessages ID of the mailshot the message was in (compatible with API functions).
*//mailshotname//: The name of the mailshot the message was in (as shown on your mailings page).
*//ip//: The IP address of the user that opened the message.
*//agent//: The HTTP user-agent string of the user that opened the message.
!!Click
*//event//: The word 'click'.
*//address//: The email address that clicked.
*//url//: The url they clicked.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//mailshotid//: The Smartmessages ID of the mailshot the link was in (compatible with API functions).
*//mailshotname//: The name of the mailshot the link was in (as shown on your mailings page).
*//ip//: The IP address of the user that clicked the link.
*//agent//: The HTTP user-agent string of the user that clicked the link.
!!Subscribe
*//event//: The word 'subscribe'.
*//address//: The email address that subscribed.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//listid//: The Smartmessages ID of the list they subscribed to (compatible with API functions).
*//listname//: The name of the mailing list they subscribed to (as shown on your contacts page).
*//ip//: The IP address of the user that requested the subscribe.
*//agent//: The HTTP user-agent string of the user that requested the subscribe.
You won't receive subscribe events until subscriptions are confirmed via double-opt-in.
!!Unsubscribe
*//event//: The word 'unsubscribe'.
*//address//: The email address that unsubscribed.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//listid//: The Smartmessages ID of the list they unsubscribed from (compatible with API functions).
*//listname//: The name of the mailing list they unsubscribed from (as shown on your contacts page).
*//ip//: The IP address of the user that requested the unsubscribe.
*//agent//: The HTTP user-agent string of the user that requested the unsubscribe.
!!Spam Report
*//event//: The word 'spamreport'.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//address//: The email address that reported a message as spam.
*//mailshotid//: The Smartmessages ID of the mailshot (compatible with API functions).
*//mailshotname//: The name of the mailshot (as shown on your mailings page).
We only receive these from a few major ~ISPs, however it's vitally important to the health of your lists that you remove them at source as well as in smartmessages (which happens automatically anyway).
!!Bounce
*//event//: The word 'bounce'.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//address//: The email address that bounced.
*//reasoncode//: Our internal bounce reason code (See [[Bounces]]).
*//severity//: The bounce severity (hard, soft, other).
*//reason//: A text description of the bounce reason.
We only emit bounce callbacks when an address has received sufficient bounces to warrant removing it from lists. The point at which this happens is quite involved. For some ~ISPs (e.g. Yahoo!), a single hard bounce is considered fatal, for others an address may not be considered dead until several have been received. For this reason we don't emit callbacks for every bounce, only ones that result in an address being removed from lists, so in general you won't receive callbacks for soft bounces. In some cases the specific bounce reason stated may not sound fatal (e.g. mailbox full), but it may be the last in a long sequence of similar failures that has led to address removal.
!!Mailshot Started
*//event//: The word 'mailshotstarted'.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//mailshotid//: The Smartmessages ID of the mailshot (compatible with API functions).
*//mailshotname//: The name of the mailshot (as shown on your mailings page).
!!Mailshot Complete
*//event//: The word 'mailshotcomplete'.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//mailshotid//: The Smartmessages ID of the mailshot (compatible with API functions).
*//mailshotname//: The name of the mailshot (as shown on your mailings page).
!!List Upload Complete
*//event//: The word 'listuploadcomplete'.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//listid//: The Smartmessages ID of the mailing list (compatible with API functions).
*//uploadid//: The Smartmessages ID of the upload (compatible with API functions).
*//listname//: The name of the mailing list (as shown on your contacts page).
!!List Upload Failed
*//event//: The word 'listuploadfailed'.
*//time//: The time this happened (~ISO8601 '{{{YYYY-MM-DD HH:MM:SS}}}' format, UTC time zone).
*//listid//: The Smartmessages ID of the mailing list (compatible with API functions).
*//uploadid//: The Smartmessages ID of the upload (compatible with API functions).
*//listname//: The name of the mailing list (as shown on your contacts page).
*//message//: A text message explaining why the upload failed, e.g. misnamed fields, corrupt file etc.
!Example code
Here is a short PHP script to accept a callback request, apply some validation and log the incoming request as a simple tab-delimited text file:
{{{
<?php
if (array_key_exists('ping', $_REQUEST)) {
echo "pong\n";
exit;
}
if (!array_key_exists('event', $_POST)) {
exit;
}
$record = '';
try {
switch($_POST['event']) {
case 'send':
if (array_keys_exist(array('address', 'time', 'listid', 'listname', 'mailshotid', 'mailshotname'), $_POST)) {
$record .= "send\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['listid']."\t".
substr(strip_tags($_POST['listname']), 0, 100);
(integer)$_POST['mailshotid']."\t".
substr(strip_tags($_POST['mailshotname']), 0, 100);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'subscribe':
if (array_keys_exist(array('address', 'address', 'time', 'listid', 'listname', 'ip', 'agent'), $_POST)) {
$record .= $_POST['event']."\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['listid']."\t".
substr(strip_tags($_POST['listname']), 0, 100)."\t".
substr(strip_tags($_POST['ip']), 0, 15)."\t".
substr(strip_tags($_POST['agent']), 0, 255);
}
break;
case 'unsubscribe':
if (array_keys_exist(array('address', 'address', 'time', 'listid', 'listname', 'ip', 'agent'), $_POST)) {
$record .= $_POST['event']."\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['listid']."\t".
substr(strip_tags($_POST['listname']), 0, 100)."\t".
substr(strip_tags($_POST['ip']), 0, 15)."\t".
substr(strip_tags($_POST['agent']), 0, 255);
}
break;
case 'click':
if (array_keys_exist(array('address', 'url', 'time', 'mailshotid', 'mailshotname', 'ip', 'agent'), $_POST)) {
$record .= "click\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(strip_tags($_POST['url']), 0, 255)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['mailshotid']."\t".
substr(strip_tags($_POST['mailshotname']), 0, 100)."\t".
substr(strip_tags($_POST['ip']), 0, 15)."\t".
substr(strip_tags($_POST['agent']), 0, 255);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'open':
if (array_keys_exist(array('address', 'time', 'mailshotid', 'mailshotname'), $_POST)) {
$record .= "open\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['mailshotid']."\t".
substr(strip_tags($_POST['mailshotname']), 0, 100)."\t".
substr(strip_tags($_POST['ip']), 0, 15)."\t".
substr(strip_tags($_POST['agent']), 0, 255);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'bounce':
if (array_keys_exist(array('address', 'time', 'severity', 'reasoncode', 'reason'), $_POST)) {
$record .= "bounce\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
substr(strip_tags($_POST['severity']), 0, 100)."\t".
(integer)$_POST['reasoncode']."\t".
substr(strip_tags($_POST['reason']), 0, 100);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'spamreport':
if (array_keys_exist(array('address', 'time'), $_POST)) {
$record .= "spamreport\t".
substr(strip_tags($_POST['address']), 0, 100)."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'mailshotstarted':
case 'mailshotcomplete':
if (array_keys_exist(array('time', 'mailshotid', 'mailshotname'), $_POST)) {
$record .= $_POST['event']."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['mailshotid']."\t".
substr(strip_tags($_POST['mailshotname']), 0, 100);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'listuploadfailed':
if (array_keys_exist(array('time', 'listid', 'listname', 'uploadid', 'message'), $_POST)) {
$record .= $_POST['event']."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['listid']."\t".
substr(strip_tags($_POST['listname']), 0, 100)."\t".
(integer)$_POST['uploadid']."\t".
substr(strip_tags($_POST['message']), 0, 100);
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
case 'listuploadcomplete':
if (array_keys_exist(array('time', 'listid', 'listname', 'uploadid'), $_POST)) {
$record .= $_POST['event']."\t".
substr(preg_replace('/[^ 0-9\:-]/', '', $_POST['time']), 0, 19)."\t".
(integer)$_POST['listid']."\t".
substr(strip_tags($_POST['listname']), 0, 100)."\t".
(integer)$_POST['uploadid'];
} else {
throw new Exception('Invalid '.$_POST['event'].': '.serialize($_POST));
}
break;
default:
//If we get here we've received an unexpected callback type
throw new Exception('Unknown callback type: '.serialize($_POST));
break;
}
} catch (Exception $e) {
$record = 'Invalid callback request received: '.$e->getMessage();
}
if (!empty($record)) {
file_put_contents('callback.txt', $record."\n", FILE_APPEND | LOCK_EX);
}
function array_keys_exist($keys, $array) {
if (!is_array($keys)) {
return array_key_exists($keys, $array);
} else {
foreach($keys as $key) {
if (!array_key_exists($key, $array)) {
return false;
}
}
}
return true; //didn't fail to find one, so must all be present
}
}}}
Please contact us if you have any suggestions, complaints or general comments.
[[support@smartmessages.net|mailto:support@smartmessages.net?subject=Support Request]]
If you're a recipient who wants to be removed from a list, use the unsubscribe link in the message you received. They are there for a reason and will work far faster and more efficiently than anything we can do manually.
Continuous mailshots are a kind of auto-responder. You create a continuous mailshot just list any other mailshot, but under the 'To' tab, select a mailing list, and then check the 'Continuous mailshot' checkbox. When you send a continuous mailshot, ''no messages are sent to the existing list''. When someone subscribes to the list, they ''will'' be sent the message as configured within the mailshot. This makes them ideal for sending a 'welcome' message to anyone that signs up for one of your mailing lists. You might normally send a monthly newsletter to your 'newsletter' mailing list, but you can set up a welcome message (using all the normal template features) to thank them for signing up. As their name suggests, continuous mailshots have no particular send time - new subscribers will be sent a message a few seconds after they subscribe. After they have been activated, you can pause/continue/stop continuous mailshots individually on the mailings page.
When you have a double-opt-in subscription policy, messages will only be sent after a double opt-in veerification loop has been completed.
List uploads are exempted from continuous mailshots, so after uploading a list into a list that's being used by an active continuous mailshot, the new subscribers will ''not'' be sent messages.
We offer a range of customised projects, using smartmessages at the center, and then coding in custom forms, with a complete business process from start to finish.
[[DomainKeys|http://en.wikipedia.org/wiki/Domainkeys]] and [[DKIM|http://en.wikipedia.org/wiki/DomainKeys_Identified_Mail]] (~DomainKeys Identified Mail) are anti-forgery technologies that help prevent phishing and spam, and as such increase the trust in messages that use them.
DKIM is a step beyond what [[SPF and SenderID|SPF]] can offer for forgery prevention, at the expense of some complexity. While SPF attempts to ensure that the //origin// of the message is not forged, DKIM ensures that the //content// of a message is not forged or altered, thus the two technologies complement each other nicely; DKIM is not in any way a replacement for SPF. For ultimate trust in your email, you should use both.
DKIM is a replacement for the older and less-capable ~DomainKeys, and we only implement DKIM in Smartmessages. Like SPF, it requires that you create records in your DNS server - how you achieve that will vary widely, but if you own your own domain, you should be able to access that through your registrar or DNS hosting service (incidentally, we highly recommend [[Gandi.net|http://www.gandi.net/]] for domain registration and DNS hosting).
!Why?
What follows may seem like a lot of effort, and all too complicated, so why do it? Well, since you're here, you're obviously interested in getting email in front of your audience, in their inbox, not their spam folder. DKIM can help this - although there is nothing preventing spammers from using DKIM (and some do), it means that the receiver knows that what you sent is what you meant to send, and that you are not lying about who you are (courtesy of SPF). That helps them to trust the messages they receive from you, which can, when coupled with a consistent reputation of not sending messages that are reported as spam, result in more reliable inbox placement. Some email hosts display a little icon to the user indicating that a message is from a trusted source, further enhancing trust in your subscribers.
There's another key reason: Yahoo!, one of the inventors and biggest supporters of DKIM, requires that we use DKIM if we want to receive [[spam reports|SpamReports]] about messages we send for you. ''This is the single biggest driver for improving your sending reputation, deliverability and list quality!'' Mail hosts that are known to pay significant attention to DKIM signed email include Yahoo! and Google, and more are adding it every day. It's rapidly becoming the case that ''if you don't use DKIM, you can pretty much forget about being able to deliver volume to any Yahoo! domains'', which includes some other large UK ~ISPs such as btinternet.com and talk21.com. Convinced? Read on...
!!Which domain?
When you send messages through smartmessages, you have the opportunity to enter a 'from' address, such as 'newsletter@example.com'. The DKIM records need to match the domain part (the bit after the '@', in this case 'example.com') of your from address. If you use several domains, you can create the same DNS records in each of them, and register them all in smartmessages, and we will use the right one automatically. Note that you need to have control of the DNS for the domain in question.
!!What do I need to do?
If you don't already have DKIM in your DNS, you will need to create two records, firstly a TXT record called {{{_domainkey}}} (that's an underscore character on the front of it) that contains what's called a DKIM policy. This is similar to the role of the 'all' part of an SPF record, indicating whether your DKIM keys are used for all or some of your email (and thus whether unsigned messages should be rejected or not). We suggest you start with the 'relaxed' option, which is simply:
{{{
o=~
}}}
(That's a lower case letter o, an equals sign and a tilde character). The second record you create needs to use our DKIM selector, which is {{{sm}}} for smartmessages. Selectors allows different mail sources to sign messages independently, for example by different branch offices, or (heaven forbid!) other mailing list management providers, each using their own selector.
Create another TXT record and name it {{{sm._domainkey}}}. The value of the record should be set to this:
{{{
k=rsa; p=MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxALRHPRnFUks4Hntg7AidEbw3zZGWhVXAy4Gm6TiP1Ln96IwGV1Knde4bJClKTvsGTQIDAQAB
}}}
If your DNS software complains about this value, try changing the beginning of it to {{{k=rsa\;}}}, leaving the rest unchanged.
!!How do I know it's working?
Once you've set it up, you can check it (and everything else in your DNS) is working using a service like dnsreport. If you have a Unix/Linux/~MacOS X command line handy, you can try {{{dig txt _domainkey.example.com}}} and {{{dig txt sm._domainkey.example.com}}} to check that your DNS is publishing your records correctly. Alternatively use [[this tool|http://domainkeys.sourceforge.net/policycheck.html]] for testing your policy, [[this one |http://dkimcore.org/tools/dkimrecordcheck.html]] for your selectors.
Once you've set up your DNS, you need to enter the domains you send from on the settings page of your smartmessages account. When you do so, it will check SPF and DKIM entries and confirm that they are OK. When you send correctly ~DKIM-signed messages to Yahoo! addresses recipients will see a small key-shaped icon next to your from address, indicating that it's validated.
!!!Here's one I made earlier
A complete set of DKIM and SPF DNS records for working with smartmessages will look something like this (exactly how it is presented will depend on your provider):
{{{
example.com. 10800 IN TXT "v=spf1 a mx include:smartmessages.net ~all"
_domainkey.example.com. 10800 IN TXT "o=~"
sm._domainkey.example.com. 10800 IN TXT "k=rsa\; p=MEwwDQYJKoZIhvcNAQEBBQADOwAwOAIxALRHPRnFUks4Hntg7AidEbw3zZGWhVXAy4Gm6TiP1Ln96IwGV1Knde4bJClKTvsGTQIDAQAB"
}}}
!!How do I register domains with smartmessages?
Once you've set up your DNS, log into your smartmessages account and go to the 'settings' tab in the 'account' page. At the bottom you will see an 'authenticated domains' section. Add your domain there and it will automatically check that your DNS contains the correct values. If you get some little red crosses instead of green ticks, double-check your DNS entries according to this guide, and you can use the 'recheck' button to try again. If you still have trouble, [[contact us|http://www.synchromedia.co.uk/about-us/contact-us/]]. Note that DNS changes can take a while to refresh (though additions are instant), so it may not give you the all-clear for several hours if you don't get it right first time.
!!How does it work?
DKIM uses [[public-key cryptography|http://en.wikipedia.org/wiki/Public_key_cryptography]], a technique which uses mathematically related public and private keys to encrypt and decrypt data. Our public key (All that random-looking text in the DNS record) is placed in //your// DNS so that when an email is received (which contains an encrypted signature created using our private key), the email server can do a DNS lookup using our {{{sm}}} selector and use the public key it finds there to verify that the email is legitimate and hasn’t been tampered with in transit. The public key needs to be in //your// DNS in order to prove that //we// are not just making it up!
!!What if I don't set up DKIM?
You don't have to use DKIM; if you don't, we will still sign the message as an intermediary, which doesn't carry nearly as much weight as your own signature. It's rapidly becoming the case that ''if you don't use DKIM, you can pretty much forget about being able to deliver volume to any Yahoo! domains'', which includes some other large UK ~ISPs such as btinternet.com and talk21.com.
This happens under the contacts tabs. All files can be opened by Excel (CVS).
[IMG[images/contact.gif]]
Choose ''the manage'' button: [IMG[images/manage2.gif]]
Scroll down the page to Mailing list actions, click the ''download list'' button:
[IMG[images/upload.gif]]
Email addresses are surprisingly complex and tricky things, and there are many addresses that you might not think are valid that are, and vice versa. For example, this is a valid email address: {{{!#$%&'*+-/=?^_`{}|~@example.org}}}, but this is not: {{{joe_user@example_site.org}}}. Many web sites don't understand this and reject some perfectly valid addresses, much to the annoyance of their users. This is especially common for the '+' character which is used when creating extremely useful 'disposable' addresses such as {{{user+something@example.com}}}, something that is supported on Gmail, Yahoo! and Hotmail/Live. You can name and shame sites that don't know what email addresses look like [[here|http://youdontknowemail.tumblr.com]].
That said, many strictly valid email addresses are unlikely to be correct, so it's common to only allow a subset, but any validation should //never// allow something that is really invalid, no matter what subset is chosen (for example it's always invalid for a domain to contain an underscore).
It's important to note that an address that's valid doesn't necessarily exist (i.e. it may still bounce if you email it), though an invalid address is guaranteed not to exist, so it's not worth sending to in the first place.
The applicable standards for email addresses are the original [[RFC822|http://tools.ietf.org/html/rfc822]] and its updates from ~RFCs [[2822|http://tools.ietf.org/html/rfc2822]] and [[5322|http://tools.ietf.org/html/rfc5322]], and related definitions in [[RFC821|http://tools.ietf.org/html/rfc821]], [[2821|http://tools.ietf.org/html/rfc2821]], [[5321|http://tools.ietf.org/html/rfc5321]] and [[1035|http://tools.ietf.org/html/rfc1035]]. ~RFC822 on its own isn't very useful for validation because it's missing updates and contains conflicts with these other standards.
!!Validation in Smartmessages
We avoid problems with invalid addresses by simply not allowing them into our system. They will be rejected in subscription requests, ignored (but reported to you) in list uploads etc. Smartmessages uses the definition of an email address from the [[official HTML5 specification of an email address|http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)]], which we wrote.
!!Validating in your own code
You can use the {{{validateaddress}}} function in [[our API|API]] to check against the same validation we use internally, but we don't recommend that for high volumes as it's much more efficient to check addresses locally.
The most common way to validate in your own code is to use regular expressions, which is supported in almost all programming languages in some form. You'll find huge numbers of regular expressions for validating email if you search for them, but many are wrong, or at least wrong enough to be likely to annoy valid address holders or to allow invalid addresses. There are several validation expressions we recommend:
* [[Ex-parrot's RFC822 validator|http://www.ex-parrot.com/pdw/Mail-RFC822-Address.html]]. This is a complete ~RFC822 expression in perl, and shows just how horribly complicated a correct expression can be.
* [[Michael Rushton's PHP regex|http://squiloople.com/2009/12/20/email-address-validation/]]. This is also the expression used in [[PHPMailer|https://github.com/PHPMailer/PHPMailer]].
* [[PHP's built-in filter_var function|http://www.php.net/manual/en/function.filter-var.php]] with the {{{FILTER_VALIDATE_EMAIL}}} flag set. This is built-in to PHP 5.2 and later. It uses Michael Rushton's expression, with one change to not allow 'dotless' domains like 'a@b', which are not permitted by ~ICANN even though they are valid in ~RFC822.
* [[HTML5's email definition|http://www.whatwg.org/specs/web-apps/current-work/#e-mail-state-(type=email)]], which we wrote, attempts to provide a sensible compromise between allowing valid addresses and rejecting likely bad ones. This is what we are currently using within Smartmessages.
Several features are being considered for future releases:
* Automatic pre-send [[SPF]] checks and enforcement
* Dynamic list segmentation
* Always keeping an eye on developments in ~JavaScript WYSIWYG text editors to improve mailshot editing.
* Multi-language support in the customer interface
* Brandable (white-labelling) user interface for agencies (it's already brandable for your subscribers)
* New UI with nested campaign data
[[Google Analytics|http://www.google.com/analytics/]] is a quick and easy way to add tracking for end-to-end marketing feedback. While we track all that we can (opens and clicks), we have no way of knowing how that helps your bottom line. How would you like to be able to see how many people from your mailshots actually bought something from your web site? That's the kind of thing that Google Analytics can tell you.
!How it works
When we handle a clickthrough for you (see [[Link Tracking]]), we check if the link is pointing at one of your analytics domains, we add some additional parameters to the clicked URL to let analytics know where the link came from before telling the user's browser where to go. For example, a link to //http://www.example.com/// might be extended to //https://www.example.com/?utm_source=smartmessages.net&utm_medium=email&utm_campaign=My mailshot&utm_content=12345// that is, we add in information to the link about which mailshot the click is associated with. When the browser gets to the destination site, the Google Analytics javascript spots the additional parameters on the incoming URL and adds it to the information that it logs. It then means that all activity that this particular user does can be linked to the fact that they came from the mailshot.
!How to set it up
There are two parts to setting up analytics in smartmessages. Firstly you need to go to your smartmessages accounts settings page, click the 'enable analytics' checkbox, enter the domains that you want to use analytics for (for example 'www.example.com') and then save your settings. After that, all redirected links in that domain will get the additional parameters added. Domains not in your list will not get the additional parameters. The campaign property comes from the name you give your mailshot when you send it, and the content value is our internal ID for the mailshot, which you may find useful if you use our [[API]] (or to help differentiate mailshots that have been given the same name).
The second step is to install Google Analytics on your web site. That's highly dependent on how your site is set up, but [[Google tells you how to do it|http://www.google.com/analytics/discover_analytics.html]]. Once it's done, you should find that you start to see (on the analytics site, not in smartmessages) a proportion of your visitors as coming from email links, referred by us, ripe for further analysis. It's not a real-time service, so you'll find that there is some delay before stats start coming through.
Gravatars are personalisation images provided through [[gravatar.com|http://www.gravatar.com]], which are commonly used by users of wordpress.com, and many internet forums. This service associates an icon with an email address, and can either be set by the user, or generated algorithmically, so every address gets a unique icon. Here's an example of a custom icon set for info@smartmessages.net:
[<IMG[http://www.gravatar.com/avatar/cb4119cab961918d4d1a0488878042b2.png?s=32]]
and here's a generated one for an address that has no predefined icon:
[<IMG[https://secure.gravatar.com/avatar/f211411290589aea573c916329ec8691?r=pg&d=identicon&s=32]]
We use gravatars in our admin interface, but you can also use them in email templates with the {{{[[gravatar16]]}}}, {{{[[gravatar32]]}}} and {{{[[gravatar64]]}}} template tags which render icons of 16x16, 32x32 or 64x64 icons respectively for the recipient's email address.
var ImportWizard, WizardMaker;
(function($) {
window.WizardMaker = function(place, wizard) {
var steps = wizard[0];
var options = wizard[1] || {};
$("<h1 />").text(options.heading || "Wizard").appendTo(place);
var wizard = this;
$('<button class="button">restart wizard</button>').click(function(ev) {
wizard.jumpTo(0);
}).appendTo(place)[0];
this.currentStep = 0;
this.body = $('<div class="wizardBody"/>').appendTo(place)[0];
this.steps = steps;
this.values = {};
this.createStep(0);
};
WizardMaker.prototype = {
/*
OPTIONS
step: [function, options]
*/
createStep: function(stepNumber) {
$(this.body).empty();
var step = this.steps[stepNumber];
if(!step) {
throw "invalid step (" + stepNumber + ")"
}
var options = step[1] || {};
var humanStep = stepNumber + 1;
var heading = "Step " + humanStep;
if(options.heading) {
heading += ": " + options.heading;
}
$("<h2 />").text(heading).appendTo(this.body);
var container = $('<div class="wizardStep" />').appendTo(this.body)[0];
step[0](container, this);
},
next: function() {
if(this.currentStep < this.steps.length - 1) {
this.currentStep += 1;
}
this.createStep(this.currentStep);
},
jumpTo: function(step) {
this.currentStep = step;
this.createStep(step);
},
setValue: function(name, val) {
this.values[name] = val;
},
getValue: function(name) {
return this.values[name];
}
};
if(window.FileReader) {
window.ImportWizard = function(options) {
var proxy = options.proxy, saveFunction = options.save,
internalizeTiddler = options.internalizeTiddler, proxyType = options.proxyType || "GET";
return [
[
[function(body, wizard) {
$(body).html('Where do you want to import from? <select><option value="1">file</option><option value="2">the web</option></select><button class="button">ok</button>');
$("button", body).click(function(ev) {
var opt = $("select", body).val();
if(opt === "1") {
wizard.next();
} else {
wizard.jumpTo(2);
}
});
},
{ heading: "File or Web?" }],
[function(body, wizard) {
$(body).html('Browse for a file: <input type="file" size="50" name="txtBrowse"><br><hr><div class="wizardFooter"><div class="message"></div></div>');
function handleFileSelect(evt) {
reader = new FileReader();
reader.onerror = function(e, msg) {
alert("Error occurred")
};
reader.onabort = function(e) {
alert('File read cancelled');
};
reader.onload = function(e) {
var html = reader.result;
wizard.setValue("html", html);
wizard.jumpTo(3)
}
// Read in the image file as a binary string.
window.reader = reader;
reader.readAsText(evt.target.files[0]);
}
$("[type=file]", body)[0].addEventListener('change', handleFileSelect, false);
}, { heading: "Locate TiddlyWiki file" }],
[function(body, wizard) {
$(body).html('Enter the URL or pathname here: <div class="message"></div><input type="text" size="50" name="txtPath"><button class="button">open</button>');
$("button", body).click(function(ev) {
var url = proxy.replace("%0", $("input", body).val())
ajaxReq({
type: options.proxyType,
url: url,
success: function(html) {
wizard.setValue("html", html);
wizard.jumpTo(3);
},
error: function() {
$(".message").html("There is something wrong with that url please try another.");
$("input", body).addClass("error");
}
})
})
},
{ heading: "Import from Web" }],
[function(body, wizard) {
var html = wizard.getValue("html");
var doc = $(html);
var store;
$(html).each(function(i, el) {
if(el.id === "storeArea") {
store = el;
}
});
if(store) {
var tiddlers = [];
$(store).children().each(function(i, el) {
var title = $(el).attr("title");
tiddlers.push(internalizeTiddler(el));
});
$("<div />").text("Choose tiddlers that you wish to import");
var table = $("<table />").appendTo(body)[0];
$("<tr />").html('<th><input type="checkbox" checked/></th><th>title</th>').
appendTo(table)
$("input", table).change(function(ev) {
var checked = $(ev.target).is(':checked');
$("input[type=checkbox]", body).attr("checked", checked);
});
for(var i = 0; i < tiddlers.length; i++) {
var title = tiddlers[i].title;
var row = $("<tr />").data("tiddler", tiddlers[i]).appendTo(table)[0];
$("<td />").html('<input type="checkbox" checked="checked"/>').appendTo(row);
$("<td />").text(title).appendTo(row);
}
$('<button class="button">import all selected tiddlers</button>').click(function(ev) {
var tids = [];
$("input[type=checkbox]:checked").each(function(i, chk) {
var tiddler = $(chk).parents("tr").data("tiddler");
if(tiddler) {
tids.push(tiddler);
}
});
wizard.setValue("selected", tids);
wizard.jumpTo(4)
}).prependTo(body);
}
},
{ heading: "Choose tiddlers" }],
[function(body, wizard) {
var tids = wizard.getValue("selected");
$(body).text("Please wait");
// do import
var save = 0;
var complete = function() {
save += 1;
if(save === tids.length) {
wizard.jumpTo(5);
}
};
$(body).text("Please wait (Importing " + tids.length + " tiddlers)");
for(var i = 0; i < tids.length; i++) {
var tid = tids[i];
$(body).text("Please wait (Importing " + tid.title + ")");
saveFunction(tid, complete);
}
},
{ heading: "Importing" }],
[function(body, wizard) {
$(body).html("Good news! Everything is now imported.");
},
{ heading: "Finished!" }]
],
{
heading: "Import tiddlers from another file or server"
}
];
}
} else {
$("#container").addClass("error").text("Your browser is not modern enough to support this app.");
}
})(jQuery);
(function($) {
if(window.ImportWizard) {
var proxy = "%0", proxyType = "GET";
if(config.extensions.tiddlyspace) {
proxy = "/reflector?uri=%0";
proxyType: "POST";
}
var loader = new TW21Loader();
var internalizer = function(node) {
var title = $(node).attr("title");
var tiddler = new Tiddler(title);
loader.internalizeTiddler(store, tiddler, title, node);
return tiddler;
};
var importer = ImportWizard({proxy:"%0", save: function(tid, callback) {
merge(tid.fields, config.defaultCustomFields);
delete tid.fields["server.page.revision"];
delete tid.fields["server.etag"];
tid = store.saveTiddler(tid.title, tid.title, tid.text,
tid.modifier, tid.modified, tid.tags, tid.fields, null, tid.created, tid.creator);
autoSaveChanges(null, [tid]);
callback();
}, internalizeTiddler: internalizer, proxyType: proxyType });
config.macros.importTiddlers = {
handler: function(place) {
var container = $("<div />").appendTo(place)[0];
new WizardMaker(container, importer);
}
};
} else if(config.macros.importTiddlers) {
var _import = config.macros.importTiddlers.handler;
config.macros.importTiddlers.handler = function(place) {
_import.apply(this, arguments);
jQuery("<div class='annotation error' />").text("Please upgrade your browser to take advantage of the modernised file import mechanism of the TiddlyFileImportr plugin.").prependTo(place);
};
}
})(jQuery);
By default we track all links within a template - you don't have to do anything at all. We take your ~URLs and re-encode them so that they come via our web server using something called a redirect. This allows to track exactly who has clicked which links in which messages, but also makes your ~URLs longer.
Some email programs do not like ~URLs over 255 chars, so it's a good idea to keep your ~URLs as short as possible. For various reasons (mainly a long-standing bug in apache), we base-64 encode the ~URLs for our redirector, which adds about 37% to original URL length. We then add the unique identifier (about 32 chars) that allows us to track the message that the click happened in and add it all to the URL for the redirector. The ~URLs end up like this:
{{{https://www.smartmessages.net/v/<message identifier>_<original URL + 37%>}}}
Our overhead is thus about 64 chars, so you should be able to safely use ~URLs up to about 138 chars. That said, ~URLs longer than this will still work in many clients. We truncate ~URLs at 2048 characters before encoding - if you need them longer than that, you're probably doing something wrong!
When a message is generated for sending, we enforce [[RFC2396|http://www.faqs.org/rfcs/rfc2396.html]] section 2.4.3 compliance (the standard for URL syntax) in link ~URLs, stripping any characters that are not allowed. These are:
* All control characters (line breaks, tabs etc)
* Spaces
* Reserved delimiters: |, {, }, \, <, >, ^, ' and ".
Links in plain text messages are another matter - many plain text clients will break ~URLs over 1 line long, which is often only 72 chars. For that reason ''we don't add tracking to plain text ~URLs'' as it's more important that people get to where the link is going than that we know they have done so.
We don't track links to our unsubscribe pages (they're just not very interesting and you you can get the unsubscribe data elsewhere). If you want to exclude a link from being tracked, just add a 'nosmtracking' keyword to the link, like this:
{{{
<a href="http://www.example.com/secretclicks.php" nosmtracking>I'm not tracked!</a>
}}}
The keyword will be removed during the send process, so your recipients will never see it, the link will not go via our redirector, and it will not appear on any reports.
If you want to add further sophistication to your link tracking (for example correlating mailshot activity with completed sales), take a look at GoogleAnalytics.
See [[open tracking|Open Tracking]] for information on tracking message openings.
!!Phishing warning
If your link points at a destination likely to be the target of phishing campaigns, such as Facebook or Paypal, you need to be careful not to use link text that looks like a URL as it may trigger anti-phishing defences. In those cases, either use the above feature to disable tracking on the URL, change the link text, or make the link go via another redirector/shortener such as [[bit.ly|https://bitly.com/]] or [[goo.gl|http://goo.gl/]].
Unsurprisingly, a mailing list is a list of recipients you wish to send email to. Smartmessages doesn't stop at just the lists themselves, but also allows you to gather data about your recipients, so your customers are stored once, and their subscriptions to multiple lists are stored separately. This means that if you know that fred@example.com actually likes to be called John, that fact will be available across all of your lists, not just one of them - your customer is your customer, not several different flavours of that customer. Most mailing list management services don't offer this independence, and suffer from fragmented and incomplete data as a result.
When you upload a mailing list, it is first filtered according to any active [[suppressions|SuppressionLists]] that may apply to it.
At present we don't have dynamic list segmentation (e.g. mail the subset of a list that responded the last time you mailed them), but it's [[something we're working on|FutureFeatures]].
[[Start here]]
[[AccountSettings]]
[[UploadLists]]
[[DownloadLists]]
[[Templates]]
[[SendingMailings]]
[[Reporting]]
[[RepeatMailings]]
[[Payment]]
[[API]]
[[ContactUs]]
All content Copyright © 2012 [[Synchromedia Limited|http://www.synchromedia.co.uk/]]
If you want to track the opening of your messages, you need to insert what's called a "web bug" or "beacon" image. This delivers a transparent 1-pixel GIF image, but also tells us exactly which message has been opened and when. When using our standard templates, you don't need to do anything, but when using your own you have a choice of two macros. Somewhere in your template, insert the {{{[[tracking_image]]}}} tag from the pop-up menu above the editor into your template, somewhere inside the body tag of your html, commonly at the bottom. You'll end up with something like this:
{{{
[[tracking_image]]
</body>
</html>
}}}
If you don't like the image tag that we generate for you, you can create your own using the {{{[[tracking_url]]}}} tag instead:
{{{
<img src="[[tracking_url]]" width="1" height="1" alt="*">
</body>
</html>
}}}
Either way, make sure that your HTML editor doesn't rewrite the tags with URL encoding as that will break the links.
!Oh no! I forgot to put a tracker in!
If you don't put an opening tracker in, the result will typically be that you get no opens shown on the reporting page. However, there is another factor that comes into play here: if we receive a clickthrough from a message that we do not have an opening logged for, we will log an open (just one) as well, as it's not possible for a click to happen from a message that was never opened - even with images turned off. If you have done this, the usual symptom is that number of reported clicks and opens will typically be very similar, whereas you would normally expect opens to be much higher than clicks.
Note that we do not attempt to support tracking opening of plain-text templates, because obviously they don't support images. Some mailing companies claim that it's possible to do this - it's not; they're lying!
See [[link tracking|Link Tracking]] for information on tracking clickthroughs.
There are two ways of paying for Smartmessages - Self-service or contract. Most of our customers take the self-service route which allows you to control your account entirely yourself. Those with more complex needs, large lists (> 100,000) or those who prefer alternative payment arrangements (such as monthly/quarterly invoice) should [[contact us|ContactUs]].
When choosing your subscription level, you should bear in mind [[what the account limits mean|Account limits and quotas]].
Click on the "Account" tab. [IMG[images/account1.gif]]
Click on the "Buy smartmessages services" tab [IMG[images/smtab1.gif]]
Choose the service you require, click the ~PayPal button next to the service you want. You'll be taken to ~PayPal. Either log into your account, or use the credit card payment service they offer.
Generally Smartmessages uses ~PayPal's subscription services, so your payments will happen automatically every month without you having to do anything.
Phishing attempts to trick you into revealing your personal and financial details, often by making web sites that pretend to be sites such as eBay, Paypal and banks, but it can occur for any site that typically asks for your details.
Smartmessages includes support for anti-phishing measures. We provide an opportunity for our customers to include a link back to us, and we use that link wherever we ask for your details, so you can check that we are legitimately acting for a customer, and not attempting to obtain your data for nefarious purposes.
To be safe from phishing, you should check that email messages really come from where they say they do, and that any domain name (the 'example.com' part of 'user@example.com') used in an email is the same as the anti-phishing page our customer has posted.
This service is optional for our customers and if you want such assurance, you should ask them to provide us with an anti-phishing page.
RSS stands for [[Really Simple Syndication|http://en.wikipedia.org/wiki/Rss]], which is a micro-publishing mechanism for web sites and other content generating sources. RSS is also used as a general term covering the common RSS 1.0, 2.0 and [[Atom|http://en.wikipedia.org/wiki/Atom_(standard)]] publishing standards.
An RSS feed generally contains a few bits of info about the feed itself, such as its title and where it's coming from, and a collection of items constituting the feed content, usually news stories or blog posts, along with their titles, publishing date, author, category, and a link to the source of the item. They may contain other metadata elements too, but those are the common ones. The content elements may contain complete content (known as a "full feed") or just a summary or introductory paragraph.
Many people like using RSS feeds as they are typically free of ads, provide clutter-free offline reading (ideal for mobile use), and allow the reader (rather than the web site owner) to decide how they are presented. An RSS reader application will manage multiple feeds, group them into categories and update them regularly, and such functionality is often integrated into browsers (as it is in Internet Explorer, Safari, Opera and others). The usual pattern is that a feed is downloaded, summaries are read, and links to full content (e.g. the original blog post) are followed.
Most content management systems and blogs have the ability to publish RSS feeds of their content which may be consumed by anyone that's interested. A request for an RSS feed is often accompanied by additional parameters that might allow the consumer to specify a date range, a maximum number of items to return, one or more category selections, summarised or full content and so on. As an example, [[this news page at the BBC|http://www.bbc.co.uk/news/]] is also available as [[an RSS feed|http://feeds.bbci.co.uk/news/rss.xml]].
!Why use RSS?
The upshot of all this is that it's possible to create programs that consume the feeds and repurpose them - so you might pull the top 5 stories from your company blog and use them to populate your monthly newsletter in Smartmessages, saving you a lot of copy and paste in the production of your newsletter!
!Using RSS in Smartmessages
Smartmessages supports RSS feeds in templates. In the template editor we provide an {{{[[rss]]}}} tag that can be inserted at any point in your HTML template. This marks the point at which your rendered feed will be inserted. This is a generic placeholder - which feed you are reading from is not decided until you create a mailshot with your template - for template preview purposes we use the BBC's news feed. If you're familiar with programming concepts, you might think of the feed as being a mailshot instance property, not a template class property.
Using RSS is entirely optional - just omit the {{{[[rss]]}}} tag from your main template (it's omitted by default) and the RSS features will be ignored.
We provide several feed-level tags that can be used in your HTML template:
* {{{[[rss_feedtitle]]}}} The title of the feed, for example "News from the BBC".
* {{{[[rss_feedsubtitle]]}}} The sub-title of the feed, e.g. "Top stories from around the world".
* {{{[[rss_feedurl]]}}} The URL the feed comes from.
* {{{[[rss_feeddesc]]}}} A longer description of the feed.
The RSS sub-template is rendered for each item in the feed. Each item may contain these elements (Note that these tags may not be used in the main HTML or plain text templates):
* {{{[[rss_title]]}}} The item's title.
* {{{[[rss_url]]}}} The item's URL.
* {{{[[rss_summary]]}}} A story summary.
* {{{[[rss_content]]}}} The item's content.
* {{{[[rss_morelink]]}}} A ready to use HTML 'A' tag linking to the story's source URL wrapped around the word "More...".
* {{{[[rss_author_link]]}}} A link to the author using the author_url (if available) or author_email otherwise.
* {{{[[rss_author_name]]}}} The author's name.
* {{{[[rss_author_email]]}}} The author's email address.
* {{{[[rss_author_url]]}}} The author's URL.
* {{{[[rss_date]]}}} The item's publication date.
* {{{[[rss_updated_date]]}}} The item's last update date.
It's important to realise that RSS feeds vary a lot, and many of these properties may be unpopulated (empty), though we have tried to consolidate the different feed formats (for example Atom feeds typically don't include a category, RSS 1.0 feeds don't include a last update date), so if you're using an element, it's important to test it with your intended feed to make sure it looks right. Most important of these consolidations is that we always make sure that {{{[[rss_content]]}}} contains the item contents, even though the feed may place it in summary or description fields internally.
Our default RSS item template looks like this:
{{{
<h2><a href="[[rss_url]]">[[rss_title]]</a></h2>
<p>By [[rss_author_link]]; [[rss_date]]</p>
[[rss_content]]<br>
[[rss_morelink]]
}}}
This template is rendered in a loop using the data from item story in the feed you have specified, so if you specify 5 stories, it will be rendered 5 times, at the point in your HTML template denoted by the {{{[[rss]]}}} tag. We only allow rendering of a single RSS feed per template at present - it's possible to aggregate multiple feeds into a single one using an external RSS aggregator service or application.
In order to test your template, you need to create a mailshot using it. You set the RSS feed URL and the number of stories to include as part of the sending process. We take this approach so that you can use the same template for multiple feeds without having to change it.
The quickest way to send a new mailshot that's similar to one you've done before is to make a copy and edit it, for example to send this month's newsletter, you might copy last month's and edit it. This can save quite a bit of work.
Choose the mailings tab: [IMG[images/mailings.gif]]
Click the copy button for the mailing you wish to use as the basis for next mailing. [IMG[images/mailings2.gif]]
Click the Edit button for the new mailshot.[IMG[images/edit.gif]]
This will take you to the send page populated with the details from the original mailing.
Click on the ''mailings'' tab: [IMG[images/mailings.gif]]
Choose a mailing, create a new campaign or download a campaigns results to open in Excel.
[IMG[images/mailings3.gif]]
Click on the name of the mailing or a ''report'' button to see results.
Alternatively, click on the ''reports'' tab: [IMG[images/reports.gif]]
See the last viewed results, choose the required mailing from the popup list. View results.
SPF is the [[Sender Policy Framework|http://www.openspf.org/]], a mechanism used for preventing forgery of from addresses in email, and because of its strength in proving you are who you say you are, improves deliverability as filters are less likely to think that you are sending spam or phishing emails (and conversely, if you are sending spam, they can block you more effectively). It's an increasingly important weapon in the war against spam, it's used by most of the big email providers (AOL, Hotmail/MSN/Windows Live, Yahoo!, ~GMail, etc) and we recommend full support for it.
!!How does it work?
When you send a message through smartmessages, it will be sent by one of our mail servers. Normally, there's nothing to confirm that we're not just pretending to send legitimate messages from you, and because of that, servers receiving these messages may treat them with suspicion. You can fix this by naming us as a source of email for your domain in an SPF record, which is part of your domain name (DNS) setup. Read the [[OpenSPF site|http://www.openspf.org/]] for info on how to do that - the key thing is that you should add us to your SPF record, which will allow us to send mail from your domain.
!!The technical bit
Read the [[OpenSPF|http://www.openspf.org/]] docs and/or use their wizard to create an appropriate SPF string, then insert it into your DNS as a TXT record.
A fail-safe SPF record that includes us is:
{{{
v=spf1 a mx include:smartmessages.net ?all
}}}
This translates to: //Explicitly allow sending from this domain, its mail servers and smartmessages. For everyone else, give a neutral response.// Ideally you should aim for a {{{-all}}} (default to 'fail' rather than 'neutral' status), but don't set that until you're sure you have all your mail sources covered.
Once you've set it up, you can check it (and everything else in your DNS) is working using a service like [[dnsreport|http://dnsreport.com/]]. If you have a Unix/Linux/~MacOS X command line handy, you can try {{{dig txt example.com}}} to check that your DNS is publishing your record correctly.
You can do SPF lookups [[here|http://www.openspf.org/Why?showform=1]]. Enter one of our IP addresses (e.g. {{{93.93.128.140}}}) and your proposed from address. Ideally you want a 'Pass' result, though 'Neutral' will work. If you get a 'Fail' or 'Softfail' response, we will not be able to send from that address reliably for you.
If you want to be an especially good citizen in your SPF records, it's nice to provide IP addresses rather than hostnames as it saves the receiving mail server a DNS lookup. For example instead of using {{{a}}} or {{{a:www.example.com}}}, specify the actual IP, like {{{ip4:123.123.123.123}}}. The terms in an SPF record are evaluated left to right, so put the most likely matches first. You can do similar things for your MX record, though if you use a commercial incoming mail service like messagelabs, we suggest you stick with a regular {{{mx}}} rather than enumerating their ~IPs. They may also have an SPF include option you can use, so check their support pages.
We recommend that you don't enumerate our server ~IPs in your SPF, but use the include delegation we suggest as then we'll retain the ability to reconfigure our own servers without having to bother you with updates. Our own SPF uses a {{{-all}}} default, so we don't represent a back-door to neutral responses.
!!Are there any downsides?
One in particular: If you commonly send email through your ISP or web mail provider using your own domain as the from address (i.e. you are in the habit of forging your own address!), you have three options:
#Use the '?all' default action (which doesn't do anything for spam prevention)
#Include the ~IPs of your ~ISPs mail servers (they should be able to supply them, but it means that anyone else at your ISP can forge mail from you)
#Alter your sending settings so you only send through your own mail servers (the most effective, but least flexible way). If your ISP blocks or redirects SMTP traffic on port 25, you may be able to get to your mail server using a submission port which is typically on port 587 and tends not to get blocked; the submission protocol usually stipulates that you use both authentication and SSL encryption too, so it's a good security measure.
Another downside is that even if you go to the effort of implementing SPF, but can't get as far as a {{{-all}}} default policy, it's not going to gain you very much since even the most brazen forger will only merit a neutral response.
!!Google's Blacklisting Policy
Google has a very simple approach to SPF failures: messages are rejected, and they automatically add you to the [[CBL blacklist|http://cbl.abuseat.org/]], which is used by hundreds of ~ISPs. So a single message with an incorrect SPF setup could get you (and us) blacklisted from many ~ISPs. We plan to enhance our send-time SPF checks to ensure you can't send if your from address will cause an SPF failure.
!!What about ~SenderID?
[[SenderID|http://www.microsoft.com/mscorp/safety/technologies/senderid/default.mspx]] is a Microsoft-backed extension of SPF. Though it has some [[well documented pitfalls|http://www.openspf.org/SPF_vs_Sender_ID]], they are easily avoided, and it's worthwhile implementing it because hotmail (and related sites such as MSN and live.com) uses it, which is a good proportion of most ~B2C mailing lists. The most straightforward way to implement ~SenderID is to set up your SPF record and then delegate to it. This way you will only have one record to maintain (your SPF) to identify your legitimate mail sources. To do this, create another TXT record containing this text:
{{{
spf2.0/pra ?all
}}}
And that's it. Having done this, you should [[register your use of SenderID|https://support.msn.com/eform.aspx?productKey=senderid&page=support_senderid_options_form_byemail&ct=eformts]] on Microsoft's site to ensure they use it.
!!What about ~DomainKeys and DKIM?
While SPF helps to increase trust that a message's //origin// is where it says it is, DKIM and Domainkeys serve to show that a message's //content// has not been altered in transit, so the two go hand-in-hand. The way it works is fairly complex (using public-key cryptographic signatures), however the practicalities are quite straightforward. If you're already altering your DNS to add our SPF, then we thoroughly recommend that you set up DKIM as well. [[Read our guide|DKIM and DomainKeys]] on what it means and how to do it.
!!Example of a forgery rejection
Someone on the Turkish ~MarsNet DSL service attempted to send a message that claimed it was sent from our own mail server. Not surprisingly, when an SPF check was done by a Turk Telecom mail server (thank you Turk Telecom!), the message was rejected because our SPF record correctly identified it as a forgery. You can see that amongst the headers is a link to a page which will explain why the message was rejected. This is absolutely what SPF is intended to achieve, and here you can see it working perfectly.
{{{
This is an informative message sent by mail.atamedya.com.
The server was not able to deliver your email message to the following addresses:
<sales@synchromedia.co.uk> (mail.synchromedia.co.uk: 550 See http://spf.pobox.com/why.html?sender=postmaster@synchromedia.co.uk&ip=77.92.158.139&receiver=mail.synchromedia.co.uk (#5.7.1))Reporting-MTA: dns; mail.atamedya.com
Arrival-Date: Thu, 16 Jul 2009 12:01:44 +0300
Final-Recipient: rfc822;sales@synchromedia.co.uk
Action: failed
Status: 5.1.1
Remote-MTA: mail.synchromedia.co.uk
Diagnostic-Code: SMTP; 550 See http://spf.pobox.com/why.html?sender=postmaster@synchromedia.co.uk&ip=77.92.158.139&receiver=mail.synchromedia.co.uk (#5.7.1)
Received: from synchromedia.co.uk ([81.215.222.134])
by mail.atamedya.com (Kerio MailServer 6.1.4)
for sales@synchromedia.co.uk;
Thu, 16 Jul 2009 12:01:42 +0300
From: "Post Office" <postmaster@synchromedia.co.uk>
To: sales@synchromedia.co.uk
Subject:
Date: Thu, 16 Jul 2009 12:01:11 +0300
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_0014_D23B030D.A4D8B533"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 6.00.2600.0000
X-MIMEOLE: Produced By Microsoft MimeOLE V6.00.2600.0000
}}}
We are very aware that we are holding your precious data, so we ensure that your data is safe as far as we can:
!!SSL encryption
We use secure connections whenever you're viewing our site (look out for a little padlock in your browser) - this means that all ~URLs, data (e.g. your lists), pages, cookies are safely away from anyone eavesdropping on the connection between us. Any site that doesn't do this is bordering on breaking the data protection act (though not quite breaking it, yet). Our mail servers use SSL too, so the messages we send out are also safe from prying eyes while in transit, so long as the recipients mail servers support it too.
!!Make no attempt to hide
All our internet addresses resolve properly both forwards and backwards, and they change only very rarely (for example when we use a new ISP or add new servers) - we don't try to hide our servers because we don't send spam.
!!SPF support
All the mail we send in our own name uses our [[SPF]] record (which is set as strict as it can be) so recipients can be sure that we sent it, not someone pretending to be us.
!!Our code
We follow best practices when it comes to writing secure code. We have defences against SQL and header injection attacks (about the most common kind) amongst others, and our servers are always up to date with the latest security patches.
Generally, we don't run other web apps (e.g. forums, blogs, ~CMSs etc) on our cluster (for example this wiki is not on it), so we're immune to security holes in such packages.
!!Viruses, trojans etc
We don't scan outgoing messages for two reasons. Firstly, we generate every message from scratch - we never forward anything from others, and we don't allow attachments or ~JavaScript in our messages, eliminating the most common infection vectors. Secondly, we don't run Windows (on desktops or servers), where 99% of security threats are found.
!!Backups
We take backups every night, so we guarantee to have a backup of all your data up to that age. Beyond that your data is your responsibility - you can download a copy from us any time you like.
!!Redundancy
Smartmessages runs on a cluster of redundant servers, allowing hot failover should one fail. All critical systems reside on RAID volumes and use redundant power supplies, so we're immune to the most common kinds of hardware failures.
!!Physical security
We host at ~RedBus in London's docklands, one of the UK's premier hosting locations, featuring clean, reliable power supplies, overspecified air conditioning, heavy physical security (retinal scanners etc).
!!But that's not the end of the story...
It's your data we're trying to keep safe, so you can help too. Implement [[SPF]] for your own domain names and add us to your list of permitted email sources. Make use of our [[anti-phishing|Phishing]] features, especially if you are collecting data from you recipients through any of our services.
"Send to a friend" is a feature whereby having received a message from us, you feel you might like to forward it to a friend, and rather than using your email program's forward facility, you come to our site and fill in a form that sends your friend a copy of a message. Sounds like a good idea, but it has numerous hidden pitfalls.
!!Our solution
We provide a simple forwarding approach that has none of the downsides of the "traditional" approach: We provide a link that creates an email message in the user's usual email program, pre-populated with a link to the web version of the original message. This way:
* The message is sent directly from the recipient to the friend - we're not involved at all.
* It's instant and very easy to use - no logins, no web forms required
* The friend gets to see your message in full, with none of the original recipient's personalisation, and guaranteed free of any problems introduced by the original recipients own email service or program (many such programs corrupt or "constructively rewrite" forwarded messages).
!!What's wrong with the original way?
* It's very unlikely to be used. When reading an email you want to forward, your first reaction is to use your normal "forward" command, not one built in to the message.
* The person who receives the message effectively receives an unsolicited message from us. That's borderline legal in the UK, but in many countries it's an imprisonable offence (for example in Germany and Spain). Since it is us that is doing the sending, we're not too happy about that.
* For compliance with data protection laws (including in the UK), we have no rights to, and thus can hold no data about the friend that you sent the message to - we must discard everything, so this operation has zero value for collecting leads.
* Any unsolicited message is much more likely to be reported as spam, which when considered globally is likely to negatively impact the deliverability of ALL of your other mailings, including those for which you have full opt-in permission.
* If we pretend that you are the sender (e.g. by putting your address in the from field), we will be spotted as a fake, your message may well be rejected by any receiver that implements SPF (as we recommend), and you're even more likely to be reported as spam.
* In order to provide a web page where you could do this safely, you would need to log in to our site. If we didn't do this, we would be providing a spam gateway.
* It's not all down to you - we send message for all kinds of companies, and allowing it for one could reduce the deliverability of ALL those that use smartmessages, including you.
* It's not permissible under Goodmail compliance, which is something we're aiming for (which gets better deliverability for everyone).
Does it still sound like a good idea? Even if we did provide this service, the one and only benefit you could possibly derive is a very small increase in the number of people that you know have viewed your message. Say 0.5% of recipients (being generous!) forward the message by this route. Perhaps half of those might arrive unaffected by filters (because of their forged origin), and of those that do, 75% of them might actually be read (the one upside of supposedly being from a friend!). All that effort represents a possible 0.18% increase in opening stats, something that could easily be wiped out by a single spam report.
!!How fast does sending go?
Rates vary because of many factors, but you should expect rates over 500,000 messages/hour for larger mailshots. We are working on improving this all the time. Factors affecting send rates include:
* Size and complexity of your template
* Size of your list
* Activities of other smartmessages users
* Distribution of recipients (e.g. sending all to one ISP may be slower).
Lists of over about 16,000 recipients will get the highest send rates as over that size mailings can take best advantage of our sending cluster.
Should you have particular performance requirements beyond this, we can add servers to improve throughput at any time.
!!Why wouldn't I want it to go faster?
There is a large negative associated with a high send rate - it may adversely affect your delivery success rate. Many large ~ISPs (Yahoo!, Googlemail, AOL, MSN etc) impose restrictions on how many messages may be sent from one location in a given amount of time, or the frequency with which you send messages to specific users. They don't explicitly tell you that they are doing this, or let you know what their limits are, but you'll find that you'll get increasingly large numbers of bounces with a "4.4.1 Deferred Delivery" code. This code means that they may accept the message at some unspecified time in the future. Our mail server will retry delivery and progressively fall back with longer and longer times until it just gives up, typically after 4-7 days.
There are six steps to the send process:
# Choose your template
# Name your mailshot & choose where it will be saved
# Add extra content if required for the email
# Choose who you are sending it to
# Choose which email address you are sending the email from & also the reply address
# Choose when to send the mailshot
There is also a 'final check' page where all the selected options are presented together for you to check before sending. Once a mailshot has been sent, it has gone, there is no 'undo', so be careful! Our servers go very fast, so hitting pause could still mean that 1000's of emails have been sent, and may not be possible at all on small mailings.
See also [[Continuous mailshots]].
Here are the screens from the system:
[IMG[images/smsend1.gif]]
[IMG[images/smsend2.gif]]
[IMG[images/smsend3.gif]]
[IMG[images/smsend4.gif]]
[IMG[images/smsend5.gif]]
[IMG[images/smsend6.gif]]
[IMG[images/smsend7.gif]]
Help and tips for your Smartmessages.net account
Smartmessages has support for helping your message get out onto popular social networking sites including Facebook, Twitter, Google+ and ~LinkedIn.
!What can be shared?
Our social networking features are all about your subscribers liking the email they have been sent, rather than your Facebook/~LinkedIn etc pages. Because the email message itself can't be shared directly on the web, the sharing is of the web version of an email. This also prevents any personal data from being exposed. The template tags we offer all point at the web version, and because every web version has a unique URL we can report on them separately, which is the data that you'll see in our social networking reports.
If you want to point your subscribers at, for example, your facebook page, do that using a normal link, which will be tracked like any other link in your emails and reported with other clicks, not in the social networking reports.
!How social network sharing works
Here's an example of how facebook's 'like' button works; other networks are similar.
* A user sees something they like that has a Facebook 'like' button on it, which they click.
* Behind the scenes, Facebook receives the click, examines the source URL that the button gives it (which might not be the same page), requests it, and examines the page for additional information.
* A web page opens giving the recipient a chance to add a comment about the item, and also showing a preview of information about the link, which will usually include a title (which is a link), a short excerpt of text, and a preview thumbnail image.
* The recipients optionally fills in a comment, clicks ok.
* The 'like' appears in their timeline and is made visible to all their friends, who look at it and hopefully 'like' it too.
The 'behind the scenes' part here is implemented through something called [[open graph tags|http://ogp.me/]], which is a standard common to many social networking sites, including facebook, linkedin and Google+. We take care of all that for you.
After this has happened, we can ask the social networks how many 'likes' have happened for the page in question, and report that figure back to you. The link we present to social networks is that of the un-personalised web preview of your mailshot, so there is no opportunity for leaking your recipients' confidential data.
If you want to target something other than the mailshot preview (for example your own site's home page), you'll need to create your own ~URLs, and you will be limited to using only static content (see notes about appearance below).
!Getting started
First things first: go to your smartmessages account settings page and make sure the 'Allow open graph tags' option is checked. Open graph tags are added to your templates if they are going to be visible to social networks: they are used to extract information about the page (metadata) that's not available from the URL itself, such as the page title, a preview image for it, where it's from etc.
Next, fill in your Twitter ID - if you don't have one, sign up for one on [[twitter.com|https://twitter.com/]] - and we will use this when creating links to share on twitter, and mentions of your ID will automatically appear in your twitter client (or on the twitter web site).
!Twitter follow
If you want to have people follow you on twitter rather than share a link to one of your emails, fill in your twitter account in account settings and add the tag {{{[[twitterfollow]]}}}, and we'll add a standard twitter follow button.
!Adding tags
You need to add links to your templates for the various sharing buttons. These are available in the pop-up menu of insertable fields in the HTML editor. The tags are {{{[[facebooklike]]}}}, {{{[[twitter]]}}}, {{{[[googleplus]]}}} and {{{[[linkedin]]}}}. That's all there is to it!
If you want to use your own graphics or text to build these links, we provide plain ~URLs for these services too. The tags for them are:
{{{[[staticshareurl_facebook]]}}}, {{{[[staticshareurl_twitter]]}}} and {{{[[staticshareurl_linkedin]]}}}.
Using any of these tags will automatically insert an {{{[[opengraphtags]]}}} tag in the head tag of your template.
!Practical considerations
The standard sharing buttons for social web sites are designed for use on the web, and are not a good fit for email, in particular they often use javascript and iframes, neither of which work //at all// in email. Fortunately there are simplified buttons for most of the networks where the most important sharing options are possible via nothing more than a normal HTML link. A complication is that you want to use the simplified buttons in email, but the enhanced ones when viewing a web page. This split functionality is built-in to the buttons we provide, but you'll find they don't quite look the same - the web versions often include additional dynamic information, such as the number of people that have already clicked, and the names of a recipient's friends that have clicked them, see below for examples.
In general we have selected the simplest, smallest display option available for each network, though we do include click counts if they are available as they are good encouragement. We have no control over how dynamic versions appear (and they may change at any time), so we suggest you place them in wrapper divs away from your main content, and don't get hung up on pixel perfection since we have no control over it!
!Button appearance
There are three different contexts that sharing buttons can appear in:
#When previewing a template in the template editor
#When previewing a mailshot (sent or unsent)
#In a sent email message
Cases 1 and 2 both occur in your web browser, so they make use of dynamic buttons. A problem with case 1 is that the URL that you 'share' is not visible to the outside world (it requires that the requester (in this case Facebook etc) has access to your smartmessages account, which they don't); this means that the buttons may not work as expected in this view. Case 2 will work fine since the mailshot preview is publicly visible (it's the same thing as the web version your emails link to), and will display the dynamic versions of the buttons. Case 3 is how your recipients will typically see the buttons, which is the simplified versions mentioned above, which are small images with HTML links. Here are examples of how they appear in email:
* Facebook: [IMG[http://www.smartmessages.net/images/facebooklikebutton.gif]]
* Twitter: [IMG[http://www.smartmessages.net/images/twitterbutton.gif]]
* ~LinkedIn: [IMG[http://www.smartmessages.net/images/linkedinsharebutton.png]]
Where possible we have used official images for these buttons so they look the same as buttons for these services seen elsewhere.
Please note that ''Google '+1' buttons will not appear in email'' because Google provides no static implementation of their button, so it can't work in email.
Here are the same buttons rendered in a web page:
[IMG[images/socialnetworkbuttons.png]]
as you can see, the dynamic versions often include additional features and thus look slightly different.
!Reporting
We request information about sharing activity from the social networks whenever we update your mailshot reports, and present it on our reporting pages. There may be some lag between shares happening and the stats being updated.
If a recipient receives a message they don't want, they often have two choices: unsubscribe from the list, or report the message as spam. All of our messages contain a working [[unsubscribe link|Unsubscribes]], and we also populate the "~List-Unsubscribe" header, making it as easy as possible for recipients to remove themselves from lists, however, some will still click the "report as spam" button. Most of the time this is not very helpful - it often won't stop them receiving messages because we don't get to find out about it (unlike unsubscribes), and it can adversely affect the delivery to those who do wish to receive them. Fortunately there is a little light at the end of the tunnel - some ~ISPs provide spam report data whenever someone reports a message as spam. Unfortunately, some don't do it at all, and some make it difficult to receive such messages. AOL is a shining example of how to do it right - not only do they provide the reports with a minimum of fuss, but they are in a format (Defined by an open standard - [[RFC 3462|http://www.faqs.org/rfcs/rfc3462.html]]) that's easy for us to process automatically. When other ~ISPs see the light, we will add them to our spam report handler.
Yahoo! requires that you use [[DKIM|DKIM and DomainKeys]] in order to receive spam reports.
!!What happens when we receive a spam report?
For the ~ISPs that we receive reports from, we process spam reports automatically, as soon as they are received. The address that the original message was sent to is unsubscribed from all lists within the account that the spam reported message was sent from, and a block is placed on that address so that no messages will ever be sent there again - a spam report has such bad consequences for deliverability that it's simply not worth risking them for the sake of a single address. The address will be suppressed from any subsequent list uploads and prevented from subscribing in future. Until the market recovers from the damage caused by the "never click an unsubscribe link" myth, we have little choice in this approach.
!Welcome to Smartmessages Help [>IMG[images/butterfly.gif]]
This is here to help you to use our service as easily as possible. Anything else you need help with? [[Ask us|ContactUs]]!
There are several parts to email marketing:
*[[What you send|Templates]]
*[[Who you send it to|MailingLists]]
*[[How & when you send it|SendingMailings]]
*Tracking [[opens|Open Tracking]] and [[clicks|Link Tracking]]
*[[What happened after you sent it|Reporting]]
*[[Mailing list|MailingLists]] management - [[unsubscribes|Unsubscribes]]/[[subscribes|Subscribing]], [[bounces|Bounces]] and [[spam notifications|SpamReports]]
This is what Smartmessages does for you.
Have a browse around this ~TiddlyWiki. Click some of the topics on the right and they will appear in this middle area.
!!Technical Info
If you're looking for technical info to integrate Smartmessages services into your site, you're in the right place.
*[[Creating custom sign-up/subscription forms|Subscribing]]
*[[Creating custom unsubscribe forms|Unsubscribes]]
*[[Complete programmable API|API]] with [[sample code|APIExamples]]
*[[Callbacks for real-time integration|Callbacks]]
*[[Using Google Analytics|GoogleAnalytics]]
*Authenticating your email with [[SPF]] and [[DKIM|DKIM and DomainKeys]]
*Adding [[custom branding|Branding]] to standard Smartmessages pages
There are several ways that people can subscribe to your lists. In order of increasing complexity and flexibility:
* A simple signup link
* Our standard signup form
* A custom form using our processor
* A custom form using our [[API]]
On the contacts page you'll see a list of your mailing lists, and each one has a "Link" link which will take you to a subscribe page for that list, for example https://www.smartmessages.net/subscribe/1886. If you want to add a simple subscribe link to your own pages, you should use whichever of those links is appropriate. The default form captures just the email address and a single-field name. If you want more than that, you can build your own form.
!!Ready-to-roll signup forms
If you click on 'manage' on the contacts page to look at an individual mailing list, you'll see a tab in the actions section called 'Signup form'. In there you'll find a text box containing a ready-to-use HTML form for that list. Just select it, copy it and paste it into your own site's HTML.
!!Custom signup forms
Many of our customers want to use their own web forms to submit information to our subscription processor, and that's also easy to do - just point your form's action at https://www.smartmessages.net/subscribe.php. The standard ready-to-roll form we provide has lots more options available in its processor via the following parameters (bold fields are required):
*''//command//'' - the value 'subscribe'
*''//emailaddress//'' - the email address of the new subscriber
*''//mlid//'' - the ID of the mailing list they are subscribing to (this is the value that's visible in the "link" links on the contacts page)
*//title// - 'Mr', 'Miss', 'Dr.', 'Herr' etc.
*//dear// - a straightforward single-field name that actually corresponds with our 'dear' field
*//firstname// - the subscriber's first name
*//lastname// - the subscriber's last name
*//companyname// - the subscriber's company name
*//postcode// - the subscriber's postcode
*//language// - the subscriber's preferred language (2-character ISO 639-1 code, e.g. 'en', 'fr', 'de', defaults to 'en')
*//country// - the subscriber's country (2-character ISO 3166 code e.g. 'GB', 'FR', 'DE', defaults to 'GB')
*//redirect// - a URL to go to after processing the subscription request instead of displaying our own default page (don't forget to ~URL-encode it)
*//json// - any value - its presence will indicate that you want a response in a JSON format (e.g. you're subscribing from ~JavaScript)
By building your own form and providing a redirect URL, you can use our subscribe processor completely transparently and invisibly to your users. If you specify a redirect URL, you will receive an HTTP GET request to that URL containing your submitted parameters (so you can process it further - for example add it to your own contact database) along with additional information about the success (or not) of the subscription request in a 'statuscode' parameter. This has the following meanings:
*1 - Success
*2 - Already subscribed
*3 - Internal error
*4 - Missing required parameters
*5 - Invalid email address
*6 - Invalid mailing list ID
The JSON output also includes a 'success' value that is true for codes 1 and 2, false for others, along with a 'message' value containing a plain-text explanation of the response.
Here's a minimal example form that you can adapt:
{{{
<form action="https://www.smartmessages.net/subscribe.php" method="post">
<p>
<input type="hidden" name="command" value="subscribe" />
<input type="hidden" name="mlid" value="<insert your mailing list ID here>" />
Name: <input type="text" name="dear" />
Email address: <input type="text" name="emailaddress" />
<input type="submit" name="Subscribe" />
</p>
</form>
}}}
Of course you can add more input fields from the list above.
If you want subscribers to be able to choose from multiple lists, create an HTML select input with multiple options for the different mailing lists' ~IDs you want.
!!Landing page
Every smartmessages account gets to use our simple landing page which includes data capture of all standard fields and the option to subscribe to all mailing lists that are marked as visible. You'll find the link to your landing page on the front page after you log into smartmessages.
!!API
If you are undertaking more complex integrations (for example capturing custom fields or a full postal address) then you will be better served by the subscribe and setUserInfo functions in our [[API]].
A suppression list is an "anti-mailing list" - a list of people that you do NOT want to mail.
We maintain several different kinds of suppression list:
*Unsubscribes
*Manual suppressions (for example people that phoned you up to ask to be unsubscribed)
*Spam reporters
*Persistent bouncers
[[Spam reporters|SpamReports]] are always applied account-wide, to all your mailing lists, so if someone reports one of your messages as spam, they will be removed from all your lists and prevented from being uploaded to all mailing lists in future. The small increase in delivery count you might get from persisting with such recipients is far outweighed by the negative impact it can have on your deliverability to everybody else. You can download a list of the people who have reported your messages as spam on the contact page.
Persistently [[bouncing|Bounces]] addresses are the only thing we share across all accounts, so you can benefit from some level of list cleaning simply by uploading your list before you have sent anything, just because one of our other customers may already have ascertained that some addresses bounce persistently. This bounce list is not available for download.
We do not currently have a mechanism for suppressing entire domains, but it's something we are considering.
When you do a list upload, all addresses that match any of these suppressions are removed before they are added to your list.
!!Removing suppressions
Sometimes you might want to undo or remove a suppression, for example if you unsubscribed yourself while testing. Each mailing list page has an 'add a single subscriber' option, and if you enter an email address here, any suppressions they have will be removed, and they will be subscribed to your list. We consider that if you have sufficient administrative privileges, then adding a single address is something we will allow. Similarly, anyone completing a double-opt-in subscription will have suppressions removed, not least because this requires explicit opt-in (which we consider a reasonable request to override any previous opt-out), and can't be done to a bouncing address. In short, suppressions are ONLY applied while uploading lists.
!!Global Unsubscribe
You'll see something called "Global unsubscribe" mentioned on our contacts page. This parameter controls what happens when someone unsubscribes, and it can be ON (the default) or OFF. This is a parameter we maintain control over, and you need to ask us if you'd like it changed.
!!!Global unsubscribe ON
If global unsubscribe is ''ON'', when someone unsubscribes from ''ONE'' of your lists, they will be removed from ''ALL'' of your lists. They are added to a suppression list that will prevent them from being uploaded into ''ANY'' list. This is the default mode of operation, and is equivalent to what most other email providers do too.
!!!Global unsubscribe OFF
If global unsubscribe is ''OFF'', when someone unsubscribes from ''ONE'' of your lists, they will be removed from ''ONLY'' that list. They will be added to a suppression list linked to only that list. Subscriptions of that address to other lists will be unaffected, and you will be able to upload them into other lists.
We maintain this policy because of the following scenario:
* Customer uploads their address list into Smartmessages.
* They send a few mailshots to it.
* Records of bounces, unsubscribes, spam reports and other usage data accumulates in Smartmessages.
* Customer uploads their original (or their own new version) address list into a new list on Smartmessages.
At this point with global unsubscribe OFF, sending to the new list would be illegal because it would contain people that had unsubscribed. For this reason, we will only turn off global unsubscribe for customers who can demonstrate that they are handling their data with sufficient care that they don't run into this problem.
Some of our customers want to integrate their mailings with surveys using services like [[SurveyMonkey|http://www.surveymonkey.com/]] and packages like [[LimeSurvey|http://www.limesurvey.org/]]. Here are some integration notes.
!!~SurveyMonkey
~SurveyMonkey doesn't preload lists of potential respondents, instead it relies on collecting details of those that do respond. This means that they can't prevent unauthorised people accessing the survey, and also that they can't offer any data pre-population or personalisation. On the upside, it also means there is very little setup required. To connect smartmessages messages to a survey, you need to use their [[web collector integration|http://www.surveymonkey.com/HelpCenter/Answer.aspx?HelpID=343]], and you'll generate ~URLs to feed into it using our template editor. You need to start with a link to the anonymous survey entry point and add a unique identifier for each recipient - their email address will do nicely. So a base URL for your survey (obtained through your ~SurveyMonkey account interface) that you link to from your message template might be:
{{{
http://www.surveymonkey.com/s.aspx?sm=v8MbvURxoHkWfvud7Or3Cg_3d_3d
}}}
You then need to add a 'c' paramater to it like this:
{{{
http://www.surveymonkey.com/s.aspx?sm=v8MbvURxoHkWfvud7Or3Cg_3d_3d&c=[[email]]
}}}
That code will expand to the recipient's email address
After your survey is complete you can download the results from ~SurveyMonkey.
For those that like to know such things, here is some technical background on Smartmessages.
* Built in OO ~PHP5, around 60,000 lines, excluding libraries
* Message generation using [[PHPMailer|http://phpmailer.worxware.com/]] (of which we are one of the maintainers)
* Tested with ~SimpleTest
* Templates (for both site and messages) with Smarty
* ~MySQL 5 database cluster
* Memcached distributed cache
* Run on Ubuntu Linux, on dual- and quad-core servers
* Cluster deployment for scalability and redundancy (using linux-ha) - we can add more servers to increase speed as needed
* Sharedance for persistent distributed, redundant session storage
* ~FCKEditor x-browser WYSIWYG editor
* Last and definitely not least, ~GreenArrow qmail-based mail servers, capable of > 1million messages/hour per server!
We offer complete support for user-uploadable templates, so you can paste in your HTML, or grab it from a web page. These templates can make use of some (but not all) of our dynamic reusability features.
//You don't necessarily need to use this feature. Our standard templates provide straightforward WYSIWGYG editing without any HTML authoring required. You only need to use this feature if you're familiar with HTML email and want to code your own.//
The templates tab allows you to add a new template, then add your contents. You can provide both HTML and plain-text versions, which you can grab from ~URLs if you happen to have posted your templates on a web server somewhere, though be aware that the web and email are really quite different, and most web pages will simply not work as email.
!!On the templates page...
You'll see a list of your templates (which will initially be empty) on the left hand side - click the 'Add' button below the list to create a new template.
Each template can have a title (which will appear in menus and informational screens, such as the mailings tab). The description is used on the send page to provide a longer description of that particular template's features or purpose.
If you include a {{{[[body]]}}} tag in your HTML and plain text templates, you will be provided with an HTML editor in the content tab on the send page. This allows you to re-use templates, rather than having to define a new one every time. If you omit the {{{[[body]]}}} tag, you won't see an editor.
The subject line is a default and may be overridden on the send page (and you can use the same syntax for field insertion in the subject as in the HTML and plain text bodies, though bear in mind that HTML tags won't work.
!!!HTML, RSS and plain text
There are three parts to a template:
* ''HTML'' contains markup source that the mail client will render in a similar way to how a browser renders a web page.
* [[RSS]] optionally contains a small HTML sub-template that's rendered inside your HTML template for each story within an RSS feed.
* ''Plain text'' is used to generate a markup-free version that's used by simpler email clients, including some mobile clients (e.g. Blackberry, Pine, Mutt).
Email messages are constructed in a way that uses both HTML and plain parts, and tells receiving programs to prefer the HTML version, but fall back to the plain text one if it's not able to display it. It's important to include both versions of your content so that recipients can choose how to read it, and many spam filters frown upon ~HTML-only messages.
!!Dynamic fields and macros
Each editor panel has a pop-up menu containing various dynamic fields and macros that you can insert into your templates. These range from simple tags like {{{[[firstname]]}}} to more complex macros like {{{[[formal_greeting|Reader]]}}}.
!!!Greeting your recipient
The greeting macros dynamically create a suitable greeting string. Given data 'Mr', 'John' Smith' in 'title', 'firstname', and 'lastname' fields, {{{[[formal_greeting]]}}} would result in 'Mr Smith', and {{{[[informal_greeting]]}}} would give 'John'. If you had a 'dear' field containing 'Johnny', that would be used in preference in both cases. If you lack appropriate data to generate a suitable greeting, it will fall back to whatever default you specify in the tag. A typical example in your template might be:
Dear {{{[[informal_greeting|Subscriber]]}}},
The text after the '|' character (that's a 'pipe' character, not a letter l or I), in this case 'Subscriber', is used when you don't have any suitable personalisation fields.
Here's a table of various input field possibilities and how they affect greeting tag output:
|!Template tag |!Dear|!Title|!Firstname|!Lastname|!Output|!Notes |
|{{{[[informal_greeting|Subscriber]]}}}|||||Subscriber|No data|
|{{{[[informal_greeting|Subscriber]]}}}||Mr|John|Smith|John|Basic firstname|
|{{{[[formal_greeting|Subscriber]]}}}||Mr|John|Smith|Mr Smith|Basic title + lastname|
|{{{[[formal_greeting|Subscriber]]}}}|||John|Smith|Subscriber|Missing title|
|{{{[[informal_greeting|Subscriber]]}}}||Mr||Smith|Subscriber|Missing firstname|
|{{{[[formal_greeting|Subscriber]]}}}||Mr|John||Subscriber|Missing lastname|
|{{{[[informal_greeting|Subscriber]]}}}|Johnny|Mr|John|Smith|Johnny|Dear overrides firstname|
|{{{[[formal_greeting|Subscriber]]}}}|Johnny|Mr|John|Smith|Johnny|Dear overrides formal address|
''Note that this syntax for a default value applies ONLY to these greeting fields, not any others''.
!!!Tracking openings
For your message openings to be tracked, ensure that you include the {{{[[tracking_image]]}}} macro. This expands to a transparent 1-pixel GIF that includes tracking information in its URL. See [[open tracking|Open Tracking]] for more information on tracking message openings. If you don't put this in, we automatically add one for you, just before the closing body tag.
!!!Unsubscribe
For your subscribers to be able to remove themselves from your lists, the {{{[[unsubscribe_link]]}}} generates a simple unsubscribe link with the work 'unsubscribe'. If you want to make your own link, you can use {{{[[unsubscribe_url]]}}} in your own href instead.
To conform to our terms and conditions and data protection / anti-spam laws, ''you MUST include one of these links''. If you do not include one of these options, we will insert one automatically at the bottom of your template, just inside the closing body tag.
!!!Web version
For clients that end up with unreadable messages, or those with plain text email readers, you can include a link to a generic (no personal info, because it's public) version of your mailing using the {{{[[webversion_url]]}}} or {{{[[webversion_link]]}}} tags which resolve to a plain URL or a complete link tag respectively.
Note that web versions will suppress the appearance of unsubscribe and web version links, since they are meaningless and confusing in that context.
There are alternative tags to create personalised web versions, but you need to [[read about what they may mean|WebVersions]] before using them.
!!!Unique ~IDs
Sometimes you need a unique ID to prevent caching (a.k.a. a "cachebuster") or provide unique links into an ad tracking system etc. We provide two different kinds of unique ID in the template editor: {{{[[unique_message_id]]}}}, and {{{[[unique_recipient_id]]}}}. The difference is that every message sent to every recipient will have a different unique message ID, but every message sent to the same person will have the same recipient ID. The ~IDs themselves are standard 32-character ~MD5 hashes. Which one is appropriate for your application will be up to you, but generally you should only need {{{[[unique_recipient_id]]}}}. Alternatively you could use the recipient's email address as a unique id using {{{[[emailaddress]]}}}.
A template provides the overall look and layout of the content of the messages you send through smartmessages.
Out of the box, we provide some standard basic templates for a blank message (you provide all content), a simple note, an invitation to an event, and a newsletter. These are ready to roll, and if you have paid for our account setup service, you'll find these automatically make use of your custom headers and footers. Each template can contain multiple editable elements - a simple note has a single content area, but a newsletter has four, and the send page will automatically show the options to match the template.
These templates also include a plain text version, an automatic link to a web version (for those who can't or have trouble receiving HTML email), a link to subscription options (unsubscribe etc), and an opening tracking image.
!!What's special about Smartmessages templates?
The big difference between our templates and those of most other providers is that they can be reused. Instead of having a designer create a new template for you every month, you can get them to do it just once, and then you can just change the content every month without having to go back to your designer every time. We feel that anything less doesn't really justify the name 'template' - why go to all that effort and testing for a single mailing?
We can build custom templates for you from scratch, or you can provide us with your version and we can adapt it for you, or you can do it all yourself through our [[templates|TemplateUploads]] page.
!!Creating your own templates
If you're creating your own templates using our templates page, there are some things you need to be aware of when designing HTML for email.
!!!Email is not the web
Many people forget this and expect HTML email to act just like a web page. It won't. Here are some things you need to look out for:
* ''Old-school HTML 4 rules''. With the recent ravages of MS Outlook 2007, CSS in email is now mainly a recipe for disaster. While there are still ways you can use it, it's so convoluted to make it work reliably that it's not really worth the hassle. This means that you should dust off those old articles about sliced images, tables for layout and font tags - they are the only things that work reliably and easily. Email now is like the web was in 1997.
* ''All ~URLs must be absolute'' - if your links or images don't start with 'http', they are probably wrong. On a web page these ~URLs are considered relative to the current page, but when you're looking at an email, the message doesn't have a URL, so those ~URLs will not work.
* ''No external resources'' - that means no external scripts or style sheets.
* ''Don't use the <base> tag'' - it can work, but will also break in some clients (hotmail), and will prevent you from using intra-page links (href="#...").
* ''No text over images''. One of Outlook 2007's most heinous acts is to make it impossible to place text on top of images because table cell backgrounds don't work in it.
* ''Web pages are not email messages''. The majority of web pages presented directly as emails simply won't work, so don't expect them to.
* ''Imported templates are filtered'' before use. If we didn't filter javascript and various other aspects, we could become a vector for [[XSS attacks|http://en.wikipedia.org/wiki/Cross-site_scripting]].
In order to work within these restrictions, we have some other suggestions which are mainly just good practice for web, email and informational design:
* ''Use our checkers'' - On the templates page you'll find a whole array of tools to check your links, images markup and spam rating - ''Use them!''
* ''Validate your HTML'': Invalid HTML is just wrong, no exceptions - if you have cross-client layout problems, check the HTML is valid first - Use the validation link in our template editor, or alternatively the [[Total Validator|https://addons.mozilla.org/firefox/addon/2318]] plugin for Firefox is a good tool for this, or paste a rendered message (our web preview is a good source for this) into the [[W3C validator|http://validator.w3.org/]].
* Make sure your message remains meaningful and legible with images disabled since that's the default in the majority of email clients. //Always// use alt attributes on your images, and make them meaningful as they often displayed when images are off. Our template editor has a 'preview without images' link for testing how this looks.
* ''Check your content'': We provide link and image testing options in the template editor, which will tell you about broken links, missing, misformatted or mis-sized images. We also provide a ~SpamAssassin-based spam checker and some simple tests against our own set of useful rules.
* ''Keep it simple'' - email clients are not modern browsers; keep your stories short and messages clear, link to more complex content (clicks are your marketing intelligence goldmine). Smaller, simpler messages send faster, bounce less, open faster, dilute recipients precious attention less.
* Provide a plain text version of your template - some users //like// living in the last century...
* Use background colours to add colour to your messages when there are no images.
* ''TEST, TEST, TEST'' in all email clients you expect your message to be read in - you'll probably need yahoo, hotmail, gmail accounts, and Outlook 2003, Outlook 2007, Apple Mail, Eudora, Evolution and Thunderbird desktop clients. Alternatively, use a service like [[Litmus|http://www.litmusapp.com]] for cross-client testing.
* You don't have to do anything special to get your links tracked - just leave them as they are. You can also [[suppress link tracking|Link Tracking]] on a per-link basis.
* To insert dynamic data within your templates, use the macros available in the template editor pop-up.
* If you stick to straightforward HTML with no javascript and no external references (other than images), you shouldn't run into any issues with filtering.
!![[The Daily Telegraph|http://www.telegraph.co.uk/]]
We recently undertook a data protection and list cleaning exercise for the Daily Telegraph for their list of two million readers.
!!Daily Telegraph Fantasy Football
We ran last season's fantasy football mailings to their list of 200,000.
!![[Guitar Amp & Keyboard|http://www.guitarampkeyboard.com/]]
One of Europe's biggest music retailers, we handle their list of 140,000
!![[Woolovers|http://www.woolovers.com/]]
A small business that got some great results:
//"The broadcast of the email started at just after 9pm last night finishing around 10.45pm.
Number of sales for both today and yesterday 142 & 102 just for today. If you look at the same time period for last week we only had 31 orders for the two days. A big success so far. Let’s see how more orders flow through over the weekend. A big thank you to all of you for the help you gave me in setting this fantastic marketing tool up."//
Unsubscribing is the act of removing a subscriber from a mailing list. This could be accomplished by clicking a link, submitting a web form, sending an email to a special address, calling an [[API]] or some combination of those actions. By default, Smartmessages provides a simple unsubscribe form that's linked from every message we send, so you don't need to do anything in order to have full unsubscribe handling.
!!Why support unsubscribes?
You might think that the last thing you want is to make it easy for someone to get off your list, but there are major advantages (quite apart from your legal obligations) to doing so. Put it like this - every time someone unsubscribes, the quality of your list goes up; it contains proportionally less people that don't want to hear from you and more that do. If you persist in sending messages to someone that doesn't want them, they will most probably [[report your message as spam|SpamReports]], which can reduce the deliverability of ALL your other messages, or even get you blacklisted/blocked in the worst case. That's not good. As well as being illegal in many countries, it's also not polite, so do what your subscribers ask.
To comply with data protection laws, including the US ~CAN-SPAM act, It's vital that every message sent to a list includes an unsubscribe link. We always process unsubscribes immediately - if you have a mailshot scheduled to send and someone unsubscribes 1 second before it is sent, they will not be mailed, which also exceeds the requirements of ~CAN-SPAM.
All our standard templates include an unsubscribe link, and you can add one to your own templates in our [[template editor|TemplateUploads]] - if you don't add one, we'll put one in automatically since it's a legal requirement.
!!Standards support
Because links can sometimes go astray or become corrupted within the body of a message by mail/spam filters, we include something called a "~List-Unsubscribe" header in every message, as described [[here|http://www.list-unsubscribe.com/]] and in the [[RFC 2369|http://www.faqs.org/rfcs/rfc2369.html]] standard. These provide a simple and consistent way for subscribers to remove themselves from your lists, are much more likely to make it through filters intact, and are supported by an increasing number of the major email services, most notably Windows Live Mail, Gmail and Yahoo!
!!Who has unsubscribed?
You can get a list of users that have unsubscribed from each of your lists on our contacts page - just click the 'Download Unsubscribes' button to get a simple CSV format list. If you maintain a list of subscribers outside of Smartmessages, you can use that list to remove those that have unsubscribed.
When someone unsubscribes from a list, we automatically [[suppress|SuppressionLists]] them from uploads to that list so that they do not get inadvertently re-subscribed against their wishes.
!!Unsubscribe processing options
There are several options available for handling unsubscribes in Smartmessages:
*Use our standard unsubscribe links and forms
*Use our standard link and forms customised with with your own [[branding|Branding]]
*Build your own unsubscribe form (see below for details) and let us process it
*Build your own form, process it youself (for example so you can add an unsubscribe survey), and notify us of the unsubscribe via our form processor or an API call.
!!Custom unsubscribe forms
In a similar way to [[automating subscribes|Subscribing]], you can automate our unsubscribe forms too. Many of our customers want to use their own web forms to submit information to our unsubscribe processor, and that's also easy to do - just point your form's action at https://www.smartmessages.net/unsubscribe.php. This approach is much simpler than using our API.
The form processor can make use of the following parameters (bold fields are required):
*''//command//'' - the value 'unsubscribe'
*''//emailaddress//'' - the email address of the new subscriber
*''//mlid//'' - the ID of the mailing list they are unsubscribing from (this is the value that's visible in the "link" links on the contacts page)
*//redirect// - a URL to go to after processing the subscription request instead of displaying our own default page (don't forget to ~URL-encode it)
*//json// - any value - its presence will indicate that you want a response in a JSON format (e.g. you're unsubscribing from ~JavaScript)
By building your own form and providing a redirect URL, you can use our unsubscribe processor completely transparently and invisibly to your users. If you specify a redirect URL, you will receive an HTTP GET request to that URL containing your submitted parameters (so you can process it further - for example remove it from your own contact database) along with additional information about the success (or not) of the subscription request in a 'statuscode' parameter. This has the following meanings:
*1 - Success
*3 - Mailing list not found
*5 - Missing required parameters
*6 - Already unsubscribed
*7 - Already globally unsubscribed
*8 - Placeholder link followed
*9 - Invalid email address
The JSON output also includes a 'success' value that is true for codes 1, 6 and 7, false for others, along with a 'message' value containing a plain-text explanation of the response.
Here's a minimal example form that you can adapt:
{{{
<form action="https://www.smartmessages.net/unsubscribe.php" method="post">
<p>
<input type="hidden" name="command" value="unsubscribe" />
<input type="hidden" name="mlid" value="<insert your mailing list ID here>" />
Email address: <input type="text" name="emailaddress" />
<input type="submit" name="Unsubscribe" />
</p>
</form>
}}}
Note that if your account has global unsubscribes turned on, it doesn't matter which mailing list ID you use, so long as it's one of yours.
!!Further automation
It's possible to change the ordering of this process. If you handle the request yourself, you can then make a behind-the-scenes request to our processor to tell us about it. We recommend that you make use of the json parameter in that case as it will provide the quickest and simplest response.
To upload a list click on the ''contacts'' tab. [IMG[images/contacts.gif]]
Choose ''the manage'' button: [IMG[images/manage1.gif]]
Scroll down the page to Mailing list actions, there are 6 choices:
[IMG[images/upload.gif]]
Choose the ''Upload a list'' tab
[IMG[images/upload2.gif]]
When uploading a list there are some options.
!!Source
It's important to be able to keep track of where the email addresses in your account came from, so when you fill this field in, anyone who is added to a list will have this note attached to their history.
!!Overwrite existing data
When you add someone to a list, there are three scenarios:
* They are already on this list
* They are not on this list, but they are on other lists of yours, or have been in the past
* They are new, and you've never seen them before
The first case is simple - we do nothing, leaving their data untouched. In the second case we add them to this list, but don't touch any of their other data. In the third case, we simply add all the data you upload attached to the new address.
Now imagine that the data that is already in the system is wrong, or outdated and you want to replace what's there. For example if you have live data in custom fields that is used every week, you would want to check this box, similarly if you uploaded data that was incorrect (e.g. you got firstname and lastname the wrong way around).
!!Replace existing list
Normally a list upload only adds to a list. If you check this box, anyone not included in the list you upload will be removed. So if there are 10,000 subscribers on this list and you upload a list of 10 new ones and check this box, you will end up with a list with only 10 people on it - so tread carefully!
!!First line contains field names
Checking this box means that the import expects the first line of the CSV file to be a list of the column names, such as first name, email address, etc
!!Upload history
Lists are uploaded from a queue, so the upload history gives feedback of the status and once uploaded, information on the list.
[IMG[images/uphistory.gif]]
Roll over the column names on the live system to have an explanation of the finer detail.
When you send a mailshot, you can include a link to a web version of it (using the {{{[[webversion_url]]}}} or {{{[[webversion_link]]}}} [[template tags|TemplateUploads]]), typically placed at the very top of your message as a simple text link, that will open in a recipient's web browser instead of their email program. This is commonly used by recipients for several main reasons:
#The message they received has been corrupted/rewritten/filtered in some way, rendering it unreadable or degraded in some way.
#They have images turned off and don't want to turn them on to view images (usually for security or privacy reasons)
#Their email program doesn't support HTML, e.g. it's on an older mobile phone, or a text-only email program such as Eudora or Mutt.
All of these have one thing in common - when the recipient looks at your message, they are already seeing a degraded version in one way or another; the web version link is thus provided as a last-ditch attempt to help those recipients to see what you intended.
The first of these is common if you're sending to business customers with outsourced email systems and web-mail clients such as gmail, both of which are often heavy-handed with their mail filtering, sometimes to the point of completely destroying messages. Ideally messages would not get corrupted, but they do (in ways we have no control over), so we need to be defensive and put additional effort into making sure that these links work when others might not. The biggest factor in URL breakage is length. Many breakages are down to ~URLs being too long - and in a environment of multiply-nested ad tracker links with large, messy parameter lists (I'm looking at you, tradedoubler!), these can often get to several hundred characters long. So we keep web version ~URLs as short as possible. The biggest thing we typically add to a URL is a user identifier, and by removing that we save up to 40 characters - and when a length limit may be less than 72 characters, that's enough to make the difference.
Many users fall into the second group because that's teh default behaviour of most email clients, whether desktop, web or mobile. Users that are already sufficiently paranoid about security and privacy will also be fully aware of identifiers in ~URLs, so will not click a link with obvious embedded data in anyway, even if it would provide a better looking result.
The third reason probably has a lot of overlap with the second - many privacy advocates are also keen users of text-only mail programs - but many such users actively prefer text-only programs as they are significantly smaller and faster than the likes of Outlook. It's also another reason to make sure you produce a properly formatted plain-text version of your template.
There are other factors too: by serving a static, generic web version of your mailshot we can dramatically improve performance, probably by a factor of over 100. Our re-usable templates are a big feature that's unique to us - but what happens if you change your template after you've sent it (perhaps when preparing for your next send)? By keeping a 'frozen' copy of the mailshot, we avoid that problem, and it also acts as a kind of audit on exactly what was sent - if someone makes a mistake there's no covering it up!
We'd estimate that typically less than 1% of recpients are affected by these factors, so we're already talking about a very small proportion of recipients - a recent analysis showed around 1 in 500 users who opened the messages used the web version, which only accounted for about 1 in 20000 of the overall mailing. Of course you want to catch them all, but it's important to keep some perspective - do you consider it more important that someone sees your message, or that you know that they have done so, when trying to keep that connection may mean they don't see it at all? The time and effort required to chase down these last few people is much better spent on the probably >75% that don't log any activity at all!
For all of these reasons, in common with most other email marketing services, we don't do personalisation of web versions by default in an effort to keep the links working, avoid exposing personal data, and avoid turning away otherwise perfectly good customers.
!!Personalised web versions
Despite the above, we do provide the ability to override this behaviour in your own templates. Normally we use the equivalent of the generic {{{[[webversion_link]]}}} template tag; there are alternatives called {{{[[webversion_link_personal]]}}} and {{{[[webversion_url_personal]]}}} which will provide a link or ~URL to a fully personalised web version, however you should only use this with the full understanding that:
* You may potentially expose personal data, at your own risk
* It's much less likely to work
* Performance will be reduced
* Privacy-aware recipients are less likely to use it
* You can be defeating the entire point of having a web version at all
In short ''we strongly recommend you do not do this''.