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":[]}]

Flow Info

Created 7 years, 8 months ago
Rating: not yet rated

Owner

Actions

Rate:

Node Types

Core
  • change (x1)
  • comment (x2)
  • function (x1)
  • http in (x1)
  • http response (x5)
  • switch (x1)
Other
  • config (x1)
  • subflow (x1)

Tags

  • particle
  • particle.io
  • webhook
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option