Skip to content

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

FeaturePurposeWhen Applied
EncryptionProtect data at restBefore storage in database
MaskingHide data in UIWhen viewing events

Encryption Details

  • Algorithm: AES-256-GCM (authenticated encryption)
  • Key management: Organization-specific encryption keys
  • Field targeting: JSONPath expressions

Configuration

Via API

bash
# 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

  1. Navigate to Sources
  2. Click Add Source or edit an existing source
  3. Expand the Advanced Security section
  4. In Field Security, enter JSONPath patterns:
    • Encrypt Fields: Fields to encrypt in storage
    • Mask Fields: Fields to hide in UI
  5. Save the source

JSONPath Syntax

JSONPath expressions specify which fields to target:

PatternDescriptionExample Match
$.fieldTop-level field{"field": "value"}
$.nested.fieldNested field{"nested": {"field": "value"}}
$.array[*].fieldField in array items{"array": [{"field": "value"}]}
$.*.secretsecret field at any path{"any": {"secret": "value"}}

Examples

json
// 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:

json
{
  "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:

json
// Original (stored encrypted)
{
  "email": "[email protected]",
  "card": {
    "number": "4111111111111111"
  }
}

// Displayed with masking
{
  "email": "j***@example.com",
  "card": {
    "number": "****1111"
  }
}

Masking Rules

Data TypeMasking Pattern
EmailFirst char + *** + @ + domain
Card number**** + last 4 digits
Phone***-***- + last 4 digits
Generic stringFirst 2 chars + **** + last 2 chars

Compliance Use Cases

PCI DSS

For payment card data:

json
{
  "encryptFields": [
    "$.card.number",
    "$.card.cvv",
    "$.card.pin"
  ],
  "maskFields": [
    "$.card.number"
  ]
}

HIPAA

For healthcare data:

json
{
  "encryptFields": [
    "$.patient.ssn",
    "$.patient.medicalRecordNumber",
    "$.diagnosis",
    "$.treatment"
  ],
  "maskFields": [
    "$.patient.ssn",
    "$.patient.name",
    "$.patient.dateOfBirth"
  ]
}

GDPR

For personal data:

json
{
  "encryptFields": [
    "$.user.email",
    "$.user.phone",
    "$.user.address"
  ],
  "maskFields": [
    "$.user.email",
    "$.user.phone"
  ]
}

Decryption for Delivery

When delivering webhooks to destinations, fields can be:

  1. Delivered encrypted: Destination receives encrypted values
  2. Decrypted for delivery: Fields are decrypted before forwarding (default)

Configure per-route in advanced settings.

Best Practices

  1. Encrypt sensitive data: PII, financial data, authentication tokens
  2. Mask for visibility: Even encrypted data should be masked in UI
  3. Use specific paths: Target exact fields rather than broad patterns
  4. Test with sample data: Verify encryption works before production
  5. Document encrypted fields: Keep track of what's encrypted for debugging

Updating Encryption Settings

bash
# 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

bash
# 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

  1. Verify the JSONPath pattern matches your payload structure
  2. Check for typos in field names (JSONPath is case-sensitive)
  3. Test the JSONPath with a sample payload

Decryption errors

  1. Ensure you're using the correct organization context
  2. Check that the event was encrypted with current keys
  3. Contact support if keys need rotation

Transform compatibility

Encrypted fields are decrypted before transforms are applied, so your JSONata expressions work with plaintext values.

Released under the MIT License.