Field Encryption & Masking
Field encryption allows you to automatically encrypt sensitive data in webhook payloads before storage. Combined with field masking, you can protect sensitive information in storage and hide it when viewing events in the UI.
Overview
| Feature | Purpose | When Applied |
|---|---|---|
| Encryption | Protect data at rest | Before storage in database |
| Masking | Hide data in UI | When viewing events |
Encryption Details
- Algorithm: AES-256-GCM (authenticated encryption)
- Key management: Organization-specific encryption keys
- Field targeting: JSONPath expressions
Configuration
Via API
# Create source with field encryption and masking
curl -X POST "https://api.hookbase.app/api/sources" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"name": "Payment Webhooks",
"slug": "payments",
"encryptFields": [
"$.card.number",
"$.card.cvv",
"$.ssn",
"$.password"
],
"maskFields": [
"$.card.number",
"$.email",
"$.phone"
]
}'Via Dashboard
- Navigate to Sources
- Click Add Source or edit an existing source
- Expand the Advanced Security section
- In Field Security, enter JSONPath patterns:
- Encrypt Fields: Fields to encrypt in storage
- Mask Fields: Fields to hide in UI
- Save the source
JSONPath Syntax
JSONPath expressions specify which fields to target:
| Pattern | Description | Example Match |
|---|---|---|
$.field | Top-level field | {"field": "value"} |
$.nested.field | Nested field | {"nested": {"field": "value"}} |
$.array[*].field | Field in array items | {"array": [{"field": "value"}]} |
$.*.secret | secret field at any path | {"any": {"secret": "value"}} |
Examples
// Original payload
{
"customer": {
"name": "John Doe",
"email": "[email protected]",
"card": {
"number": "4111111111111111",
"cvv": "123",
"expiry": "12/25"
}
},
"amount": 99.99
}
// encryptFields: ["$.customer.card.number", "$.customer.card.cvv"]
// maskFields: ["$.customer.email", "$.customer.card.number"]How Encryption Works
Ingest Flow (Storage)
When a webhook arrives, sensitive fields are encrypted before storage:
┌─────────────────┐
│ Incoming Webhook│
│ (plain text) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Parse Payload │
└────────┬────────┘
│
▼
┌─────────────────┐
│ For each field │
│ in encryptFields│
└────────┬────────┘
│
▼
┌─────────────────┐
│ Encrypt with │
│ AES-256-GCM │
│ (org-specific │
│ derived key) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Store encrypted │
│ payload in R2 │
│ + metadata │
└─────────────────┘Delivery Flow (Decryption)
When delivering to destinations, fields are automatically decrypted:
┌─────────────────┐
│ Delivery Queue │
│ picks up job │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Fetch encrypted │
│ payload from R2 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Read encryption │
│ metadata │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Decrypt fields │
│ with org key │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Apply transform │
│ (if configured) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Deliver to │
│ destination │
│ (plain text) │
└─────────────────┘UI Display Flow (Masking)
When viewing events in the dashboard or API:
┌─────────────────┐
│ Request event │
│ details │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Fetch encrypted │
│ payload from R2 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Decrypt fields │
│ (if encrypted) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Apply masking │
│ for maskFields │
│ (****1111, etc) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Return masked │
│ payload to UI │
└─────────────────┘Complete Data Flow
┌──────────────────────────────────┐
│ STORAGE │
│ (encrypted data at rest) │
└──────────────────────────────────┘
▲
│
┌──────────────┐ ┌──────────────┐ ┌─────────┴────────┐
│ Webhook │───▶│ Encrypt │───▶│ R2 Bucket │
│ Provider │ │ Fields │ │ (encrypted) │
└──────────────┘ └──────────────┘ └─────────┬────────┘
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Delivery Queue │ │ Dashboard UI │ │ API Request │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Decrypt │ │ Decrypt + Mask │ │ Decrypt + Mask │
│ Fields │ │ Fields │ │ Fields │
└────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘
│ │ │
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Destination │ │ Masked Display │ │ Masked JSON │
│ (plain text) │ │ (****1111) │ │ Response │
└──────────────────┘ └──────────────────┘ └──────────────────┘Encrypted Field Format
Encrypted fields are stored as base64-encoded strings with metadata:
{
"card": {
"number": "enc:v1:aGVsbG8gd29ybGQ=:iv=abc123",
"cvv": "enc:v1:Zm9vYmFy:iv=def456"
}
}How Masking Works
When viewing events in the UI or API, masked fields are replaced:
// Original (stored encrypted)
{
"email": "[email protected]",
"card": {
"number": "4111111111111111"
}
}
// Displayed with masking
{
"email": "j***@example.com",
"card": {
"number": "****1111"
}
}Masking Rules
| Data Type | Masking Pattern |
|---|---|
First char + *** + @ + domain | |
| Card number | **** + last 4 digits |
| Phone | ***-***- + last 4 digits |
| Generic string | First 2 chars + **** + last 2 chars |
Compliance Use Cases
PCI DSS
For payment card data:
{
"encryptFields": [
"$.card.number",
"$.card.cvv",
"$.card.pin"
],
"maskFields": [
"$.card.number"
]
}HIPAA
For healthcare data:
{
"encryptFields": [
"$.patient.ssn",
"$.patient.medicalRecordNumber",
"$.diagnosis",
"$.treatment"
],
"maskFields": [
"$.patient.ssn",
"$.patient.name",
"$.patient.dateOfBirth"
]
}GDPR
For personal data:
{
"encryptFields": [
"$.user.email",
"$.user.phone",
"$.user.address"
],
"maskFields": [
"$.user.email",
"$.user.phone"
]
}Decryption for Delivery
When delivering webhooks to destinations, fields can be:
- Delivered encrypted: Destination receives encrypted values
- Decrypted for delivery: Fields are decrypted before forwarding (default)
Configure per-route in advanced settings.
Best Practices
- Encrypt sensitive data: PII, financial data, authentication tokens
- Mask for visibility: Even encrypted data should be masked in UI
- Use specific paths: Target exact fields rather than broad patterns
- Test with sample data: Verify encryption works before production
- Document encrypted fields: Keep track of what's encrypted for debugging
Updating Encryption Settings
# Update encryption fields on existing source
curl -X PATCH "https://api.hookbase.app/api/sources/{sourceId}" \
-H "Authorization: Bearer {token}" \
-H "Content-Type: application/json" \
-d '{
"encryptFields": [
"$.card.number",
"$.card.cvv",
"$.newSensitiveField"
]
}'WARNING
Changing encryption settings only affects new events. Existing events retain their original encryption configuration.
Viewing Encrypted Data
In Dashboard
Encrypted fields show a lock icon and masked values in the event viewer.
Via API
# Get event with masked fields (default)
curl -X GET "https://api.hookbase.app/api/events/{eventId}" \
-H "Authorization: Bearer {token}"
# Get event with decrypted fields (requires elevated permissions)
curl -X GET "https://api.hookbase.app/api/events/{eventId}?decrypt=true" \
-H "Authorization: Bearer {token}"Troubleshooting
Field not being encrypted
- Verify the JSONPath pattern matches your payload structure
- Check for typos in field names (JSONPath is case-sensitive)
- Test the JSONPath with a sample payload
Decryption errors
- Ensure you're using the correct organization context
- Check that the event was encrypted with current keys
- Contact support if keys need rotation
Transform compatibility
Encrypted fields are decrypted before transforms are applied, so your JSONata expressions work with plaintext values.