Webhook Tutorial - Send an Email!

so i would not use the wake up pin i would just used a normal DI pin which wakes up the program?

Yup, any pin that can be used with System.sleep(wkp, trigger) but with WKP you could also use System.sleep(SLEEP_MODE_DEEP) (but only for RISING edge).

Hi @harrisonhjones,

thank you very much for this great tutorial! :smile:
Would it be possible to change the subject and body of the email in a kind of flexible way?
For example including the temperature in the body?

Yes, it should be possible. Here is, roughly, what you would need to do:

First: Edit the JSON file I linked to. Replace

subject: "ANY_SUBJECT",
text: "ANY_BODY"

with

subject: "{{s}}",
text: "{{b}}"

Second: Publish an event with a body/payload that looks like this:

"{\"s\":\"Your subject here\", \"b\":\"Your body here\"}"

2 Likes

Thank you very much for your help! I have adapted your example and it is working great :smile:
It would be cool to be able to edit Webhooks instead of heaving to delete them everytime I want to change something.

Thanks @harrisonhjones for a great tutorial. Got this working with only a minimum of false starts.

Very minor suggestion (to make things crystal clear for the newbies like me): Step 8: Paste the text of the linked file over (replace entirely) the default text that appears in the JSON text editor. …yes I know obvious…who would make such a mistake… :flushed:

Suggestions/comments for those coming after:

  1. Because if you make an error in your JSON text and your webhook doesnt work as you intended it to, you will need to delete and reinstate your webhook. Yes, you heard it right, at present you cannot edit your webhook. So I learned pretty quickly you need that JSON text saved in Notepad or your favourite text editor equivalent, so you can re-create your webhook easily.

  2. Mailgun has great documentation and logs and seems to clearly explain what the errors are you may have made in the JSON text (if any). If you get errors returned from Mailgun they will show up in the Particle Console Logs tab. To debug, best to go into Mailgun and track through the logs/diagnostics. To do this go to the “Logs” screen and click on a message/log event. The details will drop down.

1 Like

@benjiiiuk:

Check out IFTTT. I use it to monitor a number of Particle devices and I can get an email when the device goes offline and then again when it comes back online.

Just to help summarise, I found it a bit tricky to follow myself. So here is an example that is easily reusable.

Part 1: Write a Particle.publish() on the Particle device to trigger the webhook to activate
Part 2: Write a webhook on the console.particle.io page which sends a message to the Mailgun API
Part 3: Mailgun API - some tips

Part 1: White a Particle.publish()
I used this code in the ‘setup’ (e.g. void setup() ) section to trigger the Publish only once.
Why? So it only runs each time the device starts.

Tip: Careful here as if the device gets stuck in a reboot loop (e.g. your watchdog timing isn’t right) you could end up sending a lot of emails overnight and not notice. So be sure to setup some limits on your Mailgun account.

Here the is Particle.Publish I used to trigger my webhook.

//DEVICE BOOTED - EMAIL NOTIFICATION
        String msgBody = String("From device:" + String(anotherVariableIHadElseWhereInMyCode) + ", ModuleId: " + String(anotherVariableNameIHadElsewhereInMyCode) + "It is alive now");

        sendEmailViaWebhook("my-email-address-was-here@gmail.com", "BOOT ADVISORY MESSAGE", String(msgBody));

The first bit ‘String msgBody’ - let’s you put a whole let of Strings together and put it into the message body.
The second bit 'sendEmailViaWebhook - is code that calls a function that is in my code. Why use a function? It let’s me re-use that same email sending ability later, so I don’t have to keep re-writing the next bit…

This bit was placed AFTER the loop() { }

The function on the particle device that sends the command to the web hook.

//This bit declares the function
int sendEmailViaWebhook(String input_to_email, String input_subject, String input_body)
    {
        //This bit wraps all of the various bits into one single String (into what is called JSON format)
        //Don't worry - we this single string is in a format called JSON which the Webhook & Mailgun API knows how to unwrap
        String publishData = String::format(
                	        "{ \"TO_EMAIL\": \"%s\", \"SUBJECT\": \"%s\", \"BODY\": \"%s\" }",
                	        input_to_email.c_str(), input_subject.c_str(), input_body.c_str());
       
       //We use a 'Publish' to send our JSON 'string' or 'message' to our Webhook called 'myMailgunEvent'     
       Particle.publish("myMailgunEvent", publishData, PRIVATE))
        

        return 0;
    }

Part 2: Write a webhook on the console.particle.io page which sends a message to the Mailgun API
They did a good job of explaining this above, but to show you exactly what worked with the same code I used above, use this below.

Step 1: Go to console.particle.io
Step 2: Go to integrations (icon on left hand side)
Step 3: Click 'new integration’
Step 4: Select 'webhook’
Step 4: Select custom template
Step 5: Delete all of the existing text in the box that appears
Step 6: Copy in the text below:

BEFORE you copy it in, remember to change these key parts:
YOURDOMAINGOESHERE = putyourdomainnamehere.com
YOURKEYGOESHERE = the api key that is written in Mailgun’s domain page.

 {
        "event": "myMailgunEvent",
        "url": "https://api.mailgun.net/v3/YOURDOMAINGOESHERE/messages",
        "requestType": "POST",
        "noDefaults": true,
        "rejectUnauthorized": false,
        "form": {
            "to": "{{TO_EMAIL}}",
            "from": "Notifications <Notifications@YOURDOMAINGOESHERE>",
            "subject": "{{SUBJECT}}",
            "text": "{{BODY}}"
        },
        "auth": {
            "username": "api",
            "password": "YOURKEYGOESHERE"
        }
    }

Part 3: Mailgun API - some tips

  • Make sure you click the email confirmation to validate your Mailgun account, you can’t send emails until you do this
  • It helps to try send basic text emails first - fancy formatting is a bit harder, do this later.

If you need any help - feel free to message below.

PS: You don’t need to use a function, you could put all the code in step 1 in the same area, but if you want to use it a few times in various places, it will save you a lot of space/time.

2 Likes

@Cameron What type of content-type encoding if your example using?

I used your example and it looks like application/x-www-form-urlencoded.

I’m almost there…one problem is that it’s trying to cram the subject into the body.

The magic is to escape the quotation marks e.g. "

You can do this by doing a find and replace of all "
and replace them with

\"

By adding a \ in-front of " should prevent the quotation mark from ending it.

Here is some code that if you copy in between “text”: “putItAllInHere”

I find it easier to put the HTML formatted text into the Webhook not into the passed code.

<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">
<html xmlns=\"http://www.w3.org/1999/xhtml\" style=\"font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\">
<head>
<meta name=\"viewport\" content=\"width=device-width\" />
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />
<title>Alert - Device has just started</title>


<style type=\"text/css\">
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em;
}
body {
background-color: #f6f6f6;
}
@media only screen and (max-width: 640px) {
  body {
    padding: 0 !important;
  }
  h1 {
    font-weight: 800 !important; margin: 20px 0 5px !important;
  }
  h2 {
    font-weight: 800 !important; margin: 20px 0 5px !important;
  }
  h3 {
    font-weight: 800 !important; margin: 20px 0 5px !important;
  }
  h4 {
    font-weight: 800 !important; margin: 20px 0 5px !important;
  }
  h1 {
    font-size: 22px !important;
  }
  h2 {
    font-size: 18px !important;
  }
  h3 {
    font-size: 16px !important;
  }
  .container {
    padding: 0 !important; width: 100% !important;
  }
  .content {
    padding: 0 !important;
  }
  .content-wrap {
    padding: 10px !important;
  }
  .invoice {
    width: 100% !important;
  }
}
</style>
</head>

<body itemscope itemtype=\"http://schema.org/EmailMessage\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6em; background-color: #f6f6f6; margin: 0;\" bgcolor=\"#f6f6f6\">

<table class=\"body-wrap\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background-color: #f6f6f6; margin: 0;\" bgcolor=\"#f6f6f6\"><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;\" valign=\"top\"></td>
		<td class=\"container\" width=\"600\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto;\" valign=\"top\">
			<div class=\"content\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;\">
				<table class=\"main\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background-color: #fff; margin: 0; border: 1px solid #e9e9e9;\" bgcolor=\"#fff\"><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"alert alert-warning\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background-color: #FF9F00; margin: 0; padding: 20px;\" align=\"center\" bgcolor=\"#FF9F00\" valign=\"top\">
							Main title appears here
						</td>
					</tr><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"content-wrap\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;\" valign=\"top\">
							<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"content-block\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;\" valign=\"top\">
										You device sensor called:  <strong style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\">{{deviceFriendlyName}}</strong> has just turned on.
									</td>
								</tr><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"content-block\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;\" valign=\"top\">
										more text in here
									</td>

                </tr><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"content-block\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;\" valign=\"top\">
                    more text in here
                  </td>

								</tr><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"content-block\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;\" valign=\"top\">
										<a href=\"http://www.google.com\" class=\"btn-primary\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2em; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; text-transform: capitalize; background-color: #348eda; margin: 0; border-color: #348eda; border-style: solid; border-width: 10px 20px;\">Contact Us</a>
									</td>
								</tr><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"content-block\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;\" valign=\"top\">
										Thanks for ....text here
									</td>
								</tr></table></td>
					</tr></table><div class=\"footer\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; clear: both; color: #999; margin: 0; padding: 20px;\">
					<table width=\"100%\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><tr style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; margin: 0;\"><td class=\"aligncenter content-block\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;\" align=\"center\" valign=\"top\"><a href=\"http://www.mailgun.com\" style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 12px; color: #999; text-decoration: underline; margin: 0;\">Unsubscribe</a> from these alerts.</td>
						</tr></table></div></div>
		</td>
		<td style=\"font-family: 'Helvetica Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0;\" valign=\"top\"></td>
	</tr></table></body>
</html>

I’ve followed this tutorial and can send an email when my event is triggered. But, I’m having a hard time trying to figure out how to add the data (string value) from my particle publish variable to the body of the email that gets sent. I’m sure my lack of experience with json and html is a large part of my problem. Any suggestions would be appreciated.

BTW, I can make IFTTT send a text with the particle publish data, but the lag from event to reception of the text is 45 minutes or longer. IFTTT can generate an email, but apparently only to me as they’ve dropped support for Gmail.

Glad you got your device sending emails! As for your question about adding more info it would be helpful to see what you’ve tried so far and what worked (or didn’t). Code samples and reproduction steps would be fantastic.

1 Like

Hello Harrison
This code below gets called from the main loop to test for a change in input port variable status :

void showStatus() {
if (status != oldstatus)      // Where status ranges from 1 to 4 and points to a string in msg[ ] string array
    alert1=msg[status];       // This pulls 1 of 4 messages from msg[ ] 
    alert="{\"BODY\":\"alert1\"}";   // Trying to pass the contents of alert1 into the mailgun webhook message body---and failing!
    Particle.publish("ALERT", String(alert), PRIVATE);
    oldstatus=status;              
     }    
}

The result is that I literally get the word alert1 in the body of the generated email instead of the string value of alert1 .
I clearly don’t know how to properly build the syntax to pass into the webhook.
I’ve tried "{\"BODY\":\alert1\}" and "{\"BODY\": alert1}" and they don’t work at all.
Suggestions appreciated.

This is what your code tells the webhook to do. The code highlighting clearly shows that alert (written red inside the string) is taken literally and not as variable name.

What you want to do is insert the content of your variable alert1 into the string you are creating.
I must say I do advise to not use String objects for reasons elaborated about often enough in this forum, hence I'm not going to show how to do this with String and assume char alert[64]

void showStatus() {
  if (status != oldstatus) {         // Where status ranges from 1 to 4 and points to a string in msg[ ] string array
    alert1 = msg[status];            // This pulls 1 of 4 messages from msg[ ] 
    snprintf(alert, sizeof(alert)    // Trying to pass the contents of alert1 into the mailgun webhook message body
            , "{\"BODY\":\"%s\"}"
            , (const char*)alert1
            );   
    Particle.publish("ALERT", alert, PRIVATE);
    oldstatus=status;              
  }    
}

This is in essence already shown in a previous post

(marked between tripple asteriscs (***))

BTW, your original code was missing an opening curly brace ({) for the if() block.

1 Like

ScruffR- That did the trick. Thank you very much for taking the time to answer my question. I’m new to this programming environment and I don’t know what I don’t know, but I’m learning. :grinning:

2 Likes

This was all very helpful. However, I am seeing that the Integrations screen within Particle.io does not allow one to scroll to the very bottom.

The UI appears to be messed up - right scroll bar is missing. Only way I can see below the upper fold is to zoom way out, which is counter productive because then the content is so small.

Sometimes I observe a similar “glitch”. It usually goes away if I force reload the page (at least on Chrome/Edge).

Thanks. Speaking of glitches. How come sometimes I can test my integration, but other times that button is not available?

If you restrict a webhook to a particular device, the Test button is disabled, since for security reasons the cloud API cannot generate an event from a specific Device ID. This is done to prevent being able to spoof the source of an event.

Good to know.

Another tip - If at first you don’t see your messages, check your spam folder! Finally got some stuff to come through, but it was going to spam at first.