{"id":14963109,"url":"https://github.com/espressif/esp-rainmaker-webhooks","last_synced_at":"2025-10-19T08:31:49.719Z","repository":{"id":74588573,"uuid":"502881304","full_name":"espressif/esp-rainmaker-webhooks","owner":"espressif","description":"Rainmaker Web hooks Project","archived":false,"fork":false,"pushed_at":"2024-12-13T07:40:07.000Z","size":121,"stargazers_count":5,"open_issues_count":0,"forks_count":3,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-01-31T17:17:35.468Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/espressif.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-13T09:01:29.000Z","updated_at":"2024-12-13T07:40:11.000Z","dependencies_parsed_at":"2024-02-26T18:54:13.592Z","dependency_job_id":null,"html_url":"https://github.com/espressif/esp-rainmaker-webhooks","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/espressif%2Fesp-rainmaker-webhooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/espressif%2Fesp-rainmaker-webhooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/espressif%2Fesp-rainmaker-webhooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/espressif%2Fesp-rainmaker-webhooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/espressif","download_url":"https://codeload.github.com/espressif/esp-rainmaker-webhooks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237092699,"owners_count":19254255,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T13:31:02.312Z","updated_at":"2025-10-19T08:31:49.356Z","avatar_url":"https://github.com/espressif.png","language":"Python","readme":"# Readme file for WebHooks\n\n## What is WebHook?\n\nThere are various events generated in RainMaker, which are the result of the user actions\nand messages which are transferred between the nodes and RainMaker. Some\nexamples of these events are - user associates a node or node status changes to online.\n\nThese events can be used by external third party systems, for further processing, e.g. Google\nVoice Action or SalesForce, etc.\n\nTo facilitate the processing of these events by third party systems, RainMaker provides\nsupport for WebHooks. Developers can extend the code for WebHooks and perform \nfurther actions, e.g. calling a REST API of some external system or maybe simply writing these\nevents into an AWS Simple Storage Service (S3) bucket.\n\nYou can develop your own WebHooks and deploy them in your AWS account, where Espressif’s\nRainMaker is also deployed.\n\n## How does a WebHook work?\n\nA WebHook consists of an AWS Lambda function along with configuration files, which needs to be \ndeployed in the AWS account.\n\nWhenever a new event is generated, RainMaker publishes this event onto one of the pre-defined\nAWS Simple Notification Service (SNS) topics. Every event type has a separate SNS topic\nassociated with it.\n\nThe lambda function, which is part of the WebHook, listens to the required SNS topics. When the\nevent occurs, the lambda function retrieves the payload for the event. It does the required\nprocessing on the event payload and executes the required action, e.g. sending the event payload\nto an external system or to storing it into S3, etc.\n\nWhenever RainMaker generates any event (e.g. node is online), it needs to decide whether to process this event or not. \nBased on the configurations, RainMaker either sends the event to the SNS topic or may filter it out. \nThere are three types of filter configurations - System level filter, User level filter and Node level filter. \nA generated event is eligible for some or all these filters.  \nThese are evaluated for filtering in the order: System \u003e User \u003e Node  \ni.e. User level filter is checked only if System level filter is enabled.\n\nThe processing of these events can be enabled or disabled with the help of APIs.  \n\n![RainMaker Webhook Architecture](pictures/RainmakerWebhookArch.png \"RainMaker Webhook Architecture\")  \n*The above image describes the architecture of RainMaker's Webhook Framework.*\n\nHere is the reference to the RainMaker WebHooks API - Refer [RainMaker API Documentation][swagger]\n\n## Which events are currently supported by RainMaker?\n\n### RainMaker currently supports the below events -\n\n| Sr. No. |      RainMaker Event Name      |                Event Type                |                SNS Topic Name                 | Can be disabled for System | Can be disabled for User and Node |\n| :-----: | :----------------------------: | :--------------------------------------: | :-------------------------------------------: | :------------------------: | :-------------------------------: |\n|    1    |          Node Online           |       rmaker.event.node_connected        |       esp_rainmaker_sns_node_connected        |            Yes             |                Yes                |\n|    2    |          Node Offline          |      rmaker.event.node_disconnected      |      esp_rainmaker_sns_node_disconnected      |            Yes             |                Yes                |\n|    3    |     Add User Node sharing      |    rmaker.event.user_node_sharing_add    |    esp_rainmaker_sns_user_node_sharing_add    |            Yes             |                No                 |\n|    4    |   Node Added to User Account   |       rmaker.event.user_node_added       |       esp_rainmaker_sns_user_node_added       |            Yes             |                No                 |\n|    5    | Node Removed from User Account |      rmaker.event.user_node_removed      |      esp_rainmaker_sns_user_node_removed      |            Yes             |                No                 |\n|    6    |    Node Parameters changed     |     rmaker.event.node_params_changed     |   esp_rainmaker_sns_node_parameter_modified   |            Yes             |                No                 |\n|    7    |           Node Alert           |            rmaker.event.alert            |         esp_rainmaker_sns_node_alert          |            Yes             |                No                 |\n|    8    |   Node Automation Triggered    |   rmaker.event.node_automation_trigger   |    esp_rainmaker_sns_automation_triggered     |            Yes             |                No                 |\n|    9    |       Node group shared        | rmaker.event.user_node_group_sharing_add | esp_rainmaker_sns_user_node_group_sharing_add |            Yes             |                No                 |\n|   10    |        Node group added        |    rmaker.event.user_node_group_added    |    esp_rainmaker_sns_user_node_group_added    |            Yes             |                No                 |\n|   11    |       Node group removed       |   rmaker.event.user_node_group_removed   |   esp_rainmaker_sns_user_node_group_removed   |            Yes             |                No                 |\n|   12    |   Node Registered to Account   |       rmaker.event.node_registered       |      esp_rainmaker_sns_node_registration      |             No             |                No                 |\n|   13    |        Admin User Added        |      rmaker.event.admin_user_added       |      esp_rainmaker_sns_admin_user_added       |             No             |                No                 |\n|   14    |         New Tags Added         |       rmaker.event.new_tags_added        |       esp_rainmaker_sns_new_tags_added        |             No             |                No                 |\n|   15    |     Existing Tags Attached     |   rmaker.event.existing_tags_attached    |   esp_rainmaker_sns_existing_tags_attached    |             No             |                No                 |\n|   16    |      Node Config changed       |     rmaker.event.node_config_changed     |    esp_rainmaker_sns_node_config_modified     |            Yes             |                Yes                |\n|   17    |    User Node OTA triggered     |        rmaker.event.user_node_ota        |        esp_rainmaker_sns_user_node_ota        |            Yes             |                Yes                |\n|   18    |        TimeSeries Data         |        rmaker.event.node_ts_data         |        esp_rainmaker_sns_node_ts_data         |            Yes             |                No                 |\n\n## About the sample WebHook Project\n\nThis project contains a sample WebHook - “HelloWorld Template”, which can be extended further\nto develop a new WebHook. The code for this template is developed using Python language, but\nyou can select any other language (e.g. Java, Golang, etc.) which is supported by AWS Lambda. Read more [here][AWSLambdaDoc].\n\nThis WebHook listens to pre-configured SNS topics and prints the payload of the event into AWS CloudWatch Logs.\n\n### Technologies which are required for developing a new WebHook\n\nWebHooks can be developed using any programming language supported by AWS\nLambda - Python, Java, Golang, etc. Read more [here][AWSLambdaDoc].\nSome basic experience with developing Lambda functions and knowledge about AWS CLI\nwill be helpful, for developing a WebHook.\nThe deployment of WebHooks is done using AWS Serverless Application Module (SAM)\nand explained in the later sections of this document.\nReference Links - https://docs.aws.amazon.com/lambda/latest/dg/python-handler.html\n\n\n### Pre-requisites for Starting the WebHook deployment\n\nYou will need to install and configure the below frameworks on your host before you can start with the development of the WebHook.\n\n- AWS CLI (Link - https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html)\n- After the AWS CLI is installed, Execute the below AWS CLI commands to configure the AWS credentials on your computer.\n\n        $ aws configure\n\nYou will be prompted to enter AWS Access Key ID and AWS Secret Key ID.\nAfter that, you will be prompted to enter your default AWS region (e.g. us-east-1)\n\nHere is the link to configure the AWS credentials on your host using AWS access keys. (Link - https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html)\n\n- AWS SAM tool \nThe steps to install and configure the AWS SAM tool are provided in the below Link - \nhttps://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html)\n\n- Python3.7 (Link- https://www.python.org/downloads/)\n\u003e You can use any supported python version. Accordingly, you just need to update the runtime field in template.yaml  \n\u003e To check the supported versions use `$ sam build --help` \n\n\n- Virtualenv (Link - https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/#installing-virtualenv)\n\n- You will need to have an S3 bucket, which will be used for packaging and uploading the build.\n  You can use an existing S3 bucket or create a new unique bucket using the below Command.\n    \n        $ aws s3 mb s3://\u003cS3-BUCKET\u003e\n\n- RainMaker is deployed into your AWS account and you have the credentials for the super admin user of your RainMaker deployment.\n\n## Developing a new WebHook\n\nAfter all the pre-requisites are satisfied, you will need to clone the Git repository for the sample\nWebHook template.\n\n**Step 1** Clone the sample WebHook repository\n\n$ git clone https://github.com/espressif/esp-rainmaker-webhooks.git\n\n**Step 2** Extend the code of the Lambda function\n\nThe code for the WebHooks Lambda function is in the hello_rainmaker/app.py file\nThis code currently prints the payload of the event generated in RainMaker in CloudWatch logs, but you can extend it further, as required.\n\n**Step 3** Modify the SAM template file if required\n\nThe SAM Template is a yml file - WebHooksTemplatePython/template.yml\n\n## Deploying the WebHooks\n\nYou can use either use a python virtual environment or your host's python environment.\n\nFor creating a Python Virtual environment,\n\n**Step 1** Create a virtual environment venv.\n\nThis will create a local directory for creating a virtual environment which will contain all python, pip binaries, etc. Also, additional libraries can be installed in the virtual environment folder (after you activate this environment as in step 2).\n\n    $ python3 -m virtualenv env -p \u003cpath to your python3.7 binary\u003e\n\n\n**Step 2** Activate the virtual environment using \"source \u003cvirtual env folder created in step 1\u003e/bin/activate”\ne.g\n\n    $  source env/bin/activate\n\n**Step 3** Install required packages using the below commands\n\n    $ cd WebHooksTemplatePython\n    $ pip install -r hello_rainmaker/requirements.txt\n\n**Step 4** Install additional libraries if required in your project. \nUpdate requirements.txt for additionally added libraries using pip freeze.\n\n    $ pip freeze \u003e hello_rainmaker/requirements.txt\n\n\n**Step 5** Build the code\n\n    $ sam build\n\n**Step 6** Package and Deploy using the following commands. (Use previously used S3-BUCKET)\n\n    $ sam package --template-file template.yaml --output-template-file template_package.yml --s3-bucket \u003cS3-BUCKET\u003e\n\n\u003e **Note** - Please use the below command for deploying your WebHook stack using `sam deploy` . Please note that, you will need to pass the values for parameters *--capabilities* and *--parameter-overrides StageName* as mentioned below. The value for the capabilities parameter to be used in this example is *CAPABILITY_NAMED_IAM* and for Stage name, you can use the values like dev or prod, etc. \n\n    $ sam deploy --template-file template_package.yml --stack-name HelloRainmaker --capabilities CAPABILITY_NAMED_IAM --parameter-overrides 'StageName=\u003cSTAGE-NAME\u003e'\n\n## Testing the WebHook event\nAfter you login to RainMaker using your credentials, you will need to call the below APIs for configuring the WebHooks. \nYou can make use of any REST client like Postman or you can use curl commands. \nYou will first need to login to RainMaker from the REST client using your super admin credentials.\nYou will need to use the access token received in the Login response as the **Authorization** header to subsequent API calls.\n\n\u003e Please note all the following steps(1-5) are required. Currently, the supported API version is v1.\n\n### 1. Login with User\n\nAPI endpoint - \u003c Base API URL \u003e /v1/login\n\nHTTP Method - POST\n\nRequest Body \n\n```\n{\n  \"user_name\": \"\u003cuser_id\u003e\",\n  \"password\": \"\u003cpassword\u003e\"\n}\n```\n\n\\*Login using superadmin credentials so that SYSTEM level filters may be enabled or disabled.  \nNote - You will get **accesstoken** in the response to the above API Request. Use that in the header of the following API calls as `Authorization=\u003caccesstoken\u003e` \n### 2. Create the WebHooks Configuration\n\n### Description -\n\nAPI endpoint - \u003c Base API URL \u003e/{version}/admin/webhook_integration\n\nHTTP Method - POST\n\nRequest Body\n``` \n{\n    \"service_name\": “\u003cunique_name_of_the_webhook\u003e”,\n    \"endpoint_name\": “\u003cend_point_name\u003e”,\n    “integration_enabled” : true\n}\n```\n\nNote - This API needs to be called by the super admin user. This API creates the new WebHook into RainMaker. \n\n### 3. Adding System Filters\n\nAPI endpoint - \u003c Base API URL \u003e/{version}/admin/event_filter\n\nHTTP Method - POST\n\nThe following is a sample request payload for configuring a System Level Filter:\n\n```\n{\n    \"event_type\" : \"rmaker.event.node_connected\",\n    \"entity_id\" : \"system.rmaker.event.node_connected\",\n    \"entity_type\" : \"System\",\n    \"enabled\": true,\n    “enabled_for_integrations” : [“\u003cunique_name_of_the_webhook\u003e”, “helloworldintegration”]\n}\n```\n\n\\*At least one webhook_integration must be added under `enabled_for_integrations` for webhook to be called.  \nNote - This API needs to be called by the super admin user. This API enables the processing of the specific event in RainMaker, e.g. in this case, the Node connected event is enabled.  \n\n### 4. Adding User-specific Filters\n\nAPI endpoints:  \n\u003c Base API URL \u003e/{version}/user/event_filter  \n **OR**  \n\u003c Base API URL \u003e/{version}/admin/event_filter\n\nHTTP Method - POST\n\nRequest Body\n\n``` \n{\n    \"event_type\" : \"rmaker.event.node_connected\",\n    \"entity_id\" : \"\u003cuser_id\u003e\",\n    \"entity_type\" : \"USER\",\n    \"enabled\": true,\n    \"enabled_for_integrations\" : [“\u003cunique_name_of_the_webhook\u003e”, “helloworldintegration\"]\n}\n``` \n\\*At least one webhook_integration must be added under `enabled_for_integrations` for webhook to be called.  \nNote - This API needs to be called by the end user or the super admin user. This API enables the processing of the event specific to the calling user. In this case, this API enables the node connected event for the user_id provided in the request body.\n\n### 5. Adding Node-specific Filters\n\nAPI endpoints:  \n\u003c Base API URL \u003e/{version}/user/event_filter  \n **OR**  \n\u003c Base API URL \u003e/{version}/admin/event_filter\n\nHTTP Method - POST\n\nRequest Body\n\n``` \n{\n    \"event_type\" : \"rmaker.event.node_connected\",\n    \"entity_id\" : \"\u003cnode_id\u003e\",\n    \"entity_type\" : \"NODE\",\n    \"enabled\": true,\n    \"enabled_for_integrations\" : [“\u003cunique_name_of_the_webhook”, “helloworldintegration”]\n}\n``` \n\\*At least one webhook_integration must be added under `enabled_for_integrations` for webhook to be called.  \nNote - This API needs to be called by the end user or the super admin user. This API enables the processing of the event specific to the node. In this case, this API enables the node connected event for the node_id provided in the request body.\n\n\\*The webhook_integration API is an admin API  \n\\*The event_filter API is a user and admin API  \n\n### Swagger File Path\n[RainMaker Swagger API Documentation][swagger] \n\n### Testing the Node Online event\n\nStep 1 - Connect the node to an MQTT broker using the ESP32 device or using a tool like MQTT.Fx\n\nStep 2 - After the device is connected, check the logs in CloudWatch Logs for **HelloRainmakerFunction** lambda. The details about this event should be printed in the logs. e.g\n```\n{\n    \"EventVersion\":\"v1\",\n    \"Id\":\"RainMakerEventId\",\n    \"EventType\":\"rmaker.event.node_connected\",\n    \"Timestamp\":\"\u003ctimestamp\u003e\",\n    \"Description\":\"\u003cuser_id\u003e(User)'s (Node)thing3 disconnection status is false\",\n    \"EventData\":{\n        \"UserId\":\"\u003cuser_id\u003e\",\n        \"NodeId\":\"\u003cnode_id\u003e\",\n        \"Connected\":true\n    }\n}\n```\n\nStep 3 - Disconnect the device\n\nStep 4 - Disable the SYSTEM event filter for this node, using the REST API provided above.\n\nStep 5 - Connect the node to the MQTT broker using an ESP32 device or using a tool like MQTT.Fx\n\nStep 6 - After the device is connected, check the logs in CloudWatch Logs. There should not be\nany log for this device.\n\u003e (To Watch logs go to AWS Services \\\u003e CloudWatch \\\u003e CloudWatch Logs \\\u003e Log groups \\\u003e /aws/lambda/HelloRainmakerFunction)\n\n\n### How to trigger other events? (Assuming their filters are enabled)\n\n1. Node Online:  \n   Connect the node to an MQTT Broker. This could mean connecting an ESP32 device to power, or connecting a node using an MQTT Client like MQTT.Fx\n\n2. Node Offline:  \n   Disconnect the node from the MQTT Broker. This could mean disconnecting an ESP32 device from power, or disconnecting a node from an MQTT Client like MQTT.Fx\n\n3. Add User Node sharing:  \n   Share a node with a user:\n   1. `PUT` Method at `{{base_url}}/v1/user/nodes/sharing/requests`:  \n\n        ```\n         {\n             \"nodes\": [\n                 \"node_id\"\n             ],\n             \"user_name\": \"secondary user_name\"\n         }\n         ```\n   2. **Login as the shared User** and call the `PUT` Method at `{{base_url}}/v1/user/nodes/sharing/requests`:  \n\n        ```\n         {\n             \"accept\": true,\n             \"request_id\": \"request_id_returned_in_last_call\"\n         }\n        ```\n\n4. Node Added to User Account: \n   1. Add a node through User-Node mapping:  \n    `PUT` Method at `{{base_url}}/v1/user/nodes/mapping`   \n\n        ```\n        {\n            \"node_id\": \"\u003cnode_id\u003e\",\n            \"secret_key\": \"\u003csample_secret_key\u003e\",\n            \"operation\": \"add\"\n        }\n        ```  \n       Node should send the following payload to `node/\u003cnode_id\u003e/user/mapping`:  \n\n        ```\n        {\n            \"node_id\": \"\u003cnode_id\u003e\",\n            \"user_id\": \"\u003cuser_id\u003e\",\n            \"secret_key\": \"\u003csame_secret_key\u003e\"\n         }\n        ```  \n   2. Receive a node through Node Sharing\n\n5. Node Removed from User: \n   1. Remove User-Node mapping:  \n    `PUT` Method at `{{base_url}}/v1/user/nodes/mapping`:  \n    \n       ```\n        {\n            \"node_id\": \"\u003cnode_id\u003e\",\n            \"operation\": \"remove\"\n        }\n        ```\n   2. Removed a node received from Sharing\n\n6. Node Parameters changed:  \n    Node should update it's parameters.  \n    It can send a similar payload to `node/\u003cnode_id\u003e/params/local`:  \n    ```\n    {\n        \"Lightbulb\": {\n            \"brightness\": 0\n        }\n    }\n    ```\n\n7. Node Alert:  \n    Node can send an alert(any JSON body) to `node/\u003cnode_id\u003e/alert`\n\n8. Node Automation Triggered:\n   1.  Setup Node Automation Trigger:  \n       `POST` Method at `{{base_url}}/v1/user/node_automation`: Refer [RainMaker API Documentation][swagger] \n    1. Trigger the Node Automation Condition:  \n        For example, if trigger event is *Brightness equals 100*, then set Node Brightness to 100.\n\n9.  Node group shared:  \n    Share a node group with a secondary user:  \n    1. `PUT` Method at `{{base_url}}/v1/user/node_group/sharing`:  \n\n        ```  \n        {\n          \"groups\": [\n              \"group_id1\"\n          ],\n          \"user_name\": \"secondary user_name\"\n        }\n        ```\n    2. **Login to Secondary User**, and call `PUT` Method at `{{base_url}}/v1/user/node_group/sharing/requests`:  \n\n        ```\n        {\n            \"accept\": true,  \n            \"request_id\": \"request_id_returned_in_last_call\"\n        }\n        ```\n10. Node group added:  \n    When a Node Group is shared with another User, the receiving User will get a `rmaker.event.user_node_group_added` event.\n11. Node group removed:  \n    Delete a Node Group sharing.  \n    `DELETE` Method at `{{base_url}}/v1/user/node_group/sharing?groups=\u003cnode_group_id\u003e\u0026user_name=\u003cuser_name\u003e`:  Refer [RainMaker API Documentation][swagger] \n12. Node Registered to Account:  \n    Use [Rainmaker Admin CLI][RainMakerAdminCLI] to generate and register nodes\n13. Admin User Added:  \n    Use [Rainmaker Admin CLI][RainMakerAdminCLI] to generate and register nodes\n14. New Tags Added:  \n    `PUT` Method at `{{base_url}}/v1/user/nodes?node_id=\u003cnode_id\u003e` to add a new tag that is not attached before. Refer [RainMaker API Documentation][swagger]  \n15. Existing Tags Attached:  \n    `PUT` Method at `{{base_url}}/v1/user/nodes?node_id=\u003cnode_id\u003e` to add a tag which was already attached elsewhere. Refer [RainMaker API Documentation][swagger]  \n16. Node Config changed:  \n    Publish a node's config to the MQTT topic `node/+/config` using an MQTT client. Refer [Sample config payload][SampleConfigPayload]    \n\n[swagger]: http://swaggerapis.rainmaker.espressif.com\n[AWSLambdaDoc]: https://aws.amazon.com/lambda/faqs/#:~:text=Q%3A%20What%20languages%20does%20AWS,our%20documentation%20on%20using%20Node\n[RainMakerAdminCLI]: https://github.com/espressif/esp-rainmaker-admin-cli\n[SampleConfigPayload]: https://rainmaker.espressif.com/docs/node-cloud-comm.html#appendix-sample-node-configuration\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fespressif%2Fesp-rainmaker-webhooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fespressif%2Fesp-rainmaker-webhooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fespressif%2Fesp-rainmaker-webhooks/lists"}