Skip to main content

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 token
  • STRIPE_API_KEY - Stripe secret API key
  • GITHUB_TOKEN - GitHub personal access token
  • TELEGRAM_BOT_TOKEN - Telegram bot API token

Webhook Security

  • WEBHOOK_SECRET - Shared secret for webhook signature verification
  • HMAC_SECRET - Secret for HMAC signature generation
  • API_SECRET - General API authentication secret

Database and Services

  • DATABASE_URL - Database connection string
  • REDIS_URL - Redis connection URL
  • ENCRYPTION_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

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 to false in database
  • Historical audit trail preserved
  • Can be recovered by support if needed (Enterprise plans)

Before Deleting

  1. Check usage: Ensure no transformations depend on the secret
  2. Test impact: Run transformations without the secret to verify no errors
  3. 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

  1. Use least privilege: API keys should have minimum required permissions
  2. Monitor usage: Regularly review last_used_at timestamps
  3. Clean up unused secrets: Delete secrets that haven't been used in 90+ days
  4. 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

  1. Verify secret name: Check exact spelling and case sensitivity
  2. Check organization: Secrets are organization-scoped
  3. Verify API token: Ensure you're using correct organization's token
  4. Check secret status: Ensure secret is_active: true

Secrets Not Available in Transformations

  1. Plan verification: Ensure you're on Team or Enterprise plan
  2. Secret creation: Verify secret was created successfully
  3. Transformation testing: Use test endpoint to debug secret access
  4. Console debugging: Add console.log(Object.keys(process.env)) to see available secrets

Performance Issues

  1. Minimize secret access: Load secrets once per transformation
  2. Cache values: Store in variables rather than repeatedly accessing process.env
  3. Monitor logs: Check transformation execution times

Next Steps

Now that you understand secret management:

  1. Explore real-world examples of secrets in webhook automation
  2. Review security best practices for production deployments
  3. Set up monitoring for secret usage and rotation
  4. 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.