Managing Secrets
This comprehensive guide walks you through creating, updating, and managing secrets for your webhook automation workflows.
Prerequisites
Before you begin, ensure you have:
- A Hooklistener account on the Team or Enterprise plan (secrets not available on Free plan)
- API access token (available in your account settings)
- Understanding of your webhook integration requirements
- The sensitive data (API keys, tokens) you need to store securely
Creating Secrets
Step 1: Identify What to Store
Common secrets you might need:
API Authentication
SLACK_BOT_TOKEN
- Slack app bot tokenSTRIPE_API_KEY
- Stripe secret API keyGITHUB_TOKEN
- GitHub personal access tokenTELEGRAM_BOT_TOKEN
- Telegram bot API token
Webhook Security
WEBHOOK_SECRET
- Shared secret for webhook signature verificationHMAC_SECRET
- Secret for HMAC signature generationAPI_SECRET
- General API authentication secret
Database and Services
DATABASE_URL
- Database connection stringREDIS_URL
- Redis connection URLENCRYPTION_KEY
- Application-specific encryption key
Step 2: Create a Secret via API
Use the Secrets API to securely store your sensitive data:
curl -X POST https://api.hooklistener.com/api/v1/secrets \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": {
"name": "SLACK_BOT_TOKEN",
"value": "xoxb-123456789012-1234567890123-abcdefghijklmnopqrstuvwx",
"description": "Bot token for #alerts channel integration"
}
}'
Response:
{
"data": {
"id": "secret_abc123",
"name": "SLACK_BOT_TOKEN",
"description": "Bot token for #alerts channel integration",
"organization_id": "org_xyz789",
"is_active": true,
"last_used_at": null,
"last_rotated_at": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
}
Note: The secret value is never returned in API responses for security.
Step 3: Create Multiple Secrets
For comprehensive webhook automation, create all needed secrets:
# Telegram Bot Token
curl -X POST https://api.hooklistener.com/api/v1/secrets \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": {
"name": "TELEGRAM_BOT_TOKEN",
"value": "123456789:ABCdefGHIjklmnoPQRsTuVwxyZ",
"description": "Bot token for notification alerts"
}
}'
# API Key for external service
curl -X POST https://api.hooklistener.com/api/v1/secrets \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": {
"name": "EXTERNAL_API_KEY",
"value": "sk_live_abcdef123456789",
"description": "Production API key for payment processing"
}
}'
# Webhook signature secret
curl -X POST https://api.hooklistener.com/api/v1/secrets \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": {
"name": "WEBHOOK_SECRET",
"value": "whsec_ultra_secure_secret_key_2024",
"description": "Secret for webhook signature verification"
}
}'
Listing and Viewing Secrets
View All Secrets
Get a complete list of your organization's secrets:
curl -X GET https://api.hooklistener.com/api/v1/secrets \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response:
{
"data": [
{
"id": "secret_abc123",
"name": "SLACK_BOT_TOKEN",
"description": "Bot token for #alerts channel integration",
"is_active": true,
"last_used_at": "2024-01-15T14:22:30Z",
"last_rotated_at": null,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
},
{
"id": "secret_def456",
"name": "TELEGRAM_BOT_TOKEN",
"description": "Bot token for notification alerts",
"is_active": true,
"last_used_at": null,
"last_rotated_at": null,
"created_at": "2024-01-15T10:45:00Z",
"updated_at": "2024-01-15T10:45:00Z"
}
]
}
View Individual Secret Details
Get details for a specific secret (without the value):
curl -X GET https://api.hooklistener.com/api/v1/secrets/secret_abc123 \
-H "Authorization: Bearer YOUR_API_TOKEN"
View Secret Value (Use Carefully)
For debugging or verification, you can retrieve the decrypted value:
curl -X GET https://api.hooklistener.com/api/v1/secrets/secret_abc123/value \
-H "Authorization: Bearer YOUR_API_TOKEN"
Response:
{
"id": "secret_abc123",
"name": "SLACK_BOT_TOKEN",
"value": "xoxb-123456789012-1234567890123-abcdefghijklmnopqrstuvwx",
"decrypted_at": "2024-01-15T15:00:00Z"
}
⚠️ Security Note: Only use the /value
endpoint when absolutely necessary. The decrypted value should never be logged or stored elsewhere.
Using Secrets
Secrets can be used in two different ways within Hooklistener:
1. URL Template Substitution
The most straightforward way to use secrets is directly in destination URLs using template syntax {{SECRET_NAME}}
:
Example: Telegram Bot Integration
Destination URL: https://api.telegram.org/bot{{TELEGRAM_BOT_TOKEN}}/sendMessage
Example: Slack Webhook
Destination URL: https://hooks.slack.com/services/{{SLACK_WEBHOOK_PATH}}
Example: Custom API with Multiple Secrets
Destination URL: https://api.example.com/webhook?token={{API_TOKEN}}&secret={{WEBHOOK_SECRET}}
Benefits of URL Templates:
- No code required - secrets are automatically substituted
- Secure credential management without exposing tokens in connection config
- Automatic updates when secret values are rotated
- Clean, readable destination URLs
2. Transformation Access
For more complex logic, access secrets programmatically within transformations via process.env
:
addHandler('transform', async (request, context) => {
// Access secrets using environment variable syntax
const slackToken = process.env.SLACK_BOT_TOKEN;
const telegramToken = process.env.TELEGRAM_BOT_TOKEN;
const apiKey = process.env.EXTERNAL_API_KEY;
console.log(`Loaded ${Object.keys(process.env).length} organization secrets`);
return {
...request,
headers: {
...request.headers,
'Authorization': `Bearer ${apiKey}`
}
};
});
Validation and Error Handling
Always validate that required secrets exist:
addHandler('transform', async (request, context) => {
// Check for required secrets
const requiredSecrets = ['SLACK_BOT_TOKEN', 'WEBHOOK_SECRET'];
const missingSecrets = requiredSecrets.filter(name => !process.env[name]);
if (missingSecrets.length > 0) {
console.log('Missing required secrets:', missingSecrets);
// Decide how to handle: skip transformation, return error, etc.
return {
...request,
body: {
...request.body,
error: 'Missing required secrets',
missing_secrets: missingSecrets
}
};
}
// Proceed with transformation using secrets
const slackToken = process.env.SLACK_BOT_TOKEN;
const webhookSecret = process.env.WEBHOOK_SECRET;
// Your transformation logic here
return transformedRequest;
});
Testing Secret Access
Create a simple transformation to test secret availability:
addHandler('transform', async (request, context) => {
// Log available secrets (names only, never values)
const secretNames = Object.keys(process.env);
console.log('Available secrets:', secretNames);
// Test specific secret
if (process.env.SLACK_BOT_TOKEN) {
console.log('✅ SLACK_BOT_TOKEN is available');
} else {
console.log('❌ SLACK_BOT_TOKEN is missing');
}
return request; // Return unchanged for testing
});
Updating Secrets
Update Secret Value
When API keys rotate or tokens expire, update the secret value:
curl -X PATCH https://api.hooklistener.com/api/v1/secrets/secret_abc123 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": {
"value": "xoxb-NEW_TOKEN_VALUE_HERE",
"description": "Updated bot token after rotation"
}
}'
Update Only Metadata
Update description without changing the secret value:
curl -X PATCH https://api.hooklistener.com/api/v1/secrets/secret_abc123 \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"secret": {
"description": "Updated description for Slack integration"
}
}'
Monitoring Secret Usage
Check Usage Patterns
Review the API response to monitor secret usage:
{
"data": {
"name": "SLACK_BOT_TOKEN",
"last_used_at": "2024-01-15T14:22:30Z",
"last_rotated_at": "2024-01-10T09:00:00Z",
"created_at": "2024-01-01T10:30:00Z"
}
}
Usage Indicators:
last_used_at: null
- Secret never been used in transformations- Recent
last_used_at
- Secret actively used - Old
last_used_at
- Potentially unused secret to clean up
Identify Unused Secrets
List all secrets and identify candidates for cleanup:
curl -X GET https://api.hooklistener.com/api/v1/secrets \
-H "Authorization: Bearer YOUR_API_TOKEN" \
| jq '.data[] | select(.last_used_at == null or (now - (.last_used_at | fromdateiso8601)) > 2592000) | {name: .name, last_used: .last_used_at}'
This command finds secrets unused for over 30 days (2592000 seconds).
Deleting Secrets
Soft Delete (Recommended)
Secrets are soft-deleted for audit purposes:
curl -X DELETE https://api.hooklistener.com/api/v1/secrets/secret_abc123 \
-H "Authorization: Bearer YOUR_API_TOKEN"
What happens:
- Secret immediately becomes unavailable to transformations
is_active
set tofalse
in database- Historical audit trail preserved
- Can be recovered by support if needed (Enterprise plans)
Before Deleting
- Check usage: Ensure no transformations depend on the secret
- Test impact: Run transformations without the secret to verify no errors
- Document reason: Keep note of why secret was deleted for audit purposes
Security Best Practices
Secret Rotation
Regularly rotate secrets, especially:
- API keys (quarterly or when team members leave)
- Webhook secrets (annually or after security incidents)
- Database credentials (based on compliance requirements)
Access Management
- Use least privilege: API keys should have minimum required permissions
- Monitor usage: Regularly review
last_used_at
timestamps - Clean up unused secrets: Delete secrets that haven't been used in 90+ days
- Document secret purposes: Always include meaningful descriptions
Development Practices
// ✅ Good practices
addHandler('transform', async (request, context) => {
// Check secret exists before using
const apiKey = process.env.API_KEY;
if (!apiKey) {
console.log('Warning: API_KEY not configured');
return request;
}
// Log that secret is being used (don't log the value)
console.log('Using API_KEY for authentication');
// Handle errors gracefully
try {
return await processWithApiKey(request, apiKey);
} catch (error) {
console.log('Error processing with API key:', error.message);
return request; // Fallback behavior
}
});
// ❌ Bad practices - Never do this
addHandler('transform', async (request, context) => {
// NEVER log secret values
console.log('API Key:', process.env.API_KEY); // ❌ NEVER
// NEVER assume secrets exist without checking
const result = process.env.API_KEY.split('-')[1]; // ❌ Will error if missing
// NEVER expose secrets in responses
return {
...request,
debug: { api_key: process.env.API_KEY } // ❌ NEVER
};
});
Troubleshooting
"Secret not found" Errors
- Verify secret name: Check exact spelling and case sensitivity
- Check organization: Secrets are organization-scoped
- Verify API token: Ensure you're using correct organization's token
- Check secret status: Ensure secret
is_active: true
Secrets Not Available in Transformations
- Plan verification: Ensure you're on Team or Enterprise plan
- Secret creation: Verify secret was created successfully
- Transformation testing: Use test endpoint to debug secret access
- Console debugging: Add
console.log(Object.keys(process.env))
to see available secrets
Performance Issues
- Minimize secret access: Load secrets once per transformation
- Cache values: Store in variables rather than repeatedly accessing
process.env
- Monitor logs: Check transformation execution times
Next Steps
Now that you understand secret management:
- Explore real-world examples of secrets in webhook automation
- Review security best practices for production deployments
- Set up monitoring for secret usage and rotation
- Learn about key rotation for automated security management
Effective secret management is crucial for secure webhook automation. Start with basic secrets and gradually build more sophisticated patterns as your integration needs grow.