{"id":19450181,"url":"https://github.com/webexsamples/webhook-to-card","last_synced_at":"2025-07-27T06:34:44.972Z","repository":{"id":237315138,"uuid":"618610520","full_name":"WebexSamples/webhook-to-card","owner":"WebexSamples","description":"This is a sample Python application that demonstrates how to receive a webhook and convert the data into an adaptive card that is posted into a Webex space by a Webex bot.","archived":false,"fork":false,"pushed_at":"2024-05-07T17:50:33.000Z","size":7,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-01-07T23:41:36.071Z","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":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WebexSamples.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":"2023-03-24T21:20:05.000Z","updated_at":"2024-05-07T17:50:34.000Z","dependencies_parsed_at":"2024-05-07T18:45:32.128Z","dependency_job_id":"3db8fc10-da2f-443e-a0f4-45aef0a4d326","html_url":"https://github.com/WebexSamples/webhook-to-card","commit_stats":null,"previous_names":["webexsamples/webhook-to-card"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fwebhook-to-card","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fwebhook-to-card/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fwebhook-to-card/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WebexSamples%2Fwebhook-to-card/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WebexSamples","download_url":"https://codeload.github.com/WebexSamples/webhook-to-card/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240637241,"owners_count":19833029,"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-11-10T16:35:36.917Z","updated_at":"2025-07-27T06:34:44.967Z","avatar_url":"https://github.com/WebexSamples.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# 🚀 Webhook to Adaptive Card for Webex Integration\n\nThis is a sample Python application that demonstrates how to receive a webhook and convert the data into an adaptive card that is posted into a Webex space by a Webex bot. The application showcases a rocket launch notification system with rich, interactive cards that provide detailed mission information.\n\n## ✨ Features\n\n- **🔗 Webhook Reception** - RESTful endpoint for receiving external webhook data\n- **🎴 Adaptive Card Generation** - Dynamic creation of rich, interactive cards\n- **🤖 Webex Bot Integration** - Automated posting to Webex spaces\n- **🚀 Rocket Launch Theme** - Example implementation with space mission data\n- **⚡ Flask Web Framework** - Lightweight, scalable web server\n- **🔧 Environment Configuration** - Secure credential management\n- **📊 Status Monitoring** - Health check endpoint for service monitoring\n\n## 📋 Prerequisites\n\n- Python 3.6+\n- Flask\n- Requests\n- Adaptive Card JSON schema\n\n## 🚀 Getting Started\n\n### Installation \u0026 Setup\n\n1. **Clone this repository:**\n   ```bash\n   git clone \u003crepository-url\u003e\n   cd webhook-to-card\n   ```\n\n2. **Install the required packages:**\n   ```bash\n   pip install -r requirements.txt\n   ```\n\n3. **Configure environment variables:**\n   - Rename `.env.example` to `.env`\n   - Replace the variables with your own values:\n     - `WEBEX_ACCESS_TOKEN`: Your Webex bot access token\n     - `WEBEX_ROOM_ID`: The ID of the Webex room where the adaptive card will be posted\n\n4. **Start the application:**\n   ```bash\n   python app.py\n   ```\n\n5. **Test the webhook endpoint:**\n   Send a POST request to the `/webhook` endpoint with a JSON payload that contains the data for the adaptive card.\n\n   Example:\n   ```bash\n   curl -X POST -H \"Content-Type: application/json\" -d @webhook-payload.json http://localhost:5000/webhook\n   ```\n\n6. **Verify the result:**\n   The application will parse the JSON payload, generate an adaptive card, and post it into the Webex room specified by `WEBEX_ROOM_ID`.\n\n## 📖 Usage Guide\n\n### Webhook Payload Structure\n\nThe application expects a specific JSON structure for rocket launch data:\n\n```json\n{\n    \"event\": \"rocket_launch\",\n    \"data\": {\n        \"rocket_name\": \"Falcon 9 Webex\",\n        \"payload_type\": \"Satellite\",\n        \"payload_description\": \"Communications satellite for commercial use\",\n        \"launch_time\": \"2023-03-25T16:30:00Z\",\n        \"launch_site\": \"Cape Canaveral, FL\",\n        \"mission_patch\": \"https://example.com/mission_patch.png\",\n        \"video_stream\": \"https://example.com/live_stream.mp4\"\n    }\n}\n```\n\n### Environment Configuration\n\nCreate a `.env` file with the following variables:\n\n```bash\n# Webex Bot Token (from developer.webex.com)\nWEBEX_BOT_TOKEN=your_bot_access_token_here\n\n# Target Webex Room ID\nWEBEX_ROOM_ID=your_room_id_here\n```\n\n### Testing the Integration\n\n1. **Check Service Status:**\n   ```bash\n   curl http://localhost:5000/status\n   ```\n\n2. **Send Test Webhook:**\n   ```bash\n   curl -X POST \\\n     -H \"Content-Type: application/json\" \\\n     -d @webhook-payload.json \\\n     http://localhost:5000/webhook\n   ```\n\n3. **Verify in Webex:**\n   - Check your configured Webex space\n   - Look for the adaptive card with rocket launch details\n   - Interact with the \"Watch the Launch\" button\n\n## 🏗️ Project Structure\n\n```\nwebhook-to-card/\n├── app.py                     # Main Flask application\n├── requirements.txt           # Python dependencies\n├── .env.example              # Environment variables template\n├── webhook-payload.json      # Sample webhook data\n├── adaptive_card.json        # Example adaptive card structure\n├── templates/\n│   └── status.html           # Status page template\n├── .gitignore                # Git ignore patterns\n├── LICENSE                   # Cisco Sample Code License\n└── README.MD                 # This documentation\n```\n\n### Core Components\n\n| Component | Description | File Location |\n|-----------|-------------|---------------|\n| **Flask App** | Main web server and routing | [`app.py`](app.py) |\n| **Webhook Handler** | Processes incoming webhook data | [`app.py`](app.py) lines 20-131 |\n| **Card Generator** | Creates adaptive card from data | [`app.py`](app.py) lines 36-111 |\n| **Webex Integration** | Posts cards to Webex spaces | [`app.py`](app.py) lines 114-131 |\n| **Status Monitor** | Health check endpoint | [`app.py`](app.py) lines 134-136 |\n\n## 🔧 Code Implementation\n\n### Flask Application Setup\n\n```python\nfrom flask import Flask, request, jsonify, render_template\nfrom dotenv import load_dotenv\nimport os, json, requests\n\n# Load environment variables\nload_dotenv()\n\napp = Flask(__name__)\n\n# Webex API configuration\napi_url = \"https://webexapis.com/v1/messages\"\nheaders = {\n    \"Content-Type\": \"application/json\",\n    \"Authorization\": \"Bearer \" + os.getenv(\"WEBEX_BOT_TOKEN\")\n}\n```\n\n### Webhook Processing\n\n```python\n@app.route(\"/webhook\", methods=[\"POST\"])\ndef handle_webhook():\n    # Extract webhook data\n    webhook = request.get_json()\n    data = webhook[\"data\"]\n    \n    # Parse rocket launch details\n    rocket_name = data[\"rocket_name\"]\n    payload_type = data[\"payload_type\"]\n    payload_description = data[\"payload_description\"]\n    launch_time = data[\"launch_time\"]\n    launch_site = data[\"launch_site\"]\n    mission_patch = data[\"mission_patch\"]\n    video_stream = data[\"video_stream\"]\n```\n\n### Adaptive Card Generation\n\n```python\n# Dynamic card payload creation\ncard_payload = {\n    \"$schema\": \"http://adaptivecards.io/schemas/adaptive-card.json\",\n    \"type\": \"AdaptiveCard\",\n    \"version\": \"1.3\",\n    \"body\": [\n        {\n            \"type\": \"TextBlock\",\n            \"text\": \"Rocket Launch Successful!\",\n            \"weight\": \"Bolder\",\n            \"size\": \"Large\",\n            \"color\": \"Accent\",\n            \"wrap\": True\n        },\n        {\n            \"type\": \"ColumnSet\",\n            \"columns\": [\n                {\n                    \"type\": \"Column\",\n                    \"width\": \"auto\",\n                    \"items\": [\n                        {\n                            \"type\": \"Image\",\n                            \"url\": mission_patch,\n                            \"size\": \"Small\",\n                            \"style\": \"Person\"\n                        }\n                    ]\n                },\n                {\n                    \"type\": \"Column\",\n                    \"width\": \"stretch\",\n                    \"items\": [\n                        {\n                            \"type\": \"TextBlock\",\n                            \"text\": \"Rocket Launch Details\",\n                            \"weight\": \"Bolder\",\n                            \"wrap\": True\n                        },\n                        {\n                            \"type\": \"FactSet\",\n                            \"facts\": [\n                                {\n                                    \"title\": \"Rocket Name\",\n                                    \"value\": rocket_name\n                                },\n                                {\n                                    \"title\": \"Payload Type\",\n                                    \"value\": payload_type\n                                },\n                                {\n                                    \"title\": \"Launch Time\",\n                                    \"value\": launch_time\n                                },\n                                {\n                                    \"title\": \"Launch Site\",\n                                    \"value\": launch_site\n                                }\n                            ]\n                        }\n                    ]\n                }\n            ]\n        }\n    ],\n    \"actions\": [\n        {\n            \"type\": \"Action.OpenUrl\",\n            \"title\": \"Watch the Launch\",\n            \"url\": video_stream\n        }\n    ]\n}\n```\n\n### Webex API Integration\n\n```python\n# Message payload for Webex\nmessage_payload = {\n    \"roomId\": os.getenv(\"WEBEX_ROOM_ID\"),\n    \"attachments\": [\n        {\n            \"contentType\": \"application/vnd.microsoft.card.adaptive\",\n            \"content\": card_payload\n        }\n    ],\n    \"text\": \"New Rocket Launch Detected\"\n}\n\n# Send to Webex API\nresponse = requests.post(api_url, headers=headers, json=message_payload)\n```\n\n## 🎴 Adaptive Card Features\n\n### Card Structure\n\nThe generated adaptive card includes:\n\n1. **Header Section:**\n   - Bold title: \"Rocket Launch Successful!\"\n   - Accent color for visual emphasis\n   - Large size for prominence\n\n2. **Content Layout:**\n   - Two-column design with mission patch and details\n   - Mission patch image (left column)\n   - Fact set with structured data (right column)\n\n3. **Interactive Elements:**\n   - \"Watch the Launch\" action button\n   - Opens video stream in new window/tab\n\n### Fact Set Data\n\n| Field | Description | Source |\n|-------|-------------|--------|\n| **Rocket Name** | Launch vehicle identifier | `data.rocket_name` |\n| **Payload Type** | Cargo classification | `data.payload_type` |\n| **Payload Description** | Mission details | `data.payload_description` |\n| **Launch Time** | ISO 8601 timestamp | `data.launch_time` |\n| **Launch Site** | Geographic location | `data.launch_site` |\n\n### Visual Elements\n\n```json\n{\n  \"type\": \"Image\",\n  \"url\": \"mission_patch_url\",\n  \"size\": \"Small\",\n  \"style\": \"Person\"\n}\n```\n\n## 🔐 Security Considerations\n\n### Environment Variables\n\n- Store sensitive tokens in `.env` file\n- Never commit `.env` to version control\n- Use environment-specific configurations\n\n### Bot Token Management\n\n```python\n# Secure token loading\nheaders = {\n    \"Authorization\": \"Bearer \" + os.getenv(\"WEBEX_BOT_TOKEN\")\n}\n```\n\n### Input Validation\n\n```python\n# Validate webhook structure\nwebhook = request.get_json()\nif \"data\" not in webhook:\n    return jsonify({\"error\": \"Invalid payload\"}), 400\n```\n\n## 🧪 Testing\n\n### Local Development\n\n1. **Start the Flask server:**\n   ```bash\n   python app.py\n   ```\n\n2. **Test status endpoint:**\n   ```bash\n   curl http://localhost:5000/status\n   # Expected: \"Webhook Server Listening\"\n   ```\n\n3. **Send test webhook:**\n   ```bash\n   curl -X POST \\\n     -H \"Content-Type: application/json\" \\\n     -d @webhook-payload.json \\\n     http://localhost:5000/webhook\n   ```\n\n### Production Deployment\n\n```python\n# For production, configure proper WSGI server\nif __name__ == \"__main__\":\n    app.run(host='0.0.0.0', port=5000, debug=False)\n```\n\n### Error Handling\n\n```python\n# Response validation\nif response.ok:\n    return jsonify({\"success\": True}), 200\nelse:\n    return jsonify({\n        \"success\": False, \n        \"message\": response.text\n    }), response.status_code\n```\n\n## 🔧 Customization\n\n### Extending the Card Schema\n\n```python\n# Add new fields to webhook payload\ndef extract_additional_data(data):\n    return {\n        \"mission_status\": data.get(\"mission_status\", \"Unknown\"),\n        \"weather_conditions\": data.get(\"weather\", \"Clear\"),\n        \"crew_count\": data.get(\"crew_count\", 0)\n    }\n```\n\n### Multiple Card Templates\n\n```python\n# Template selection based on event type\ndef get_card_template(event_type):\n    templates = {\n        \"rocket_launch\": create_launch_card,\n        \"mission_update\": create_update_card,\n        \"abort_sequence\": create_abort_card\n    }\n    return templates.get(event_type, create_default_card)\n```\n\n### Custom Webhook Sources\n\n```python\n# Support multiple webhook formats\n@app.route(\"/webhook/\u003csource\u003e\", methods=[\"POST\"])\ndef handle_webhook_by_source(source):\n    parsers = {\n        \"spacex\": parse_spacex_webhook,\n        \"nasa\": parse_nasa_webhook,\n        \"generic\": parse_generic_webhook\n    }\n    \n    parser = parsers.get(source, parse_generic_webhook)\n    return parser(request.get_json())\n```\n\n## 📚 Dependencies\n\n### Core Requirements\n\n```txt\nFlask==2.2.3              # Web framework\nrequests==2.28.2          # HTTP library\npython-dotenv==1.0.0      # Environment variable management\nJinja2==3.1.2             # Template engine\nWerkzeug==2.2.3           # WSGI utilities\n```\n\n### Development Tools\n\n```txt\ncertifi==2022.12.7        # SSL certificate bundle\ncharset-normalizer==3.0.1 # Character encoding\nclick==8.1.3              # Command line interface\nidna==3.4                 # Internationalized domain names\nitsdangerous==2.1.2       # Cryptographic signing\nMarkupSafe==2.1.2         # String handling\nurllib3==1.26.14          # HTTP client\n```\n\n## 🚨 Troubleshooting\n\n### Common Issues\n\n| Issue | Solution |\n|-------|----------|\n| **401 Unauthorized** | Check WEBEX_BOT_TOKEN validity |\n| **404 Room Not Found** | Verify WEBEX_ROOM_ID exists and bot has access |\n| **Invalid JSON** | Validate webhook payload structure |\n| **Module Not Found** | Run `pip install -r requirements.txt` |\n\n### Debug Mode\n\n```python\n# Enable Flask debug mode\nif __name__ == \"__main__\":\n    app.run(debug=True)\n```\n\n### Logging\n\n```python\nimport logging\n\n# Configure logging\nlogging.basicConfig(level=logging.INFO)\nlogger = logging.getLogger(__name__)\n\n# Add to webhook handler\nlogger.info(f\"Received webhook: {webhook}\")\nlogger.info(f\"Generated card: {card_payload}\")\n```\n\n## 🔄 Webhook Integration Examples\n\n### GitHub Actions\n\n```yaml\n# .github/workflows/notify-webex.yml\nname: Notify Webex on Launch\non:\n  push:\n    tags: ['v*']\njobs:\n  notify:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Send Launch Notification\n        run: |\n          curl -X POST \\\n            -H \"Content-Type: application/json\" \\\n            -d '{\"event\":\"rocket_launch\",\"data\":{\"rocket_name\":\"${{ github.ref_name }}\"}}' \\\n            ${{ secrets.WEBHOOK_URL }}/webhook\n```\n\n### External Monitoring\n\n```bash\n# Cron job for regular status checks\n*/5 * * * * curl -f http://your-domain.com/status || echo \"Webhook service down\"\n```\n\n## 🤝 Contributing\n\nWe truly appreciate your contribution to the Webex Samples!\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feature/card-enhancement`\n3. Commit changes: `git commit -am 'Add card feature'`\n4. Push to branch: `git push origin feature/card-enhancement`\n5. Submit a Pull Request\n\n### Development Guidelines\n\n- Follow PEP 8 Python style guidelines\n- Add error handling for new webhook sources\n- Test adaptive cards in Webex client\n- Update documentation for new features\n- Validate JSON schemas for new card types\n\n## 📚 Acknowledgements\n\nThis sample application was created using the following resources:\n\n- [Webex API Documentation](https://developer.webex.com/docs/api/getting-started)\n- [Adaptive Cards Documentation](https://adaptivecards.io/)\n- [ChatGPT](https://chat.openai.com)\n\n### Additional Resources\n\n- [Adaptive Cards Designer](https://adaptivecards.io/designer/)\n- [Webex Bot Creation Guide](https://developer.webex.com/docs/bots)\n- [Flask Documentation](https://flask.palletsprojects.com/)\n- [Python Webhooks Guide](https://webhooks.fyi/)\n\n## 📄 License\n\nThis project is licensed under the Cisco Sample Code License - see the [LICENSE](LICENSE) file for details.\n\n## 🆘 Support\n\nFor technical support and questions:\n\n- **Issues**: Submit via GitHub Issues\n- **Adaptive Cards**: [Microsoft Adaptive Cards Documentation](https://docs.microsoft.com/en-us/adaptive-cards/)\n- **Webex API**: [Webex Developer Portal](https://developer.webex.com)\n- **Community**: [Webex Developer Community](https://developer.webex.com/community)\n\n## Thanks!\n\nMade with ❤️ by the Webex Developer Relations Team at Cisco\n\n---\n\n**Note**: This sample demonstrates webhook-to-card conversion for educational purposes. For production use, implement proper error handling, input validation, and security measures.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebexsamples%2Fwebhook-to-card","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebexsamples%2Fwebhook-to-card","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebexsamples%2Fwebhook-to-card/lists"}