Authentication
Secure your Hub API with Bearer token authentication.
How It Works
Section titled “How It Works”Hub requires Bearer token authentication. All API endpoints require the Authorization header with a Bearer token, except the health check endpoint, which is public.
Configuring Authentication
Section titled “Configuring Authentication”1. Set Environment Variable
Section titled “1. Set Environment Variable”Add API_KEY to your .env file:
API_KEY=your-secret-key-here# macOS/Linuxopenssl rand -base64 32
# Or use a password manager's random generator2. Start the Service
Section titled “2. Start the Service”make run3. Include Token in Requests
Section titled “3. Include Token in Requests”All API requests except GET /health require the Authorization header:
curl -H "Authorization: Bearer your-secret-key-here" \ http://localhost:8080/v1/feedback-recordsProtected Endpoints
Section titled “Protected Endpoints”These endpoints require authentication:
POST /v1/feedback-records- Create feedback recordGET /v1/feedback-records- List feedback recordsGET /v1/feedback-records/{id}- Get feedback recordPATCH /v1/feedback-records/{id}- Update feedback recordDELETE /v1/feedback-records/{id}- Delete feedback recordDELETE /v1/feedback-records?user_identifier=<id>- Bulk delete feedback records by user
Always public (no auth required):
GET /health- Health check
The OpenAPI spec is maintained in the repo as openapi.yaml and used for contract tests (Schemathesis). Serving it at runtime (for example, GET /openapi.json or GET /docs) is planned.
Authentication Workflow
Section titled “Authentication Workflow”Request Flow
Section titled “Request Flow”Client Request |Has Authorization header? |No -> 401 UnauthorizedYes -> Is format "Bearer <token>"? |No -> 401 UnauthorizedYes -> Token matches API_KEY? |No -> 401 UnauthorizedYes -> Process Request |200 SuccessExample Requests
Section titled “Example Requests”Without authentication:
curl http://localhost:8080/v1/feedback-recordsResponse:
{ "title": "Unauthorized", "status": 401, "detail": "Missing Authorization header"}With authentication:
curl -H "Authorization: Bearer your-secret-key-here" \ http://localhost:8080/v1/feedback-recordsResponse:
{ "data": [...], "total": 10}Production Deployment
Section titled “Production Deployment”Hub’s built-in Bearer token authentication is ideal for:
- Internal services and tools
- Server-to-server communication
- Quick production deployments
For public APIs or multi-tenant systems, consider deploying behind an API gateway (AWS API Gateway, Kong, Traefik) for advanced features like OAuth, JWT, or per-user access control.
Docker Deployment with Auth
Section titled “Docker Deployment with Auth”docker run -d \ -p 8080:8080 \ -e DATABASE_URL=postgres://... \ -e API_KEY=your-secret-key-here \ ghcr.io/formbricks/hub:latestEnvironment Variables
Section titled “Environment Variables”# Database connectionDATABASE_URL=postgres://user:pass@host/db
# Authentication (required)API_KEY=your-secret-key-here
# OptionalLOG_LEVEL=infoPORT=8080See full environment variable reference ->
Security Best Practices
Section titled “Security Best Practices”1. Use HTTPS in Production
Section titled “1. Use HTTPS in Production”Always use HTTPS to encrypt tokens in transit:
http://api.example.com (insecure)https://api.example.com (secure)2. Store Keys Securely
Section titled “2. Store Keys Securely”Good practices:
- Use environment variables (not hardcoded)
- Store in secret management (AWS Secrets Manager, HashiCorp Vault)
- Rotate keys regularly
Avoid:
- Committing keys to Git
- Sharing keys via email/Slack
- Using the same key for dev and prod
3. Rotate Keys Regularly
Section titled “3. Rotate Keys Regularly”Schedule regular key rotation (for example, every 90 days):
- Generate new key
- Update
API_KEYin production - Restart service
- Update clients with new key
4. Monitor for Unauthorized Access
Section titled “4. Monitor for Unauthorized Access”Enable structured logging to track failed auth attempts:
LOG_LEVEL=infoLook for 401 responses in logs:
{ "level": "warn", "msg": "unauthorized request", "path": "/v1/feedback-records", "ip": "203.0.113.1"}Troubleshooting
Section titled “Troubleshooting”401 Unauthorized Error
Section titled “401 Unauthorized Error”Symptom:
{ "title": "Unauthorized", "status": 401, "detail": "Missing Authorization header"}Solutions:
- Check if
API_KEYis set:
grep API_KEY .env- Verify header format:
# Correctcurl -H "Authorization: Bearer your-key" http://...
# Wrong (missing Bearer)curl -H "Authorization: your-key" http://...
# Wrong (using X-API-Key instead)curl -H "X-API-Key: your-key" http://...- Check key matches
.env:
cat .env | grep API_KEYHealth Check Returns 401
Section titled “Health Check Returns 401”Health checks (/health) should never require auth.
If you see this, it’s a bug. Please report it.
Future Spec Endpoint
Section titled “Future Spec Endpoint”If you add a /docs or /openapi.json endpoint to serve the API spec, it should remain public (no auth).
Examples
Section titled “Examples”curl -H "Authorization: Bearer abc123" \ -X POST http://localhost:8080/v1/feedback-records \ -H "Content-Type: application/json" \ -d '{"source_type":"survey","field_id":"q1","field_type":"text","value_text":"Great!"}'JavaScript (Node.js)
Section titled “JavaScript (Node.js)”const response = await fetch("http://localhost:8080/v1/feedback-records", { headers: { Authorization: "Bearer abc123", "Content-Type": "application/json", }, method: "POST", body: JSON.stringify({ source_type: "survey", field_id: "q1", field_type: "text", value_text: "Great!", }),});Python
Section titled “Python”import requests
headers = { "Authorization": "Bearer abc123", "Content-Type": "application/json",}
response = requests.get( "http://localhost:8080/v1/feedback-records", headers=headers)client := &http.Client{}req, _ := http.NewRequest("GET", "http://localhost:8080/v1/feedback-records", nil)req.Header.Set("Authorization", "Bearer abc123")resp, err := client.Do(req)Next Steps
Section titled “Next Steps”- Data Model - Understand the feedback record structure
- API Reference - Explore endpoints
- Environment Variables - Configuration reference