Skip to content

Instantly share code, notes, and snippets.

@snikch
Last active December 29, 2015 03:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snikch/7606139 to your computer and use it in GitHub Desktop.
Save snikch/7606139 to your computer and use it in GitHub Desktop.
Apostle.io integration

Apostle.io Client Integration Guide

Apostle.io is a web service for managing web app generated emails. This guide describes the requirements for writing a Client Library in any language.

Please check http://github.com/apostle to see if there is a client library for your language already.

This gist lives at https://gist.github.com/snikch/7606139

Style Guide

While this guide describes a style of writing a library in a certain way, this style may not be appropriate for any individual language. It's up to the author to decide if the style is appropriate for the language or if there is a more appropriate API style.

Overview

Delivery works by posting a JSON body to https://deliver.apostle.io. A json header must be supplied Content-Type: application/json.

The JSON can contain multiple recipients, each sending a unique template. See the json files in this gist for more examples.

Authentication

An Apostle.io “domain key” is required to send emails on behalf of an Apostle.io “domain”. This must be provided as an authentication header Authorization: Bearer DOMAIN_KEY.

An invalid domain key will return a 401 response.

Configuration

A global singleton with configuration available should be made available. The minimum values required are domain_key and delivery_host. Sane defaults should be provided. The default delivery_host is http://deliver.apostle.io, and the domain_key should default to a configuration method that the language provides. For example, in Ruby, the convention is to make configuration variables available via the ENV variable, so it defaults to ENV['DOMAIN_KEY].

# Setting a new domain key value
Apostle.domain_key = "My domain key"

Note: The delivery_host will not be changed in almost all cases. This is provided so that clients can be tested against non production Apostle.io delivery endpoints for testing and QA.

Note: For testing configuration, please see the testing section (tl;dr add a deliver bool setting so that real delivery can be turned off in test suites).

The Mail Class

A mail class should be defined to represent an email to a single recipient. This mail object should be able to accept any data, however the minimum requirement is the template id and email address.

You should be able to add any value to the mail object and have it added to an object called data. The only attributes that are reserved and should be at the root level are email, from, headers, layout_id, name and reply_to. This maps to the json that gets sent to the delivery endpoint.

See all_attributes.json in this gist for an example.

To deliver a mail object, a method deliver should be defined. In languages where there is a convention for methods that are 'hard' rather than 'soft' (i.e. they raise exceptions rather than returning false on failure) then both methods should be defined. For example, the Ruby client has both deliver and deliver! methods.

The Queue Class

A queue class should be provided to send multiple mail objects. Note: the deliver method should define a queue with itself on it, and send via the queue's deliver method. This saves having to write delivery code in multiple places.

A queue should be able to be instantiated, have mail objects added to it, and then get those mail objects delivered as a single JSON object to Apostle.io.

See multiple_recipients.json for a multiple recipients example.

A queue should validate only the minimum requirements for a mail object to be valid; the template_id and email address. If any email on the queue is invalid, the entire queue should not be sent.

The manner in which you access the validation errors will most likely depend on the language, and how that sort of information is usually passed. For example, in Ruby the information is provided via a #results method, whereas in PHP the information is provided by passing a reference object to the deliver method that gets populated.

Ruby

queue.deliver
=> false

queue.results
=> {
    :valid=>[#<Apostle::Mail:0x007fcee5ab2550>],
    :invalid=>[#<Apostle::Mail:0x007fcee5ab23c0>]
}

queue.results[:invalid].first._exception
=> #<Apostle::DeliveryError @message="No recipient provided">

PHP

echo $queue->deliver($failures);
// false

echo count($failures);
// 2

echo $failures[0]->deliveryError();
// "No email provided"

echo $failures[1]->deliveryError();
// "No template provided"

API Response

All responses are JSON encoded. If an incorrect request is made, an error message describing the issue will be supplied

{
    "status": "error",
    "message": "Json Invalid: EOF",
    "request_data": ""
}

Successful responses will respond with a success message

{
    "status": "queued"
}

Testing

Unit tests are required before a client library will be accepted into the official repository.

To avoid integrators accidentally sending live emails in their test suite, you should provide a deliver configuration flag. While defaulting to true, if set to false it should not attempt to post to the delivery endpoint, and should always return a true response.

Getting Help

Anyone integrating with Apostle.io should reach out to Mal Curtis, mal@apostle.io, for help – I'm always happy to contribute and answer questions where I can.

{"recipients":
{
"mal@mal.co.nz": {
"template_id": "welcome",
"name": "Mal Curtis (optional)",
"from": "Optional from override",
"headers": { "Optional": "Headers Object" },
"layout_id": "Optional layout override slug",
"reply_to": "Optional reply to address",
"data": {
"Any": "Other data that the template might need",
"for_example": "Orders may have an array of items",
"items": [
{ "name": "Widget Wheel", "price": "$5", "quantity": 2 },
{ "name": "Widget Tyre", "price": "$4", "quantity": 2 },
{ "name": "Widget Seat", "price": "$25", "quantity": 1 },
]
}
}
}
{"recipients":
{
"mal@mal.co.nz": {"template_id": "welcome", "name": "Mal Curtis"},
"mal@apostle.io": {"template_id": "another-template", "name": "Mal Curtis"}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment