Particle HTTP Webhook Endpoint
Please refer to Particle.io docs for full explanation on webhooks:
https://docs.particle.io/guide/tools-and-features/webhooks/
WEBHOOKS SHOULD ONLY BE USED WITH HTTPS. BASIC AUTHENTICATION SENDS YOUR PASSWORD AND AUTHENTICATION TOLKENS IN PLAIN TEXT. CHECK www.letsencrypt.org FOR FREE CERTIFICATES.
Requires:
- node-red
- node-red-contrib-config
Inputs: None
Outputs:
- HTTP Message - For debug porpuses
- HTTP Response - For debug porpuses
- HTTP response from the event post - For conquering the world
Config:
- XAuthToken - Token to te added in the request header to confirm authenticity of the sender
- compulsory_keys - List of compulsory keys to be present in the message
Contains:
- Notes explaining how to configure Particle Webook
- Notes explaining how to test setup
Does not Contain:
- Information on how to enable Basic Authentication
[{"id":"7a80fd1.248ed04","type":"subflow","name":"Particle","info":"","in":[],"out":[{"x":720,"y":160,"wires":[{"id":"891c4084.f911a","port":0}]},{"x":720,"y":200,"wires":[{"id":"2f5bb2.884a944e","port":0},{"id":"2f5bb2.884a944e","port":1},{"id":"2f5bb2.884a944e","port":2},{"id":"2f5bb2.884a944e","port":3},{"id":"2f5bb2.884a944e","port":4}]},{"x":720,"y":440,"wires":[{"id":"20cb316.89989ce","port":0}]}],"outputLabels":["HTTP Message","HTTP Response","Data"]},{"id":"891c4084.f911a","type":"http in","z":"7a80fd1.248ed04","name":"Particle","url":"<ENDPOINT>","method":"post","upload":false,"swaggerDoc":"","x":70,"y":160,"wires":[["dc5cf45f.6747c8","25133598.49945a"]]},{"id":"dc5cf45f.6747c8","type":"function","z":"7a80fd1.248ed04","name":"Check Info","func":"var xauth = flow.get('xAuthToken');\nvar compulsory_fields = flow.get('compulsory_fields');\n\n\nvar xauthreq = null;\nif ( \"X-Auth-Token\" in msg.req.headers ) {\n xauthreq = msg.req.headers[\"X-Auth-Token\"];\n} else {\n xauthreq = msg.req.headers[\"x-auth-token\"];\n}\n\n\nif (xauthreq === null) {\n msg.status = 412;\n msg.payload = { \"error\": \"Precondition Failed: no X-Auth-Token header present\" };\n} else {\n if (xauthreq === xauth) {\n var isValid = true;\n \n for (var cfn=0; cfn<compulsory_fields.length; cfn++) {\n isValid &= compulsory_fields[cfn] in msg.payload;\n }\n \n if (isValid) {\n msg.status = 200;\n msg.data = msg.payload;\n msg.payload = { \"status\": \"OK\" };\n } else {\n if ( \"test\" in msg.payload && msg.payload.test ) {\n msg.status = 202;\n msg.payload = { \"status\": \"OK\" };\n } else {\n msg.status = 403;\n msg.payload = { \"error\": \"Missing compulsory field\" };\n } }\n } else {\n msg.status = 403;\n msg.payload = { \"error\": \"Forbidden: wrong X-Auth-Token in header\" };\n }\n}\n\nreturn msg;","outputs":1,"noerr":0,"x":230,"y":220,"wires":[["2f5bb2.884a944e"]]},{"id":"7af9d9f2.ac54f8","type":"http response","z":"7a80fd1.248ed04","name":"HTTP 403 - Forbidden","statusCode":"403","headers":{"Content-Type":"application/json"},"x":800,"y":240,"wires":[]},{"id":"2f5bb2.884a944e","type":"switch","z":"7a80fd1.248ed04","name":"Decider","property":"status","propertyType":"msg","rules":[{"t":"eq","v":"403","vt":"num"},{"t":"eq","v":"412","vt":"num"},{"t":"eq","v":"202","vt":"num"},{"t":"eq","v":"200","vt":"num"},{"t":"else"}],"checkall":"false","repair":false,"outputs":5,"x":260,"y":300,"wires":[["7af9d9f2.ac54f8"],["6b88c4f.5f0cd3c"],["27e92517.32bb3a"],["80fb5a68.5bc7f8","20cb316.89989ce"],["ac72c9c0.189938"]],"outputLabels":["403 - Forbidden","412 - Failed","202 - Test","200 - OK","Otherwise"]},{"id":"80fb5a68.5bc7f8","type":"http response","z":"7a80fd1.248ed04","name":"HTTP 200 - OK","statusCode":"200","headers":{"Content-Type":"application/json"},"x":780,"y":360,"wires":[]},{"id":"6b88c4f.5f0cd3c","type":"http response","z":"7a80fd1.248ed04","name":"HTTP 412 - Failed","statusCode":"412","headers":{"Content-Type":"application/json"},"x":790,"y":280,"wires":[]},{"id":"ac72c9c0.189938","type":"http response","z":"7a80fd1.248ed04","name":"HTTP 404 - Otherwise","statusCode":"404","headers":{"Content-Type":"application/json"},"x":800,"y":400,"wires":[]},{"id":"27e92517.32bb3a","type":"http response","z":"7a80fd1.248ed04","name":"HTTP 202 - Test OK","statusCode":"202","headers":{"Content-Type":"application/json"},"x":800,"y":320,"wires":[]},{"id":"20cb316.89989ce","type":"change","z":"7a80fd1.248ed04","name":"Delete HTTP fields; data to payload","rules":[{"t":"set","p":"payload","pt":"msg","to":"data","tot":"msg"},{"t":"delete","p":"data","pt":"msg"},{"t":"delete","p":"req","pt":"msg"},{"t":"delete","p":"res","pt":"msg"},{"t":"delete","p":"status","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":440,"wires":[[]]},{"id":"28f19cde.81da24","type":"comment","z":"7a80fd1.248ed04","name":"Particle WebHook API","info":"Event Name: <EVENT NAME>\nUrl: https://<URL>/nodes/<ENDPOINT>\nType: POST\nFormat: JSON\nDevice: Any\nHTTP Headers:\n X-Auth-Token <X AUTH TOKEN>\n\nUsername: <USERNAME>\nPassword: <PASSWORD>\n\nFormat:\n{\n \"event\": \"{{PARTICLE_EVENT_NAME}}\",\n \"temp\": \"{{t}}\",\n \"humi\": \"{{h}}\",\n \"pres\": \"{{p}}\",\n \"sens\": \"{{s}}\",\n \"vers\": \"{{v}}\",\n \"device_id\": \"{{PARTICLE_DEVICE_ID}}\",\n \"published_at\": \"{{PARTICLE_PUBLISHED_AT}}\"\n}\n\n\nREQUEST OBJECT\n {\"_msgid\":\"AAAAAAA.AAAA\",\"req\":{\"_readableState\":{\"objectMode\":false,\"highWaterMark\":16384,\"buffer\":{\"head\":null,\"tail\":null,\"length\":0},\"length\":0,\"pipes\":null,\"pipesCount\":0,\"flowing\":true,\"ended\":true,\"endEmitted\":true,\"reading\":false,\"sync\":false,\"needReadable\":false,\"emittedReadable\":false,\"readableListening\":false,\"resumeScheduled\":false,\"destroyed\":false,\"defaultEncoding\":\"utf8\",\"awaitDrain\":0,\"readingMore\":false,\"decoder\":null,\"encoding\":null},\"readable\":false,\"domain\":null,\"_events\":{},\"_eventsCount\":0,\"socket\":\"[internal]\",\"connection\":\"[internal]\",\"httpVersionMajor\":1,\"httpVersionMinor\":1,\"httpVersion\":\"1.1\",\"complete\":true,\"headers\":{\"host\":\"<URL>\",\"x-real-ip\":\"1.1.1.1, 1.1.1.1\",\"x-forwarded-for\":\"1.1.1.1, 1.1.1.1\",\"x-forwarded-host\":\"<URL>\",\"x-nginx-proxy\":\"true\",\"connection\":\"upgrade\",\"content-length\":\"13\",\"authorization\":\"Basic AAAAAAAAAAAAAAAAA\",\"user-agent\":\"curl/7.52.1\",\"accept\":\"*/*\",\"content-type\":\"application/x-www-form-urlencoded\"},\"rawHeaders\":[\"Host\",\"<URL>\",\"X-Real-IP\",\"1.1.1.1\",\"X-Forwarded-For\",\"1.1.1.1\",\"X-Forwarded-Host\",\"<URL>\",\"X-NginX-Proxy\",\"true\",\"X-Real-IP\",\"1.1.1.1\",\"Host\",\"<URL>\",\"X-Forwarded-For\",\"1.1.1.1\",\"Connection\",\"upgrade\",\"Content-Length\",\"13\",\"Authorization\",\"Basic AAAAAAAAAAAAAAAAA\",\"User-Agent\",\"curl/7.52.1\",\"Accept\",\"*/*\",\"Content-Type\",\"application/x-www-form-urlencoded\"],\"trailers\":{},\"rawTrailers\":[],\"upgrade\":false,\"url\":\"<ENDPOINT>\",\"method\":\"POST\",\"statusCode\":null,\"statusMessage\":null,\"client\":\"[internal]\",\"_consuming\":true,\"_dumped\":false,\"baseUrl\":\"/nodes\",\"originalUrl\":\"/nodes/<ENDPOINT>\",\"_parsedUrl\":{\"protocol\":null,\"slashes\":null,\"auth\":null,\"host\":null,\"port\":null,\"hostname\":null,\"hash\":null,\"search\":null,\"query\":null,\"pathname\":\"<ENDPOINT>\",\"path\":\"<ENDPOINT>\",\"href\":\"<ENDPOINT>\",\"_raw\":\"<ENDPOINT>\"},\"params\":{},\"query\":{},\"res\":\"[internal]\",\"route\":{\"path\":\"<ENDPOINT>\",\"stack\":[{\"name\":\"cookieParser\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"httpMiddleware\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"corsHandler\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"metricsHandler\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"jsonParser\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"urlencodedParser\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"multipartParser\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"rawBodyParser\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"<anonymous>\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"},{\"name\":\"<anonymous>\",\"keys\":[],\"regexp\":{\"fast_star\":false,\"fast_slash\":false},\"method\":\"post\"}],\"methods\":{\"post\":true}},\"cookies\":{},\"signedCookies\":{},\"body\":{\"{\\\"test\\\":true}\":\"\"},\"_body\":true},\"res\":{\"_res\":\"[internal]\"},\"payload\":{\"{\\\"test\\\":true}\":\"\"}}\n\nRESPONSES\n 403: no x auth\n curl -XPOST -u <USERNAME> -v -H 'Content-Type: application/json' -d '{\"test\":true}' https://<URL>/nodes/<ENDPOINT>\n 412: wrong x auth\n curl -XPOST -u <USERNAME> -v -H 'Content-Type: application/json' -H 'X-Auth-Token: 1' -d '{\"test\":true}' https://<URL>/nodes/<ENDPOINT>\n 202:\n curl -XPOST -u <USERNAME> -v -H 'Content-Type: application/json' -H 'X-Auth-Token: <X AUTH TOKEN>' -d '{\"test\":true}' https://<URL>/nodes/<ENDPOINT>\n","x":280,"y":40,"wires":[]},{"id":"5920cf6.94d0c3","type":"comment","z":"7a80fd1.248ed04","name":"Particle Integration Template","info":"{\n \"event\": \"<EVENT NAME>\",\n \"url\": \"https://<URL>/nodes/<ENDPOINT>\",\n \"requestType\": \"POST\",\n \"noDefaults\": true,\n \"rejectUnauthorized\": true,\n \"headers\": {\n \"X-Auth-Token\": \"<X AUTH TOKEN>\"\n },\n \"json\": {\n \"event\": \"{{PARTICLE_EVENT_NAME}}\",\n \"temp\": \"{{t}}\",\n \"humi\": \"{{h}}\",\n \"pres\": \"{{p}}\",\n \"sens\": \"{{s}}\",\n \"vers\": \"{{v}}\",\n \"device_id\": \"{{PARTICLE_DEVICE_ID}}\",\n \"published_at\": \"{{PARTICLE_PUBLISHED_AT}}\"\n },\n \"auth\": {\n \"username\": \"<USERNAME>\",\n \"password\": \"<PASSWORD>\"\n }\n}","x":520,"y":40,"wires":[]},{"id":"25133598.49945a","type":"config","z":"7a80fd1.248ed04","name":"Particle Config","properties":[{"p":"xAuthToken","pt":"flow","to":"<X AUTH TOKEN>","tot":"str"},{"p":"compulsory_fields","pt":"flow","to":"[\"published_at\",\"device_id\"]","tot":"json"}],"active":true,"x":240,"y":100,"wires":[]}]