Airhorn
Notification Platform (mobile push, webhook, smtp, sms).
With Airhorn, you could send notifications to various channels and users.
Send a message to someone via Airhorn API like this:
POST /send
{
"channel": "email",
"from": "me@example.com",
"to": "michael@example.com",
"text": "Hey, Michael! Your coffee is ready!"
}
Create some user profiles and use templates to automate your notifications even more:
Step: 1. Create user profile
PUT /profiles/michael
{
"first_name": "Michael",
"last_name": "Evans",
"email": "michael@example.com",
"phone": "+1100200300400",
"urban_airship_device_id": {
"ios_channel": "b8f9b663-0a3b-cf45-587a-be880946e881"
}
}
(Airhorn allows you to manage your notification templates in your git repository. If configured, it will sync with this repo and pull latest changes automatically. See "Managing Templates in Git" below)
Step 2. Create notification template
PUT /templates/coffee
{
"items": [
{
"props": {
"subject": "Coffee!",
"html": "Hey, {{first_name}}! Your coffee is ready – pick it at box <b>{{coffeebox}}</b>."
}
}
]
}
Step 3.. Send notification
POST /send/email
{
"from": "me@example.com",
"profile_id": ["michael"],
"template_id": "coffee",
"tags": ["t1", "t2"]
"coffeebox": "03 at room 14"
}
An email will be sent: 'Hey, Michael! Your coffee is ready – pick it at box 03 at room 14'.
You can group users using tags:
Create tags and subscribe to them like this.
POST /tags
{
"id": "coffee-lovers",
"profile_id": ["michael", "jane", "joe"]
}
Add a new user(s) to existing tag.
PUT /tags/coffee-lovers/john
Delete a tag.
DELETE /tags/coffee-lovers
Send notification to multiple users at once:
POST /send/sms
{
"profile_id": ["michael", "jane", "joe"],
"coffeebox": "03 at room 14",
"text": "Hey, {{first_name}}! Your coffee is ready – pick it at box {{coffeebox}}"
}
and / or
{
"tags": ["coffee-lovers"],
"coffeebox": "03 at room 14",
"text": "Hey, {{first_name}}! Your coffee is ready – pick it at box {{coffeebox}}"
}
Getting started
Requires Node.js v5.9.1 or higher.
$ git clone https://github.com/jaredwray/airhorn.git
$ cd airhorn
$ npm install
$ AIRHORN_HTTPS_DISABLED=true npm start
Airhorn would fire up API at port 8080 (or provide PORT environment variable to change this default).
Airhorn requires HTTPS by default. All requests done to an HTTP port, are redirected to HTTPS (if no HTTPS_PORT is provided, default is 8081). You can disable HTTPS by setting up AIRHORN_HTTPS_DISABLED option to "true".
Configuration
At minimum, Airhorn respects following environment variables:
- PORT default: 8080 – HTTP port to bind API
- HTTPS_PORT default: 8081 - HTTPS port to bind API
- AIRHORN_CONFIG default: [project root]/airhorn.json – override path to a configuration file (see Advanced configuration)
Simple configuration
With default config file, Airhorn would respect following environment variables:
- AIRHORN_REDIS_HOST default: 127.0.0.1 – hostname for Redis database
- AIRHORN_REDIS_PORT default: 6379 – port for Redis database
- AIRHORN_REDIS_PASSWORD default: unset - Redis password
- AIRHORN_TEMPLATES_REPOSITORY default: unset - git origin for templates data source (see Managing Templates with Git)
- AIRHORN_TEMPLATES_REPOSITORY_BRANCH default: master – override branch for AIRHORN_TEMPLATES_REPOSITORY
- AIRHORN_CONFIG_EMAIL, AIRHORN_CONFIG_PUSH, AIRHORN_CONFIG_SMS, AIRHORN_CONFIG_WEBHOOK default: unset - configure available providers (see Providers).
- AIRHORN_HTTPS_DISABLED default: unset - disabling https server. By default https is enabled
- AIRHORN_HTTPS_KEY_PATH default: [project root]/key.pem - path to your https key
- AIRHORN_HTTPS_CERTIFICATE_PATH default: [project root]/cert.pem - path to your https certificate
- AIRHORN_ACCESS_TOKEN required – master access token to restrict API access
- AIRHORN_ACCESS_SEND_TOKEN default: unset - optional token to restrict access to /send endpoints
- AIRHORN_ACCESS_INVALIDATE_TOKEN default: unset - optional token to restrict access to /invalidate endpoint
Advanced configuration
Airhorn is configured via a config file (airhorn.json by default). Smart interpolation is enabled for config file, so you can reference environment variables from it, which enables flexible and easy configuration with various deployment options.
Default config file is:
{
"git": {
"origin": "${AIRHORN_TEMPLATES_REPOSITORY}",
"branch": "${AIRHORN_TEMPLATES_REPOSITORY_BRANCH}"
},
"dataStore": {
"redis": {
"host": "${AIRHORN_REDIS_HOST}",
"port": "${AIRHORN_REDIS_PORT}",
"password": "${AIRHORN_REDIS_PASSWORD}"
}
},
"providers": {
"email": "${AIRHORN_CONFIG_EMAIL}",
"push": "${AIRHORN_CONFIG_PUSH}",
"sms": "${AIRHORN_CONFIG_SMS}",
"webhook": "${AIRHORN_CONFIG_WEBHOOK}"
},
"https": {
"keyPath": "${AIRHORN_HTTPS_KEY_PATH}",
"certificatePath": "${AIRHORN_HTTPS_CERTIFICATE_PATH}",
"disabled": "${AIRHORN_HTTPS_DISABLED}"
},
"auth": {
"type": "static_token",
"token": "${AIRHORN_ACCESS_TOKEN}",
"send_token": "${AIRHORN_ACCESS_SEND_TOKEN}",
"invalidate_token": "${AIRHORN_ACCESS_INVALIDATE_TOKEN}"
}
}
- Airhorn will expand expressions like ${ENV_VARIABLE} by replacing it with respective environment variable value. If config there expects an object, environment variable value will be treated as a serialized JSON, and parsed transparently.
- Expressions interpolation is done recursively, so you can have AIRHORN_REDIS_HOST reference another environment variable (which your hosting provider probably sets up for you)
- For more complex scenarios, you can use key paths i.e. ${REDIS_CONFIG.primary.hostname} which will expect that REDIS_CONFIG variable contains serialized JSON.
- If Airhorn spots an array while resolving key path, it will throw an error unless this array contains exactly one element. In such case, it will transparently replace whole array with it's single element.
Providers
Under the hood notification channels rely on external providers (i.e. Twilio for SMS or UrbanAirship for Push Notifications) . Each channel will use only one primary configured provider.
If two providers are available for a single channel, you can choose it in your provider configuration:
AIRHORN_CONFIG_SMS environment variable
{
"provider": "twilio",
// twilio-specific configuration parameters also go here
}
There is a set of supported providers and examples of their settings:
Mobile push: Urban Airship
Sms: Twilio
Email: smtp, Amazon SES
Webhook: webhook
Urban Airship
// airhorn.json
{
"push": {
"apiKey": "your-api-key",
"apiSecretKey": "your-secret-api-key",
"apiMasterKey": "your-master-key"
},
...
}
// message example
{
audience: "all",
notification: {
alert: "Hello!" // if custom text isn't specified we will use template
},
device_types: "all"
}
Read more how to customize your message
here.
Twilio
// airhorn.json
{
"sms": {
"authToken": "your-auth-token",
"accountSid": "your-account-sid",
"sendingNumber": "your-sending-number
},
...
}
Read more about Twilio Account Settings here.
// message example
{
text: "Hello!", // if custom text isn't specified we will use template
phone: "+19995550123" // phone number of receiver in international format
}
About formatting international phone numbers here.
smtp
// airhorn.json
{
"email": {
"provider": "smtp",
"smtp_service": "your-service",
"smtp_email": "your-email-address",
"smtp_pass": "your-password"
},
...
}
Read more about supported services here.
// message example
{
from: "your-email-address",
to: "receiver-email-address",
reply_to: "email-for-user-reply", // optional
subject: "message-subject", // optional
text: "Hello!", // will be ignored if 'html' section is present
html: "<h2>Hello!</h2>" // if custom text isn't specified we will use template
}
Amazon Simple Email Service
// airhorn.json
{
"email": {
"provider": "aws_ses",
"aws_accessKeyId": "your-aws-access-key",
"aws_secretAccessKey": "your-aws-secret-key",
"aws_region": "your-aws-region",
"aws_email": "your-email-address"
},
...
}
Use message example from smtp section above.
Webhook
// message example
{
// Your custom properties
username: "Bill",
status: "enabled"
// Require
webhook_body_type: "request-body-type", // "form" | "querystring" | "json"
webhook_url: "http://example.com/endpoint/{{status}}",
webhook_body: {
text: "Hello {{username}}",
webhook_html: "<div>World</div>", // auto from Git Templates module
webhook_plain_text: "World" // auto from Git Templates module
},
// Optional
webhook_http_method: "request-http-method", // default POST
webhook_headers: {
header: "value"
}
auth: {
user: "your-user-account",
pass: "your-password",
bearer: "your-auth-token"
}
}
Body types
Method as the webhook_body in request will be sent to your API.
- form Send URL-encoded forms.
- querystring Query params in URI
- json JSON in request body
Git templates
Allows to use your own templates from a git repository. To create a templates using html and hogan.js. Depending on its capabilities providers send html or convert to plain text.
// airhorn.json
"git": {
"origin": "https://github.com/yourrepo",
"branch": "master"
"clonePath": __dirname
}
// refresh
POST /invalidate
- Git Template adds a properties webhook_plain_text and webhook_html to the webhook_body object.
- To pull latest changes send POST request to /invalidate endpoint. Usually this is done via GitHub commit hook.
- Template files must have the extension .html and use mustache templating specification (based on hogan.js)
- The repository can contain an arbitrary folder structure, but relative file path will be translated to template id. For example, file
somefolder/basic-template.html
will become a template with id somefolder/basic-template
.
Auth providers
Airhorn is built with pluggable authentication and authorization providers, while currently there is only one shipped provider which grants API access to ones with a static Bearer token.
In future other providers can be added – please feel free to issue a feature request if you need one.
Auth provider is mandatory.
Authenticating with a static token
Available auth providers: static_token (default) or disabled to allow full public access (unsafe, be warned).
// airhorn.json
"auth": {
"type": "static_token", // required
"token": "value", // required
"send_token": "value",
"invalidate_token": "value"
}
// Examples
// URI query
/profiles/1?access_token=value
// Body
{
"access_token": "value"
}
// Header
Authorization: Bearer <token>
static_token provider parameters:
- token – token for all requests
- send_token – token for /send endpoint
- invalidate_token – token for /invalidate endpoint
If send_token or invalidate_token not provided will using token property.
REST API Reference
POST and PUT are synonyms for Airhorn API. Everywhere you see POST you can also use PUT and vice-versa.
Profiles
GET /profiles
Get a list of all profiles available in the system. Includes only ids.
GET /profiles/:id
Get a profile info. Will append "tags" array to profile data to include a list of tags attached to this profile.
POST /profiles or PUT /profiles/:id
Create or update a profile. Profile id will be taken from either an URL or request payload (in an id field). If no id is present, random UUID v4 will be generated.
"tags" property will be ignored. To update tag assignments use /tags endpoints.
DELETE /profiles/:id
Deletes a profile.
Templates
GET /templates
Get a list of all templates available in the system. Includes only ids with available channels and locales.
GET /templates/:id
Get full info for a template.
POST /templates or PUT /templates/:id
Create or update a template. Template id will be taken from either an URL or request payload (in an id field). If no id is present, random UUID v4 will be generated.
The system will not allow you to update a template which was imported from a Git repository. To update such templates, update the repository and import changes.
POST template/:id/render
Ability to view a rendered template by template :id . Properties "locale"
and "channel"
are mandatory.
POST template/client_greeting/render
{
"locale": "en",
"channel": "sms",
"props": {
"client_full_name": "Michael Smith"
}
}
The key "props"
contains the properties used by the template. If not specified they will be ignored.
DELETE /profiles/:id
Deletes a profile.
Tags
Tags are a sets of profiles for convenient group notifications.
GET /tags
Get a list of all tags available.
GET /tags
HTTP 200
[ { "id": "tagone"}, {"id": "tagtwo"}, {"id": "tagthree"} ]
GET /tags/:id
Get detailed information for a single tag.
GET /tags/tagone
HTTP 200 OK
{
"id": "tagone",
"profile_id": ["michael", "janus"]
}
or HTTP 404 Not Found
POST /tags or PUT /tags/:id
Create or update a tag. Tag id will be taken from either an URL or request payload (in an id field). If no id is present, random UUID v4 will be generated.
PUT /tags/fourthtag
{
"profile_id": ["michael"]
}
or same:
PUT /tags
{
"id": "fourthtag",
"profile_id": ["michael"]
}
HTTP 200 OK or HTTP 201 Created
{
"id": "fourthtag",
"profile_id": ["michael"]
}
DELETE /tags/:id
Delete a specific tag.
DELETE /tags/fourthtag
HTTP 204 No Content or HTTP 404 Not Found
PUT /tags/:tagId/:profileId
Add a profile to a tag.
PUT /tags/fourthtag/janus
HTTP 200 OK
{
"id": "fourthtag",
"profile_id": ["michael", "janus"]
}
or HTTP 404 Not Found
DELETE /tags/:tagId/:profileId
Delete a profile from a tag.
DELETE /tags/fourthtag/michael
HTTP 200 OK
{
"id": "fourthtag",
"profile_id": []
}
or HTTP 404 Not Found
Send
TODO
POST /send
POST /send/sms
Describe
/describe
endpoint can help you with validating your notifications. It's available in few flavours, functionally the same:
GET /describe?parameterOne=one¶meterTwo=two
or
POST /describe
{
"parameterOne": "one",
"parameterTwo": "two"
}
or
GET /sms/template-id/profile-id
All variants will return a JSON object with following fields:
- ok - a boolean with true if message is valid and complete
- error - a string that will be present if there was something wrong with the notification
- message - JSON object will contain all properties merged