
Product
Introducing Tier 1 Reachability: Precision CVE Triage for Enterprise Teams
Socket’s new Tier 1 Reachability filters out up to 80% of irrelevant CVEs, so security teams can focus on the vulnerabilities that matter.
layer-webhooks-services-nexmo
Advanced tools
This repository contains a service that SMS-notifies users of your Layer Applications of unread messages. This repository requires some configuration to work. Users can reply to these SMS messages and have the messages appear in the Conversation. Different nexmo phone numbers are used to send unread notifications so that a given thread in an SMS UI represents a single Conversation.
The following actions are needed:
For a given user, each nexmo phone number represents a single Layer Conversation. Different users will have different mappings between nexmo numbers and Layer Conversations. A nexmo phone number's link to a Conversation will expire (default is that it expires one week after the last time it was used by this Conversation), at which time the nexmo number can be used for a different Conversation.
If all available nexmo phone numbers have been used by a given user, then additional Conversations simply won't send SMS messages for unread messages.
The quantity of nexmo phone numbers needed for a specific user is the number of Conversations you expect that user to be engaged in within a given week. This number will presumably vary among your users, but if you set your quantity of numbers to 2 standard deviations above the average number of Conversations, the number of missed notifications should be pretty small.
Layer's Webhooks do not provide the recipient's phone number, only their userId. In order to send them an SMS, we will need to get their phone number. The default behavior is to automatically get the number from the Layer's Identities service; however, this only works if you've actually registered your user's phone number there.
If you are not using the Layer Identities service and putting phone numbers there, then provide a identities
function when configuring this module. The identities
function should return a User Object. Your User Object should provide name
and phone
fields; other custom fields can be added and used from your templates.
function myGetIdentity(userId, callback) {
// Lookup in a database or query a web service to get details of this user
doLookup(userId, function(err, result) {
callback(error, {
phone: result.cellnumber,
name: result.first_name + ' ' + result.last_name,
misc: result.favorite_color
});
});
}
require('layer-webhooks-service-nexmo')({
identities: myGetIdentity,
...
});
Templates use Underscore JS Templates. The SMS Message sent to notifiy users of unread messages defaults to:
<%= sender.name =>: <%= text %>
But you can configure this module with a customer template
parameter.
Templates should expect to run on a Message Object as defined by the Layer Webhooks Docs:
{
"id": "layer:///messages/940de862-3c96-11e4-baad-164230d1df67",
"url": "https://api.layer.com/apps/082d4684-0992-11e5-a6c0-1697f925ec7b/messages/940de862-3c96-11e4-baad-164230d1df67",
"conversation": {
"id": "layer:///conversations/e67b5da2-95ca-40c4-bfc5-a2a8baaeb50f",
"url": "https://api.layer.com/apps/082d4684-0992-11e5-a6c0-1697f925ec7b/conversations/e67b5da2-95ca-40c4-bfc5-a2a8baaeb50f"
},
"parts": [
{
"id": "layer:///messages/940de862-3c96-11e4-baad-164230d1df67/parts/0",
"mime_type": "text/plain",
"body": "This is the message."
},
{
"mime_type": "image/png",
"id": "layer:///messages/940de862-3c96-11e4-baad-164230d1df67/parts/1",
"content": {
"id": "layer:///content/940de862-3c96-11e4-baad-164230d1df60",
"download_url": "http://google-testbucket.storage.googleapis.com/some/download/path",
"expiration": "2014-09-09T04:44:47+00:00",
"refresh_url": "https://api.layer.com/apps/082d4684-0992-11e5-a6c0-1697f925ec7b/content/7a0aefb8-3c97-11e4-baad-164230d1df60",
"size": 172114124
}
}
],
"sent_at": "2014-09-09T04:44:47+00:00",
"recipient_status": {
"12345": "read",
"999": "sent",
"111": "sent"
}
}
In addition, the following properties will be added:
sender
Object: This will be the object you provide via an identities
call on the sender of this Message.recipient
Object: This will be the object you provide via an identities
call on a single recipienttext
String: This will extract any text/plain parts and concatenate their body's together into an easily accessed stringA custom template might look like:
{
"template": "<%= sender.name %> says <%= text %>",
}
The following parameters are supported:
Name | Required | Description |
---|---|---|
layer | Yes | An object for organizing all of your Layer Service configurations |
layer.webhookServices | Yes | An instance of Webhook Service Client |
layer.client | Yes | An instance of Layer Platform API Client |
layer.secret | Yes | Any unique string that nobody outside your company knows; used to validate webhook requests |
server | Yes | An object for organizing all of your web server's configurations |
server.app | Yes | An express server instance, listening using https protocol. |
server.url | Yes | URL that this server is on; omit paths. Used in combination with the path property to register your webhook. |
server.redis | Yes | An instance of a redis server |
server.layerPath | No | Path that the express server will listen on for Layer Webhooks; defaults to "nexmo-new-message" |
server.nexmoPath | No | Path that the express server will listen on for Nexmo Webhooks; defaults to "nexmo-new-sms" |
nexmo | Yes | An object for organizing all of your Nexmo Service configurations |
nexmo.key | Yes | Your nexmo API key |
nexmo.secret | Yes | Your nexmo API Secret |
nexmo.phoneNumbers | Yes | Array of phone numbers (strings) that you have purchased through Nexmo and will use to SMS your users |
delay | Yes | How long to wait before checking for unread messages and notifiying users. Delays can be configured using a number representing miliseconds, or a string such as '10 minutes' or other strings parsable by ms |
identities | No | Function that looks up a user's info and returns the results via callback |
template | No | Template string for formatting the SMS message |
name | No | Name to assign the webhook. |
reportOnStatus | No | Array of user states that justify notification; ['sent'] (Message could not be delivered yet); ['sent', 'delivered'] (Message is undelivered OR simply unread); ['delivered'] (Message is delivered but not read). Default is ['sent', 'delivered'] |
numberExpirationTime | No | How long to wait before inactivity causes the link between a Conversation and nexmo number for a given user to expire. Default is 1 week. Delays can be configured using a number representing miliseconds, or a string such as '10 minutes' or other strings parsable by ms |
introduceConversation | No | Asynchronous callback for introducing a Conversation before showing Messages from that Conversation. |
The first time you get a text about an unread message, you may want to identify what conversation this came from. Furthermore, the link between a Conversation and a nexmo phone number is occasionally broken, and the number reused. In which case, it may be significant to tell the user that "The Conversation is now about X".
Lets take two scenarios:
Your Conversations don't need titles, the only thing you need to know is WHO the Conversation is with. In this case, you may not need a template of <%= sender.name %>: <%= text %>
, and may want the following:
require('layer-webhooks-services-nexmo')({
...,
template: '<%= text %>',
introduceConversation: function(message, callback) {
callback(null, message.sender.name + ' says:');
}
});
Result:
You have multiple users in your Conversations, so you may stick with the default template of <%= sender.name %>: <%= text %>
, but you DO want to make sure your user knows WHICH Conversation this is happening in (and therefore, which Conversation this user's replies will go to).
require('layer-webhooks-services-nexmo')({
...,
introduceConversation: function(message, callback) {
layerClient.conversations.get(message.conversation.id, function(err, res) {
if (err) {
console.error('introduceConversation failed to get Conversation: ', err);
return callback(err);
}
callback(null, 'You have new messages in Conversation "' + res.body.metadata.conversationName + '"');
});
}
});
// Setup Redis and kue
var redis = require('redis').createClient(process.env.REDIS_URL);
var queue = require('kue').createQueue({
jobEvents: false,
redis: process.env.REDIS_URL
});
// Setup the Layer Platform API
var LayerClient = require('layer-api');
var layerClient = new LayerClient({
token: process.env.LAYER_BEARER_TOKEN,
appId: process.env.LAYER_APP_ID,
});
// Setup the Layer Webhooks Service
var LayerWebhooks = require('layer-webhooks-services');
var webhookServices = new LayerWebhooks({
token: process.env.LAYER_BEARER_TOKEN,
appId: process.env.LAYER_APP_ID,
redis: redis
});
function introduceConversation(message, callback) {
layerClient.conversations.get(message.conversation.id, function(err, res) {
if (err) {
console.error('introduceConversation failed to get Conversation: ', err);
return callback(err);
}
callback(null, 'You have new messages in Conversation "' + res.body.metadata.conversationName + '"');
});
}
secureExpressApp.listen(PORT, function() {
require('layer-webhooks-service-nexmo')({
introduceConversation: introduceConversation,
layer: {
webhookServices: webhookServices,
client: layerClient,
secret: 'Lord of the Mog has jammed your radar'
},
server: {
url: 'https://mywebhooks.mycompany.com',
app: app,
redis: redis
},
nexmo: {
key: process.env.NEXMO_KEY,
secret: process.env.NEXMO_SECRET,
phoneNumbers: process.env.NEXMO_NUMBERS.split(/\s*,\s*/)
}
});
});
This section is for those who want to understand what goes on under the hood.
A User Record contains a mapping between Layer Conversation IDs and Nexmo Phone Numbers:
{
"conversationId1": {
"phone": "phone1",
"expires": "d1"
},
"conversationId2": {
"phone": "phone2",
"expires": "d2"
},
"conversationId3": {
"phone": "phone3",
"expires": "d3"
}
}
Receiving Layer Messages:
receipts
service notifies us whenever a Message has gone unread and requires notification of UserA.expires
field to be one week after now.Receiving SMS Messages
expires
field to be one week after now.FAQs
Nexmo integration triggered by Layer Webhooks
We found that layer-webhooks-services-nexmo demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket’s new Tier 1 Reachability filters out up to 80% of irrelevant CVEs, so security teams can focus on the vulnerabilities that matter.
Research
/Security News
Ongoing npm supply chain attack spreads to DuckDB: multiple packages compromised with the same wallet-drainer malware.
Security News
The MCP Steering Committee has launched the official MCP Registry in preview, a central hub for discovering and publishing MCP servers.