Skip to content

Webhooks

Webhooks allow you to receive real-time notifications about transaction events and signing operations. When configured, rrelayer will send HTTP POST requests to your specified endpoints whenever certain events occur.

Overview

rrelayer supports webhooks for the following events:

Transaction Events

  • transaction_queued - Transaction was added to the queue
  • transaction_sent - Transaction was sent to the blockchain
  • transaction_mined - Transaction was mined (included in a block)
  • transaction_confirmed - Transaction reached required confirmations
  • transaction_failed - Transaction failed
  • transaction_expired - Transaction expired
  • transaction_cancelled - Transaction was cancelled
  • transaction_replaced - Transaction was replaced

Signing Events

  • text_signed - Text message was signed
  • typed_data_signed - Typed data (EIP-712) was signed

Balance Alert Events

  • low_balance - Relayer balance fell below minimum threshold

Configuration

Basic Configuration

rrelayer.yaml
name: first-rrelayer
description: "my first rrelayer"
api_config:
  port: 3000
  authentication_username: "${RRELAYER_AUTH_USERNAME}"
  authentication_password: "${RRELAYER_AUTH_PASSWORD}"
signing_provider:
  aws_kms:
    region: "eu-west-1"
networks:
- name: "sepolia_ethereum"
  chain_id: 11155111
  provider_urls:
  - "https://sepolia.gateway.tenderly.co"
  block_explorer_url: "https://sepolia.etherscan.io"
  max_gas_price_multiplier: 4
  gas_bump_blocks_every:
    slow: 10
    medium: 5
    fast: 4
    super_fast: 2
webhooks: 
- endpoint: "https://api.yourapp.com/webhooks/rrelayer"
  shared_secret: "${WEBHOOK_SECRET}"
  networks: "*"
  max_retries: 3
  alert_on_low_balances: true

Multiple Webhooks

You can configure multiple webhook endpoints for different purposes:

rrelayer.yaml
webhooks: 
- endpoint: "https://api.yourapp.com/webhooks/transactions"
  shared_secret: "${TRANSACTION_WEBHOOK_SECRET}"
  networks: ["sepolia_ethereum", "polygon_mainnet"] 
  max_retries: 5
- endpoint: "https://monitoring.yourapp.com/webhooks/alerts"
  shared_secret: "${ALERT_WEBHOOK_SECRET}"
  networks: "*"
  max_retries: 3
  alert_on_low_balances: true

Network Filtering

Control which networks trigger webhooks:

rrelayer.yaml
webhooks:
# Send webhooks for all networks (using string)
- endpoint: "https://api.yourapp.com/webhooks/all"
  shared_secret: "${WEBHOOK_SECRET}"
  networks: "*"
 
# Send webhooks for all networks (using array)
- endpoint: "https://api.yourapp.com/webhooks/all-array"
  shared_secret: "${WEBHOOK_SECRET}"
  networks: ["*"] 
 
# Send webhooks only for specific networks
- endpoint: "https://api.yourapp.com/webhooks/ethereum"
  shared_secret: "${WEBHOOK_SECRET}"
  networks: ["sepolia_ethereum", "ethereum_mainnet"] 
 
# Send webhooks for no networks (webhook disabled)
- endpoint: "https://api.yourapp.com/webhooks/disabled"
  shared_secret: "${WEBHOOK_SECRET}"
  networks: [] 

Configuration Options

Required Fields

  • endpoint: The URL where webhook requests will be sent
  • shared_secret: Secret used for webhook authentication and payload verification
  • networks: Network filter - can be "*" for all networks, or an array of network names

Optional Fields

  • max_retries: Maximum number of retry attempts for failed webhooks (default: 3)
  • alert_on_low_balances: Enable low balance alerts for relayers (default: false)

Webhook Payload Structure

Transaction Webhooks

All transaction webhooks send a JSON payload with this structure:

{
  "eventType": "transaction_sent",
  "apiVersion": "1.0",
  "timestamp": "2025-09-26T10:30:00Z",
  "transaction": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "to": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35cc6466c4b0de3e3e8c7b8e8f9e8a8d8c8b",
    "value": "1000000000000000000",
    "data": "0xa9059cbb000000000000000000000000...",
    "chainId": 11155111,
    "status": "INMEMPOOL",
    "txHash": "0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcdef",
    "queuedAt": "2025-09-26T10:29:30Z",
    "sentAt": "2025-09-26T10:30:00Z",
    "expiresAt": "2025-09-26T11:30:00Z",
    "externalId": "order_12345",
    "nonce": 42,
    "maxPriorityFee": "2.0",
    "maxFee": "15.5",
    "isNoop": false
  }
}

Enhanced Payloads

Some events include additional data:

Transaction Mined/Confirmed with Receipt

{
  "eventType": "transaction_mined",
  "apiVersion": "1.0",
  "timestamp": "2025-09-26T10:32:00Z",
  "transaction": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "to": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35cc6466c4b0de3e3e8c7b8e8f9e8a8d8c8b",
    "value": "1000000000000000000",
    "data": "0xa9059cbb000000000000000000000000...",
    "chainId": 11155111,
    "status": "MINED",
    "txHash": "0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcdef",
    "queuedAt": "2025-09-26T10:29:30Z",
    "sentAt": "2025-09-26T10:30:00Z",
    "confirmedAt": "2025-09-26T10:32:00Z",
    "expiresAt": "2025-09-26T11:30:00Z",
    "externalId": "order_12345",
    "nonce": 42,
    "minedAt": "2025-09-26T10:32:00Z",
    "minedAtBlockNumber": 1193046,
    "maxPriorityFee": "2.0",
    "maxFee": "15.5",
    "isNoop": false
  },
  "receipt": {
    "transactionHash": "0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcdef",
    "blockNumber": "0x123456",
    "blockHash": "0xfedcba987654321fedcba987654321fedcba987654321fedcba987654321fedcba",
    "gasUsed": "0x5208",
    "status": "0x1",
    "logs": []
  }
}

Transaction Replaced

{
  "eventType": "transaction_replaced",
  "apiVersion": "1.0",
  "timestamp": "2025-09-26T10:35:00Z",
  "transaction": {
    "id": "550e8400-e29b-41d4-a716-446655440002",
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "to": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35cc6466c4b0de3e3e8c7b8e8f9e8a8d8c8b",
    "value": "1000000000000000000",
    "data": "0xa9059cbb000000000000000000000000...",
    "chainId": 11155111,
    "status": "PENDING",
    "txHash": "0xdef456ghi789def456ghi789def456ghi789def456ghi789def456ghi789def456",
    "queuedAt": "2025-09-26T10:35:00Z",
    "sentAt": null,
    "confirmedAt": null,
    "expiresAt": "2025-09-26T11:35:00Z"
  },
  "originalTransaction": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "to": "0x1234567890abcdef1234567890abcdef12345678",
    "from": "0x742d35cc6466c4b0de3e3e8c7b8e8f9e8a8d8c8b",
    "value": "1000000000000000000",
    "data": "0xa9059cbb000000000000000000000000...",
    "chainId": 11155111,
    "status": "EXPIRED",
    "txHash": "0xabcdef123456789abcdef123456789abcdef123456789abcdef123456789abcdef",
    "queuedAt": "2025-09-26T10:29:30Z",
    "sentAt": "2025-09-26T10:30:00Z",
    "confirmedAt": null,
    "expiresAt": "2025-09-26T11:30:00Z"
  }
}

Signing Webhooks

Signing webhooks have a different payload structure:

Text Signing

{
  "eventType": "text_signed",
  "apiVersion": "1.0",
  "timestamp": "2025-09-26T10:30:00Z",
  "signing": {
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "chainId": 11155111,
    "signature": {
      "r": "0x1234567890abcdef...",
      "s": "0xfedcba0987654321...",
      "v": 27
    },
    "signedAt": "2025-09-26T10:30:00Z",
    "message": "Hello, World!"
  }
}

Typed Data Signing (EIP-712)

{
  "eventType": "typed_data_signed",
  "apiVersion": "1.0",
  "timestamp": "2025-09-26T10:30:00Z",
  "signing": {
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "chainId": 11155111,
    "signature": {
      "r": "0x1234567890abcdef...",
      "s": "0xfedcba0987654321...",
      "v": 27
    },
    "signedAt": "2025-09-26T10:30:00Z",
    "domainData": {
      "name": "MyApp",
      "version": "1",
      "chainId": 11155111,
      "verifyingContract": "0x..."
    },
    "messageData": {
      "user": "0x...",
      "amount": "1000"
    },
    "primaryType": "Transfer"
  }
}

Balance Alert Webhooks

Balance alert webhooks are sent when a relayer's balance falls below the minimum threshold for its network:

{
  "eventType": "low_balance",
  "apiVersion": "1.0",
  "timestamp": "2025-09-27T14:30:00Z",
  "balanceAlert": {
    "relayerId": "550e8400-e29b-41d4-a716-446655440001",
    "address": "0x742d35cc6466c4b0de3e3e8c7b8e8f9e8a8d8c8b",
    "chainId": 1,
    "currentBalance": "3500000000000000000",
    "minimumBalance": "5000000000000000000",
    "currentBalanceFormatted": "3.5",
    "minimumBalanceFormatted": "5.0",
    "detectedAt": "2025-09-27T14:30:00Z"
  }
}

Balance Monitoring Thresholds

rrelayer automatically monitors relayer balances and sends alerts based on network-specific thresholds:

  • Ethereum Mainnet: 0.005 ETH minimum (higher due to gas costs)
  • Layer 2 Networks (Polygon, Arbitrum, Optimism, Base, etc.): 0.001 ETH minimum
  • Testnets (Sepolia, Mumbai, etc.): 0.0005 ETH minimum

Balance checks run every 10 minutes and both log warnings and send webhooks when enabled.

Webhook Authentication

Webhook authentication is configured through the shared_secret field in your webhook configuration. This secret is used for webhook authentication and payload verification.

Security Configuration

Each webhook endpoint requires a shared secret for authentication:

webhooks:
  - endpoint: 'https://api.yourapp.com/webhooks/rrelayer'
    shared_secret: '${WEBHOOK_SECRET}' # Strong secret for authentication
    networks: '*'

Best Practices

  1. Use strong secrets - Generate cryptographically secure random strings
  2. Keep secrets secure - Store in environment variables, not in configuration files
  3. Rotate secrets regularly - Update webhook secrets periodically
  4. Use HTTPS endpoints - Always use encrypted connections for webhook delivery

Delivery and Retry Logic

Delivery Behavior

  • Webhooks are sent as HTTP POST requests with JSON payloads
  • rrelayer considers a webhook successful if it receives a 2xx HTTP response
  • Failed webhooks are automatically retried with exponential backoff

Retry Configuration

  • Default max retries: 3 attempts
  • Initial retry delay: 1 second
  • Maximum retry delay: 2 minutes
  • Exponential backoff multiplier: 2.0
  • Request timeout: 30 seconds

Retry Schedule Example

  1. Initial attempt: Immediate
  2. First retry: After 1 second
  3. Second retry: After 2 seconds
  4. Third retry: After 4 seconds
  5. Final failure: After 3 failed attempts

Low Balance Alerts

Enable Balance Monitoring

To receive alerts when relayer balances fall below safe thresholds, enable the alert_on_low_balances option:

rrelayer.yaml
webhooks:
  - endpoint: 'https://monitoring.yourapp.com/webhooks/alerts'
    shared_secret: '${MONITORING_WEBHOOK_SECRET}'
    networks: '*'
    alert_on_low_balances: true # Enable balance alerts

Dedicated Alert Endpoint

For better monitoring, consider using a separate webhook endpoint specifically for alerts:

rrelayer.yaml
webhooks:
  # Transaction notifications
  - endpoint: 'https://api.yourapp.com/webhooks/transactions'
    shared_secret: '${TRANSACTION_WEBHOOK_SECRET}'
    networks: '*'
    max_retries: 3
 
  # Balance and system alerts
  - endpoint: 'https://monitoring.yourapp.com/webhooks/alerts'
    shared_secret: '${ALERT_WEBHOOK_SECRET}'
    networks: '*'
    max_retries: 5
    alert_on_low_balances: true

Monitoring Integration

Low balance webhooks integrate well with monitoring systems:

// Example webhook handler for balance alerts
app.post('/webhooks/alerts', (req, res) => {
  const { event_type, balance_alert } = req.body;
 
  if (event_type === 'low_balance') {
    const {
      relayerId,
      chainId,
      currentBalanceFormatted,
      minimumBalanceFormatted,
    } = balanceAlert;
 
    // Send alert to monitoring system
    monitoring.alert({
      level: 'warning',
      message: `Low balance: Relayer ${relayerId} on chain ${chainId}`,
      details: {
        current: `${currentBalanceFormatted} ETH`,
        minimum: `${minimumBalanceFormatted} ETH`,
        chain: chainId,
      },
    });
 
    // Optionally trigger automatic top-up process
    if (shouldAutoTopUp(chainId, currentBalanceFormatted)) {
      triggerTopUp(relayerId, chainId);
    }
  }
 
  res.status(200).json({ received: true });
});

Best Practices

Endpoint Implementation

  1. Return 2xx status codes for successful processing
  2. Implement idempotency - Handle duplicate webhooks gracefully
  3. Process quickly - Respond within 30 seconds to avoid timeouts
  4. Verify signatures - Always validate the X-rrelayer-Signature header

Error Handling

  1. Use appropriate HTTP status codes:

    • 200-299: Success - webhook processed
    • 400-499: Client error - rrelayer will not retry
    • 500-599: Server error - rrelayer will retry
  2. Log webhook failures for debugging

  3. Implement monitoring for webhook endpoint health

Security

  1. Use HTTPS endpoints for production
  2. Validate webhook signatures to prevent spoofing
  3. Keep shared secrets secure and rotate them regularly
  4. Implement rate limiting on your webhook endpoints

Monitoring Webhooks

rrelayer provides internal monitoring of webhook delivery:

  • Failed webhooks are logged with error details
  • Retry attempts are tracked and logged
  • Webhook statistics are available for monitoring

Monitor your webhook endpoints for:

  • Response times
  • Error rates
  • Processing failures
  • Queue depth (if applicable)