A Proof of Concept (POC) application that demonstrates how Slack, integrated with Heroku, can act as a Universal Approval Hub, centralizing approval requests from various Systems of Record (e.g., Workday, Concur, Salesforce).
Note: This is a demonstration application. The Workday, Concur, and Salesforce integrations are mocked - the app accepts requests via a REST API endpoint that simulates webhooks from these systems, but does not actually connect to external services. The application can be extended to integrate with real systems by implementing webhook handlers or API clients for each service.
- Slack Home Tab Dashboard: Personalized approval dashboard for managers
- Multi-Source Support: Handles approval requests from Workday, Concur, and Salesforce (mocked for demo purposes)
- Semantic Search: Natural language search using Heroku Managed Inference and pgvector
- AI-Powered Analysis: Automatic summarization and risk scoring using Claude
- Real-Time Updates: Instant UI updates when approvals are processed
- Secure Webhooks: Slack request verification and secure API endpoints
- Web Interface: User-friendly forms and status dashboard for creating and viewing test requests
- Backend: Flask (Python 3.11)
- Database: Heroku Postgres (or Heroku Postgres Advanced) with pgvector extension
- AI/ML: Heroku Managed Inference (Cohere embeddings, Claude chat completions)
- Deployment: Heroku with Gunicorn
- Integration: Slack Events API, Interactive Components, Home Tab
Note: This application is designed for Heroku deployment and uses Heroku Postgres. For production workloads requiring high performance and scale, consider Heroku Postgres Advanced, the next generation of Heroku Postgres with enhanced performance, scalability, and zero-friction operations.
- Python 3.11+
- Heroku account (for deployment)
- Heroku Postgres add-on (automatically configured via
DATABASE_URL) - Slack App with appropriate OAuth scopes
- Heroku Managed Inference add-on (optional, for AI features)
For Local Development Only: PostgreSQL 12+ with pgvector extension (see Local Development Setup below)
π Workshop Focus: This section covers Heroku deployment, which is the primary focus of the workshop. For local development setup (outside workshop scope), see Local Development Setup below.
This application is designed to run on Heroku with Heroku Postgres. The DATABASE_URL environment variable is automatically set when you attach a Postgres add-on to your Heroku app.
heroku create your-app-nameFor standard workloads:
heroku addons:create heroku-postgresql:essential-0 --app your-app-nameFor high-performance, scalable workloads, consider Heroku Postgres Advanced (pilot program):
- Sign up for the Heroku Postgres Advanced pilot
- Provides 4X+ throughput, 200TB+ storage, and zero-downtime scaling
- Once available, provision via Heroku dashboard or CLI
Note: Heroku Postgres Advanced will eventually replace Standard, Premium, Private, and Shield tiers. The application automatically detects and uses the
DATABASE_URLprovided by any Heroku Postgres add-on.
heroku addons:create heroku-managed-inference:starter --app your-app-nameheroku config:set SECRET_KEY=your-secret-key --app your-app-name
heroku config:set SLACK_BOT_TOKEN=xoxb-your-token --app your-app-name
heroku config:set SLACK_SIGNING_SECRET=your-signing-secret --app your-app-name
heroku config:set SLACK_APP_ID=your-app-id --app your-app-nameNote:
DATABASE_URLis automatically set by Heroku when you attach a Postgres add-on. You do not need to set it manually.
git push heroku mainThe application will automatically:
- Enable the pgvector extension
- Create all required tables
- Set up the database schema
You can also manually run the setup script:
heroku run python scripts/setup_db.py --app your-app-nameheroku run python scripts/seed_data.py --app your-app-nameCheck that your app is running:
heroku open --app your-app-nameView logs:
heroku logs --tail --app your-app-nameNote: Local development setup is outside the scope of the workshop. This section is provided for developers who want to run the application locally for development or testing purposes.
git clone <repository-url>
cd UniversalApprovalHubpython3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activatepip install -r requirements.txtbrew install postgresql@14
brew install pgvectordocker run -d \
--name approval-hub-db \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=approval_hub \
-p 5432:5432 \
pgvector/pgvector:pg14Create a .env file in the project root:
# Flask Configuration
FLASK_APP=app.py
FLASK_ENV=development
SECRET_KEY=your-secret-key-here
# Database Configuration
DATABASE_URL=postgresql://user:password@localhost:5432/approval_hub
# Slack Configuration
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_SIGNING_SECRET=your-signing-secret
SLACK_APP_ID=your-app-id
# Heroku Managed Inference (when add-on is provisioned)
HEROKU_MANAGED_INFERENCE_API_URL=https://api.heroku.com/managed-inference/v1
HEROKU_MANAGED_INFERENCE_API_KEY=your-api-key
# Logging
LOG_LEVEL=INFOpython scripts/setup_db.pypython scripts/seed_data.pypython app.pyThe application will be available at http://localhost:5000
β οΈ IMPORTANT: If you don't see the Home Tab interface when opening the app in Slack, you likely need to enable the Home Tab feature in your Slack app settings. See SLACK_SETUP.md for detailed step-by-step configuration instructions.
The following Bot Token scopes are required:
chat:write- Required - Send messages as the bot and manage Home Tab contentusers:read- Required - View people in the workspaceusers:read.email- Required - View email addresses of people in the workspaceapp_mentions:read- Read mentions of your app (optional, if using mentions)channels:history- View messages in public channels (optional, if needed)commands- Add slash commands (optional, if using slash commands)
Note: The
app_home:writescope is not available in Slack's current permission model. Thechat:writescope is sufficient for publishing Home Tab views viaviews.publish(). Additionally, the Home Tab endpoint (/slack/home) approach used by this app doesn't require any special scope beyondchat:write.
-
Navigate to your Slack App:
- Go to api.slack.com/apps
- Select your app (or create a new one)
-
Open OAuth & Permissions:
- In the left sidebar, click "OAuth & Permissions" (under Features)
-
Add Bot Token Scopes:
- Scroll down to the "Scopes" section
- Find the "Bot Token Scopes" subsection
- Click "Add New Scope" button
- Add each of the required scopes listed above:
chat:write(required)users:read(required)users:read.email(required)app_mentions:read(optional)channels:history(optional)commands(optional)
-
Install/Reinstall the App:
- After adding scopes, scroll to the top of the page
- Click "Install to Workspace" (or "Reinstall to Workspace" if already installed)
- Review the permissions and click "Allow"
-
Copy the Bot Token:
- After installation, you'll see "Bot User OAuth Token" at the top of the page
- Copy this token (starts with
xoxb-) - Set it as
SLACK_BOT_TOKENin your environment variables
Note: If you add new scopes after the app is already installed, you must reinstall the app to your workspace for the new scopes to take effect.
App-level tokens are NOT required for the current webhook-based implementation. This application uses:
- Events API (webhooks) for receiving events
- Interactive Components (webhooks) for button interactions
- Bot tokens for publishing Home Tab views and sending messages
If you plan to use Socket Mode instead of webhooks (for development or firewall-restricted environments), you would need an app-level token with:
connections:write- Connect to Slack via Socket Mode
To create an app-level token:
- Go to your Slack app settings β Basic Information
- Scroll to App-Level Tokens
- Click Generate Token and Scopes
- Add the
connections:writescope - Copy the token (starts with
xapp-)
Note: For production deployments on Heroku, webhooks are recommended as they're more reliable and don't require maintaining persistent WebSocket connections.
Configure the following events:
app_home_opened- Triggered when a user opens the Home Tab
Set the Request URL to:
https://your-app.herokuapp.com/slack/events
Enable Interactive Components and set the Request URL to:
https://your-app.herokuapp.com/slack/interactive
No slash commands are required for this POC.
β Swagger/OpenAPI Documentation Available: Interactive API documentation is available at
/api-docswhen the application is running. The documentation is automatically generated from Flask docstrings using Flasgger.
Access Swagger UI:
- Local:
http://localhost:5000/api-docs - Heroku:
https://your-app.herokuapp.com/api-docs
The codebase includes:
- Comprehensive docstrings: Sphinx-style with
:param:,:return:,:rtype:tags - Type hints: Function signatures include type hints using
typingmodule - Swagger/OpenAPI: Interactive documentation via Flasgger at
/api-docs
GET /health
Returns application health status.
Response:
{
"status": "healthy",
"service": "universal-approval-hub"
}POST /api/new-approval
Content-Type: application/json
Creates a new approval request from external system webhook (or web form).
Request Body:
{
"request_source": "Workday|Concur|Salesforce",
"requester_name": "John Doe",
"approver_id": "U123456",
"justification_text": "Request justification...",
"metadata": {
"date_range": "2024-01-01 to 2024-01-05",
"amount": 500.00,
...
}
}Response (201 Created):
{
"id": 1,
"status": "created",
"message": "Approval request created successfully"
}GET /api/requests?status=Pending&source=Workday&approver_id=U123456
Fetches approval requests with optional filtering.
Query Parameters:
status(optional): Filter by status (Pending, Approved, Rejected)source(optional): Filter by source (Workday, Concur, Salesforce)approver_id(optional): Filter by approver Slack User ID
Response (200 OK):
{
"requests": [...],
"count": 5
}POST /slack/events
Handles Slack Events API callbacks (URL verification, app_home_opened events).
POST /slack/interactive
Handles button clicks (approve/reject) and other interactive components.
POST /slack/home
Returns Home Tab view for a user (deprecated - use Events API instead).
| Field | Type | Description |
|---|---|---|
| id | SERIAL | Primary key |
| request_source | VARCHAR(50) | Source system (Workday, Concur, Salesforce) |
| requester_name | VARCHAR(100) | Employee name |
| approver_id | VARCHAR(50) | Slack User ID of approver |
| status | VARCHAR(20) | Pending, Approved, Rejected |
| justification_text | TEXT | Request justification |
| metadata_json | JSONB | Source-specific metadata |
| search_vector | VECTOR(1024) | Embedding vector for semantic search |
| created_at | TIMESTAMP | Creation timestamp |
| updated_at | TIMESTAMP | Last update timestamp |
Run the test suite:
pytestRun with coverage:
pytest --cov=. --cov-report=htmlTo test the approval flow without connecting to external systems, you can create mock approval requests. See TESTING.md for detailed instructions.
π Recommended: Web Interface (Perfect for Workshops/Demos)
The easiest way is through the web interface - no command line needed!
-
Open your browser and navigate to:
https://your-app.herokuapp.com/create-request -
Fill out the form and click "Create Request" - the request will appear in your Slack Home Tab!
-
You'll be redirected to the Status Dashboard (
/status) to view all requests and their statuses
Alternative Methods:
# On Heroku (script)
heroku run python scripts/create_test_request.py --samples --approver-id YOUR_SLACK_USER_ID --app your-app-name
# Or use the API
curl -X POST https://your-app.herokuapp.com/api/new-approval \
-H "Content-Type: application/json" \
-d '{
"request_source": "Workday",
"requester_name": "John Doe",
"approver_id": "YOUR_SLACK_USER_ID",
"justification_text": "Test PTO request",
"metadata": {"date_range": "2024-02-15 to 2024-02-22"}
}'UniversalApprovalHub/
βββ app.py # Main Flask application
βββ config.py # Configuration classes
βββ database.py # Database initialization
βββ models.py # SQLAlchemy models
βββ requirements.txt # Python dependencies
βββ Procfile # Heroku process definition
βββ .python-version # Python version specification
βββ routes/
β βββ __init__.py
β βββ api_routes.py # External API endpoints
β βββ slack_routes.py # Slack integration routes
βββ utils/
β βββ __init__.py
β βββ logging_config.py # Logging setup
β βββ heroku_inference.py # AI/ML integration
β βββ semantic_search.py # Vector search utilities
β βββ slack_utils.py # Slack helper functions
βββ migrations/
β βββ 001_initial_schema.sql # Database schema
βββ scripts/
β βββ setup_db.py # Database setup script
β βββ seed_data.py # Sample data seeding
βββ tests/
βββ conftest.py # Pytest fixtures
βββ test_api_routes.py
βββ test_slack_routes.py
βββ test_models.py
| Variable | Description | Required | Set By |
|---|---|---|---|
SECRET_KEY |
Flask secret key | Yes | Manual |
DATABASE_URL |
PostgreSQL connection string | Yes | Heroku (auto) |
SLACK_BOT_TOKEN |
Slack Bot User OAuth Token | Yes | Manual |
SLACK_SIGNING_SECRET |
Slack Signing Secret | Yes | Manual |
SLACK_APP_ID |
Slack App ID | Yes | Manual |
HEROKU_MANAGED_INFERENCE_API_URL |
Managed Inference API URL | No | Heroku (auto) |
HEROKU_MANAGED_INFERENCE_API_KEY |
Managed Inference API Key | No | Heroku (auto) |
LOG_LEVEL |
Logging level (DEBUG, INFO, WARNING, ERROR) | No | Manual |
Important:
DATABASE_URLis automatically configured by Heroku when you attach a Postgres add-on. The application handles bothpostgres://andpostgresql://connection strings and configures SSL automatically for Heroku Postgres.
On Heroku:
- Verify Postgres add-on is attached:
heroku addons - Check
DATABASE_URLis set:heroku config:get DATABASE_URL - The application automatically enables pgvector extension on first run
- View database logs:
heroku logs --ps postgres
Local Development:
- Ensure PostgreSQL is running locally
- Verify
DATABASE_URLin.envfile is correct - Check that pgvector extension is installed:
CREATE EXTENSION vector;
- Verify all OAuth scopes are granted
- Check that Event Subscriptions URL is correct
- Ensure signing secret matches Slack app configuration
- Verify add-on is provisioned:
heroku addons - Check environment variables are set:
heroku config - Review logs:
heroku logs --tail
- Follow PEP 8 style guidelines
- Include docstrings for all functions and classes (Sphinx-style with
:param:,:return:,:rtype:) - Add type hints to function signatures (using
typingmodule) - Write tests for new features
- Run tests before submitting:
pytest(orpytest --cov=.for coverage) - Update documentation as needed
The project uses:
- Docstrings: Sphinx-style docstrings with
:param:,:return:,:rtype:tags - Type Hints: Function signatures include type hints using
typingmodule - API Documentation: Currently manual (see API Endpoints section above). Swagger/OpenAPI not yet implemented.
# Install test dependencies (if not already installed)
pip install -r requirements.txt
# Run all tests
pytest
# Run with coverage report
pytest --cov=. --cov-report=html
# Run specific test file
pytest tests/test_api_routes.pyThis project is licensed under the Apache License 2.0. See the LICENSE file for details.
For issues and questions, please create an issue or contact the development team.