How to send personalized emails to multiple addresses using sendgrid in NodeJS - node.js

I have an array of email addresses and an array of passwords. I want to send each password to its corresponding email address(email at same index) in the body of email. But I don't want to use a loop. Is it possible to use substitution in the email body and then have sendgrid pick a value from the password array for each email address.
I know I can construct a personalizations object like so :
personalizations: {to: [{email: "email1"}], substitutions: {"-pwd-": "pwd1"}}
and then use -pwd- in the body of email.
But to construct this object I again have to use a loop which I don't want.

Haven't tried it out, but according to the SendGrid Docs it should be able.
The given example in the docs (for SendGrid API v3) looks the following way:
{
"personalizations": [
{
"to": [
{
"email": "john#domain.com",
"name": "John"
}
],
"subject": "Example 01",
"substitutions": {
"-name-": "John"
}
},
{
"to": [
{
"email": "jane#domain.com",
"name": "Jane"
}
],
"subject": "Example 02",
"substitutions": {
"-name-": "Jane"
}
},
{
"to": [
{
"email": "matt#domain.com",
"name": "Matt"
}
],
"subject": "Example 03",
"substitutions": {
"-name-": "Matt"
}
}
],
"from": {
"email": "sender#senddomain.com",
"name": "Sender"
},
"reply_to": {
"email": "sender#senddomain.com",
"name": "Sender"
},
"subject": "Example",
"content": [
{
"type": "text/plain",
"value": "Hello -name-,"
},
{
"type": "text/html",
"value": "Hello -name-,"
}
]
}
By using Dynamic Transactional Templates it should work the same way with handlebars.

Related

What is wrong with this javascript regex string.replace?

UPDATE: I've discovered this problem is because of the "stringified" output which contains no spaces or newlines and is different than the original JSON.
Stringified output:
{"eventType":"Delivery","mail":{"timestamp":"2021-08-09T20:41:51.515Z","source":"removed#email.com","sourceArn":"arn:aws:ses:us-east-1:1234567890:identity/removed.com","sendingAccountId":"1234567890","messageId":"1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b","destination":["removedl#email.com"],"headersTruncated":false,"headers":[{"name":"From","value":"removed#email.com"},{"name":"To","value":"removed#email.com"},{"name":"Subject","value":"Test"},{"name":"MIME-Version","value":"1.0"},{"name":"Content-Type","value":"text/plain; charset=UTF-8"},{"name":"Content-Transfer-Encoding","value":"7bit"}],"commonHeaders":{"from":["removed#email.com"],"to":["removed#email.com"],"messageId":"1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b","subject":"Test"},"tags":{"ses:operation":["SendEmail"],"ses:configuration-set":["GenericLog"],"ses:source-ip":["123.123.123.123"],"ses:from-domain":["email.com"],"ses:caller-identity":["user1"],"ses:outgoing-ip":["234.234.234.234"]}},"delivery":{"timestamp":"2021-08-09T20:41:52.880Z","processingTimeMillis":1365,"recipients":["removed#email.com"],"smtpResponse":"250 2.6.0 <1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b#email.amazonses.com> [InternalId=11231247472, Hostname=CO6PR12MB5396.namprd12.prod.outlook.com] 12090 bytes in 0.214, 55.135 KB/sec Queued mail for delivery","reportingMTA":"a48-108.smtp-out.amazonses.com"}}
ORIGINAL:
I want to remove the : from the key names and replace it with a _ in the following JSON. (In the tags section)
{
"eventType": "Delivery",
"mail": {
"timestamp": "2021-08-09T20:41:51.515Z",
"source": "removed#email.com",
"sourceArn": "arn:aws:ses:us-east-1:1234567890:identity/removed.com",
"sendingAccountId": "1234567890",
"messageId": "1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b",
"destination": [
"removedl#email.com"
],
"headersTruncated": false,
"headers": [
{
"name": "From",
"value": "removed#email.com"
},
{
"name": "To",
"value": "removed#email.com"
},
{
"name": "Subject",
"value": "Test"
},
{
"name": "MIME-Version",
"value": "1.0"
},
{
"name": "Content-Type",
"value": "text/plain; charset=UTF-8"
},
{
"name": "Content-Transfer-Encoding",
"value": "7bit"
}
],
"commonHeaders": {
"from": [
"removed#email.com"
],
"to": [
"removed#email.com"
],
"messageId": "1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b",
"subject": "Test"
},
"tags": {
"ses:operation": [
"SendEmail"
],
"ses:configuration-set": [
"GenericLog"
],
"ses:source-ip": [
"123.123.123.123"
],
"ses:from-domain": [
"email.com"
],
"ses:caller-identity": [
"user1"
],
"ses:outgoing-ip": [
"234.234.234.234"
]
}
},
"delivery": {
"timestamp": "2021-08-09T20:41:52.880Z",
"processingTimeMillis": 1365,
"recipients": [
"removed#email.com"
],
"smtpResponse": "250 2.6.0 <1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b#email.amazonses.com> [InternalId=11231247472, Hostname=CO6PR12MB5396.namprd12.prod.outlook.com] 12090 bytes in 0.214, 55.135 KB/sec Queued mail for delivery",
"reportingMTA": "a48-108.smtp-out.amazonses.com"
}
}
I've created a node.js Lambda function that is being called by AWS Kinesis. It processes the above JSON and the following code is being used. I am debugging, so I am simply trying to search and replace the string for testing:
console.log('Loading function');
exports.handler = async (payload, context) => {
payload = JSON.stringify(payload);
payload = payload.replace(/".*:.*":/g, 'replaced');
console.log('Replaced payload:', payload);
};
I've run the regex through multiple code testing sites and it correctly identifies all 6 occurrences of the key names with a : in them. I've even used this tester, and it correctly replaces all 6 strings as expected. But, when it runs on Lambda I get the following output:
Replaced payload: {replaced"a48-108.smtp-out.amazonses.com"}}
Am I missing something obvious or is this some weird thing happening in Lambda?
Note, if I simply output the unmodified payload variable it matches the input JSON, so that is not the issue.
I've got additional code which should complete this task successfully once I solve this mystery. But, if you could provide the code to replace : with _ in the key names, that would be great as well.
Expanding on your update comment:
UPDATE: I've discovered this problem is because of the "stringified" output which contains no spaces or newlines and is different than the original JSON.
You can add the following options to JSON.stringify to properly format the stringified output.
JSON.stringify(payload, null, 4);
Demo:
let payload = {"eventType":"Delivery","mail":{"timestamp":"2021-08-09T20:41:51.515Z","source":"removed#email.com","sourceArn":"arn:aws:ses:us-east-1:1234567890:identity/removed.com","sendingAccountId":"1234567890","messageId":"1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b","destination":["removedl#email.com"],"headersTruncated":false,"headers":[{"name":"From","value":"removed#email.com"},{"name":"To","value":"removed#email.com"},{"name":"Subject","value":"Test"},{"name":"MIME-Version","value":"1.0"},{"name":"Content-Type","value":"text/plain; charset=UTF-8"},{"name":"Content-Transfer-Encoding","value":"7bit"}],"commonHeaders":{"from":["removed#email.com"],"to":["removed#email.com"],"messageId":"1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b","subject":"Test"},"tags":{"ses:operation":["SendEmail"],"ses:configuration-set":["GenericLog"],"ses:source-ip":["123.123.123.123"],"ses:from-domain":["email.com"],"ses:caller-identity":["user1"],"ses:outgoing-ip":["234.234.234.234"]}},"delivery":{"timestamp":"2021-08-09T20:41:52.880Z","processingTimeMillis":1365,"recipients":["removed#email.com"],"smtpResponse":"250 2.6.0 <1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b#email.amazonses.com> [InternalId=11231247472, Hostname=CO6PR12MB5396.namprd12.prod.outlook.com] 12090 bytes in 0.214, 55.135 KB/sec Queued mail for delivery","reportingMTA":"a48-108.smtp-out.amazonses.com"}};
payload = JSON.stringify(payload, null, 4)
payload = payload.replace(/".*:.*":/g, 'replaced');
console.log('Replaced payload:', payload);
Mind that simply using .* means "as many characters as you can match", i.e. it's eager. You can add a ? for lazy matching, as in "match as few as possible".
I suggest using /".*?:.*?":/g since this basically means "get all characters to the closest : and then the characters to the closest ":".
Using a recursive function to map the object instead of using a RegExp on the JSON version:
const data = { "eventType": "Delivery", "mail": { "timestamp": "2021-08-09T20:41:51.515Z", "source": "removed#email.com", "sourceArn": "arn:aws:ses:us-east-1:1234567890:identity/removed.com", "sendingAccountId": "1234567890", "messageId": "1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b", "destination": ["removedl#email.com"], "headersTruncated": false, "headers": [{ "name": "From", "value": "removed#email.com" }, { "name": "To", "value": "removed#email.com" }, { "name": "Subject", "value": "Test" }, { "name": "MIME-Version", "value": "1.0" }, { "name": "Content-Type", "value": "text/plain; charset=UTF-8" }, { "name": "Content-Transfer-Encoding", "value": "7bit" }], "commonHeaders": { "from": ["removed#email.com"], "to": ["removed#email.com"], "messageId": "1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b", "subject": "Test" }, "tags": { "ses:operation": ["SendEmail"], "ses:configuration-set": ["GenericLog"], "ses:source-ip": ["123.123.123.123"], "ses:from-domain": ["email.com"], "ses:caller-identity": ["user1"], "ses:outgoing-ip": ["234.234.234.234"] } }, "delivery": { "timestamp": "2021-08-09T20:41:52.880Z", "processingTimeMillis": 1365, "recipients": ["removed#email.com"], "smtpResponse": "250 2.6.0 <1123bfe-123123b12e12d2c212a-123b12c2e1123-213f312d32c123b#email.amazonses.com> [InternalId=11231247472, Hostname=CO6PR12MB5396.namprd12.prod.outlook.com] 12090 bytes in 0.214, 55.135 KB/sec Queued mail for delivery", "reportingMTA": "a48-108.smtp-out.amazonses.com" } };
function convert(obj) {
if (typeof obj !== 'object') return obj;
if (Array.isArray(obj)) return obj.map(convert);
const result = {};
for(const key in obj) {
result[key.replace(/:/g, '_')] = convert(obj[key]);
}
return result;
}
const converted = convert(data);
console.log(converted);
console.log(JSON.stringify(converted, null, 4));

Adaptive card in teams not workig - REST API

I created a bot (nodejs server) for teams - through bot framework.
I'm trying to send adaptive card that I created through: adaptive cards designer
and I get error :
{"code":"BadArgument","message":"ContentType of an attachment is not set"}
The request body :
{
"type": "message",
"from": {
"id": "xxxxxx"
},
"conversation": {
"id": "xxxxxx"
},
"recipient": {
"id": "xxxxx"
},
"replyToId": "xxxxx",
"text": "some text",
"attachments": [
{
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2",
"body": [
{
"type": "TextBlock",
"text": "some text"
},
{
"type": "Input.Date",
"separator": true
}
]
}
]
}
I would appreciate help
When adding attachments you'll want to set the contentType and content properties of the attachment object.
https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#attachment-object
{
"type": "message",
"from": {
"id": "xxxxxx"
},
"conversation": {
"id": "xxxxxx"
},
"recipient": {
"id": "xxxxx"
},
"replyToId": "xxxxx",
"text": "some text",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"content": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.2",
"body": [
{
"type": "TextBlock",
"text": "some text"
},
{
"type": "Input.Date",
"separator": true
}
]
}
}
]
}
As per the comments above, this is a pro-active message, but using the Bot Framework libraries are a much better approach than trying to call the bot endpoints directly (I wondered if, by "REST", you meant the Bot Framework endpoint or the Graph Api, but in either case the Bot Framework is easier to work with for proactive messages).
Please see specifically this sample for how to do this in Node.

Unable to post message via the Slack API - getting error 'no_text'

Why am I getting an error 'no_text' ?
The Json below is valid and is taken from the slack documentation example.
In bot.postMessage(channel, '', params) if I populate the second parameter (i.e. replacing ' ' with 'some_text) it prints 'some_text' but without the attachment.
bot.postMessage(channel, 'some_text', params) --> works but the attachment doesn't show up.
const element = {
"text": "Would you like to play a game?",
"response_type": "in_channel",
"attachments": [
{
"text": "Choose a game to play",
"fallback": "If you could read this message, you'd be choosing something fun to do right now.",
"color": "#3AA3E3",
"attachment_type": "default",
"callback_id": "game_selection",
"actions": [
{
"name": "games_list",
"text": "Pick a game...",
"type": "select",
"options": [
{
"text": "Hearts",
"value": "hearts"
},
{
"text": "Global Thermonuclear War",
"value": "war"
}
]
}
]
}
]
}
console.log('JSON.stringify(element): '+JSON.stringify(element));
params = {
icon_emoji: ':r2:',
attachments: JSON.stringify(element)
}
bot.postMessage(channel, '', params).always(function (data) {...}
The issue arises from the lack of a text field in the parameters that is passed to bot.PostMessage. Your params should be
params = {
icon_emoji: ':r2:',
text: "Would you like to play a game?",
response_type: "in_channel",
attachments: element
}
and the element now should start from the actual attachment
const element = [
{
"text": "Choose a game to play",
"fallback": "If you could read this message, you'd be choosing something fun to do right now.",
"color": "#3AA3E3",
"attachment_type": "default",
"callback_id": "game_selection",
"actions": [
{
"name": "games_list",
"text": "Pick a game...",
"type": "select",
"options": [
{
"text": "Hearts",
"value": "hearts"
},
{
"text": "Global Thermonuclear War",
"value": "war"
}
]
}
]
}
]
I met the same issue and found the solution.
The problem is that if only attachments field is added into the payload, it will report no_text error. But if text field is added, slack message will only show the text content.
The solution:
When we want to display attachments, we need to add a basic blocks field instead of text field. Something like
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "bar"
}
}
],
"attachments": [
{
"color": "#FF0000",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "foo"
}
}
]
}
]
}
If putting the above payload into the Slack build kit, it will be a misleading. That's also why I stuck with the issue.
I would recommend to use chat.postMessage test to debug the payload. It will work like a charm.

How to track Signer's IP Address in DocuSign

We need a IP address of Signer for audit purpose. So how we can track a IP of signer apart form certificate of completion.
Use the listAuditEvents api to retrieve the envelope audit history. ClientIPAddress is one of the properties present in the envelope audit history.
GET /v2/accounts/{accountId}/envelopes/{envelopeId}/audit_events
{
"auditEvents": [
{
"eventFields": [
{ "name": "logTime", "value": "2017-05-12T14:27:20.3541245Z" },
{ "name": "UserName", "value": "Jane Doe" },
{ "name": "UserId", "value": "c780706d-ac28-45a8-9c05-4adc6860e488" },
{ "name": "Action", "value": "Signed" },
{ "name": "Message", "value": "The envelope was Signed by Jane Doe" },
{ "name": "EnvelopeStatus", "value": "created" },
{ "name": "ClientIPAddress", "value": "128.384.222.11" },
{ "name": "Language", "value": "english" }
]
},
]
}

Docu Sign Rest API Sender View Issue for in Process Envelope

We are trying to use embedded signing to display the sender view in iframe.
If an envelope has 2 signers and one of them have signed the envelope we want to display the current state of the envelope to the sender.
Is there any API available to display current state of envelope to the sender.
Per the comment that you've added to your answer, the simplest way to retreive current status of all recipients for an Envelope is the GET Recipients API call:
GET https://{{env}}.docusign.net/restapi/{{version}}/accounts/{{acctId}}/envelopes/{{envelopeId}}/recipients?include_tabs=false&include_extended=false
The response will contain an array of recipient objects for each recipient type, and each recipient object has a status property that indicates the current status of the recipient. For example, here's a response for an Envelope that contains 2 Signers -- the first has Completed signing, the second has not (status=delivered):
{
"signers": [
{
"signInEachLocation": "false",
"name": "Abby Adams",
"email": "abby#outlook.com",
"recipientId": "04cdba57-5383-4917-9081-e9dad16ba8a9",
"recipientIdGuid": "04cdba57-5383-4917-9081-e9dad16ba8a9",
"requireIdLookup": "false",
"userId": "1860fbdc-96ee-4d14-8353-c0213cd2728f",
"routingOrder": "1",
"roleName": "Signer 1",
"status": "completed",
"signedDateTime": "2014-04-24T14:00:10.5800000Z",
"deliveredDateTime": "2014-04-24T13:59:35.2330000Z",
"templateLocked": "false",
"templateRequired": "false"
},
{
"signInEachLocation": "false",
"name": "Adam Smith",
"email": "adam#outlook.com",
"recipientId": "fb3708f5-552f-410e-8029-9abbb5214196",
"recipientIdGuid": "fb3708f5-552f-410e-8029-9abbb5214196",
"requireIdLookup": "false",
"userId": "ecd6f430-5c08-4a4c-876e-ba7ad95f13dc",
"routingOrder": "2",
"status": "delivered",
"deliveredDateTime": "2014-04-24T14:00:28.1930000Z"
}
],
"agents": [],
"editors": [],
"intermediaries": [],
"carbonCopies": [],
"certifiedDeliveries": [],
"inPersonSigners": [],
"recipientCount": "2",
"currentRoutingOrder": "2"
}
If you need more detailed audit trail information, you can use the GET Audit Events API call:
GET https://{{env}}.docusign.net/restapi/{{version}}/accounts/{{acctId}}/envelopes/{{envelopeId}}/audit_events
The Response to this request will contain an array of auditEvent objects. Each auditEvent object is composed of a single property -- eventFields -- which is an array of name/value pairs.
For example, the following response shows that Mary Smith created the envelope and sent the envelope, and then John Doe viewed the envelope and declined the envelope:
{
"auditEvents": [
{
"eventFields": [
{
"name": "logTime",
"value": "2014-05-23T03:07:52.7741974Z"
},
{
"name": "Source",
"value": "api"
},
{
"name": "UserName",
"value": "Mary Smith"
},
{
"name": "UserId",
"value": "bc266e97-83a6-472c-XXXX-ebad10e4cc6a"
},
{
"name": "Action",
"value": "Registered"
},
{
"name": "Message",
"value": "The envelope was created by Mary Smith"
},
{
"name": "EnvelopeStatus",
"value": "created"
},
{
"name": "ClientIPAddress",
"value": "XX.XX.XX.XXX"
},
{
"name": "Information",
"value": ""
},
{
"name": "GeoLocation",
"value": ""
},
{
"name": "Language",
"value": "english (us)"
}
]
},
{
"eventFields": [
{
"name": "logTime",
"value": "2014-05-23T03:07:53.6321919Z"
},
{
"name": "Source",
"value": "api"
},
{
"name": "UserName",
"value": "Mary Smith"
},
{
"name": "UserId",
"value": "bc266e97-83a6-472c-a25b-ebad10e4cc6a"
},
{
"name": "Action",
"value": "Sent Invitations"
},
{
"name": "Message",
"value": "Mary Smith sent an invitation to John Doe [john#outlook.com]"
},
{
"name": "EnvelopeStatus",
"value": "sent"
},
{
"name": "ClientIPAddress",
"value": "XX.XX.XX.XXX"
},
{
"name": "Information",
"value": "John Doe [john#outlook.com]"
},
{
"name": "GeoLocation",
"value": ""
},
{
"name": "Language",
"value": "english (us)"
}
]
},
{
"eventFields": [
{
"name": "logTime",
"value": "2014-05-23T03:12:17.9412976Z"
},
{
"name": "Source",
"value": "web"
},
{
"name": "UserName",
"value": "John Doe"
},
{
"name": "UserId",
"value": "03c8a856-c0ae-41bf-943d-ac6e92db66a8"
},
{
"name": "Action",
"value": "Viewed"
},
{
"name": "Message",
"value": "John Doe viewed the envelope [documents:(Mutual NDA with anchors.pdf)]"
},
{
"name": "EnvelopeStatus",
"value": "sent"
},
{
"name": "ClientIPAddress",
"value": "XX.XX.XX.XXX"
},
{
"name": "Information",
"value": "John Doe viewed the envelope [documents:(Mutual NDA with anchors.pdf)]"
},
{
"name": "GeoLocation",
"value": ""
},
{
"name": "Language",
"value": "english (us)"
}
]
},
{
"eventFields": [
{
"name": "logTime",
"value": "2014-05-23T03:12:30.3432181Z"
},
{
"name": "Source",
"value": "web"
},
{
"name": "UserName",
"value": "John Doe"
},
{
"name": "UserId",
"value": "03c8a856-c0ae-41bf-943d-ac6e92db66a8"
},
{
"name": "Action",
"value": "Declined"
},
{
"name": "Message",
"value": "John Doe declined the envelope [Reason: I changed my mind.]"
},
{
"name": "EnvelopeStatus",
"value": "declined"
},
{
"name": "ClientIPAddress",
"value": "XX.XX.XX.XXX"
},
{
"name": "Information",
"value": "Reason: I changed my mind."
},
{
"name": "GeoLocation",
"value": ""
},
{
"name": "Language",
"value": "english (us)"
}
]
}
]
}
The DocuSign REST API Guide (https://10226ec94e53f4ca538f-0035e62ac0d194a46695a3b225d72cc8.ssl.cf2.rackcdn.com/rest-api-guide-v2.pdf) contains information about both of the API calls.
--- UPDATE ---
You can retrieve an Envelope's document(s) at any time (i.e., regardless of signer/envelope status) by using the "GET Envelope Documents" API request:
GET https://{{env}}.docusign.net/restapi/{{version}}/accounts/{{acctId}}/envelopes/{{envelopeId}}/documents/combined
The response will contain a byte stream that represents the contents of the Envelope document(s) in PDF format -- your application can then simply write that byte stream to the browser to display the document(s). See page 177 of the REST API Guide for details about this operation.
Regarding your question about customizing the "Embedded Sender View" -- it's not possible to customize the Sender view via the API -- i.e., an 'embedded Sender will always have the ability to do everything that they'd be able to do if sending directly via the DocuSign web console.
Once a DocuSign envelope is in progress (meaning it has been sent and is NOT in draft state) then you can not generate the sender view. The sender view is used to tag the envelope, and if the envelope has already been sent then no more tagging is allowed.
So to answer your question, no it's not possible to generate a sender view on a sent envelope, it's only possible to generate a signing URL at that point, and you can only generate the signing URL for recipients who have their clientUserId property set.
More on Embedded Signing here:
https://www.docusign.com/developer-center/explore/features/embedding-docusign

Resources